diff --git a/vlib/crypto/ecdsa/ecdsa.c.v b/vlib/crypto/ecdsa/ecdsa.c.v index a785f75c26..a62377dee6 100644 --- a/vlib/crypto/ecdsa/ecdsa.c.v +++ b/vlib/crypto/ecdsa/ecdsa.c.v @@ -38,12 +38,14 @@ struct C.EVP_PKEY {} fn C.EVP_PKEY_new() &C.EVP_PKEY fn C.EVP_PKEY_free(key &C.EVP_PKEY) -fn C.EVP_PKEY_get1_EC_KEY(pkey &C.EVP_PKEY) &C.EC_KEY fn C.EVP_PKEY_base_id(key &C.EVP_PKEY) int fn C.EVP_PKEY_bits(pkey &C.EVP_PKEY) int fn C.EVP_PKEY_size(key &C.EVP_PKEY) int fn C.EVP_PKEY_eq(a &C.EVP_PKEY, b &C.EVP_PKEY) int +fn C.EVP_PKEY_check(ctx &C.EVP_PKEY_CTX) int +fn C.EVP_PKEY_public_check(ctx &C.EVP_PKEY_CTX) int +fn C.EVP_PKEY_get_group_name(pkey &C.EVP_PKEY, gname &u8, gname_sz u32, gname_len &usize) int fn C.EVP_PKEY_get1_encoded_public_key(pkey &C.EVP_PKEY, ppub &&u8) int fn C.EVP_PKEY_get_bn_param(pkey &C.EVP_PKEY, key_name &u8, bn &&C.BIGNUM) int fn C.EVP_PKEY_fromdata_init(ctx &C.EVP_PKEY_CTX) int @@ -86,23 +88,6 @@ fn C.EVP_PKEY_CTX_free(ctx &C.EVP_PKEY_CTX) fn C.EVP_PKEY_get_bits(pkey &C.EVP_PKEY) int -// Elliptic curve keypair declarations -@[typedef] -struct C.EC_KEY {} - -fn C.EC_KEY_new_by_curve_name(nid int) &C.EC_KEY -fn C.EC_KEY_generate_key(key &C.EC_KEY) int -fn C.EC_KEY_dup(src &C.EC_KEY) &C.EC_KEY -fn C.EC_KEY_free(key &C.EC_KEY) -fn C.EC_KEY_set_public_key(key &C.EC_KEY, &C.EC_POINT) int -fn C.EC_KEY_set_private_key(key &C.EC_KEY, prv &C.BIGNUM) int -fn C.EC_KEY_get0_group(key &C.EC_KEY) &C.EC_GROUP -fn C.EC_KEY_get0_private_key(key &C.EC_KEY) &C.BIGNUM -fn C.EC_KEY_get0_public_key(key &C.EC_KEY) &C.EC_POINT -fn C.EC_KEY_get_conv_form(k &C.EC_KEY) int -fn C.EC_KEY_check_key(key &C.EC_KEY) int -fn C.EC_KEY_up_ref(key &C.EC_KEY) int - // BIO input output declarations. @[typedef] struct C.BIO_METHOD {} @@ -126,9 +111,7 @@ struct C.EC_POINT {} fn C.EC_POINT_new(group &C.EC_GROUP) &C.EC_POINT fn C.EC_POINT_mul(group &C.EC_GROUP, r &C.EC_POINT, n &C.BIGNUM, q &C.EC_POINT, m &C.BIGNUM, ctx &C.BN_CTX) int -fn C.EC_POINT_point2oct(g &C.EC_GROUP, p &C.EC_POINT, form int, buf &u8, max_out int, ctx &C.BN_CTX) int fn C.EC_POINT_point2buf(group &C.EC_GROUP, point &C.EC_POINT, form int, pbuf &&u8, ctx &C.BN_CTX) int -fn C.EC_POINT_cmp(group &C.EC_GROUP, a &C.EC_POINT, b &C.EC_POINT, ctx &C.BN_CTX) int fn C.EC_POINT_free(point &C.EC_POINT) // Elliptic group (curve) related declarations. @@ -136,9 +119,6 @@ fn C.EC_POINT_free(point &C.EC_POINT) struct C.EC_GROUP {} fn C.EC_GROUP_free(group &C.EC_GROUP) -fn C.EC_GROUP_get_degree(g &C.EC_GROUP) int -fn C.EC_GROUP_get_curve_name(g &C.EC_GROUP) int -fn C.EC_GROUP_cmp(a &C.EC_GROUP, b &C.EC_GROUP, ctx &C.BN_CTX) int fn C.EC_GROUP_new_by_curve_name(nid int) &C.EC_GROUP // Elliptic BIGNUM related declarations. @@ -160,14 +140,6 @@ struct C.BN_CTX {} fn C.BN_CTX_new() &C.BN_CTX fn C.BN_CTX_free(ctx &C.BN_CTX) -// ELliptic ECDSA signing and verifying related declarations. -@[typedef] -struct C.ECDSA_SIG {} - -fn C.ECDSA_size(key &C.EC_KEY) u32 -fn C.ECDSA_sign(type_ int, dgst &u8, dgstlen int, sig &u8, siglen &u32, eckey &C.EC_KEY) int -fn C.ECDSA_verify(type_ int, dgst &u8, dgstlen int, sig &u8, siglen int, eckey &C.EC_KEY) int - @[typedef] struct C.EVP_MD_CTX {} diff --git a/vlib/crypto/ecdsa/ecdsa.v b/vlib/crypto/ecdsa/ecdsa.v index acd649f9aa..1378f8db8b 100644 --- a/vlib/crypto/ecdsa/ecdsa.v +++ b/vlib/crypto/ecdsa/ecdsa.v @@ -4,9 +4,6 @@ module ecdsa import hash -import crypto -import crypto.sha256 -import crypto.sha512 // NID constants // @@ -121,7 +118,7 @@ pub fn new_key_from_seed(seed []u8, opt CurveOptions) !PrivateKey { if seed.len == 0 { return error('Seed with null-length was not allowed') } - evpkey := evpkey_from_seed(seed, opt)! + evpkey := evpkey_from_seed(seed, opt) or { return err } num_bits := C.EVP_PKEY_get_bits(evpkey) key_size := (num_bits + 7) / 8 if seed.len > key_size { @@ -135,15 +132,7 @@ pub fn new_key_from_seed(seed []u8, opt CurveOptions) !PrivateKey { return error('seed size doesnt match with curve key size') } } - // TODO: remove this when its ready to go out - eckey := C.EVP_PKEY_get1_EC_KEY(evpkey) - if eckey == 0 { - C.EC_KEY_free(eckey) - C.EVP_PKEY_free(evpkey) - return error('EVP_PKEY_get1_EC_KEY failed') - } mut pvkey := PrivateKey{ - key: eckey evpkey: evpkey } @@ -161,11 +150,8 @@ pub fn new_key_from_seed(seed []u8, opt CurveOptions) !PrivateKey { // PrivateKey represents ECDSA private key. Actually its a key pair, // contains private key and public key parts. pub struct PrivateKey { - // The new high level of keypair opaque, set to nil now. - evpkey &C.EVP_PKEY = unsafe { nil } - // TODO: when all has been migrated to the new one, - // removes this low level declarations. - key &C.EC_KEY + // The new high level of keypair opaque + evpkey &C.EVP_PKEY mut: // ks_flag with .flexible value allowing // flexible-size seed bytes as key. @@ -229,23 +215,12 @@ pub fn PrivateKey.new(opt CurveOptions) !PrivateKey { return error('EVP_PKEY_keygen failed') } - // EVP_PKEY_get1_EC_KEY was deprecated in 3.0. Its used here for compatibility purposes - // to support the old key function. - // TODO: removes this when its ready to obsolete. - eckey := C.EVP_PKEY_get1_EC_KEY(evpkey) - if eckey == 0 { - C.EVP_PKEY_CTX_free(pctx) - C.EC_KEY_free(eckey) - C.EVP_PKEY_free(evpkey) - return error('EVP_PKEY_get1_EC_KEY failed') - } // Cleans up the context C.EVP_PKEY_CTX_free(pctx) // when using default this function, its using underlying curve key size // and discarded opt.fixed_size flag when its not set. priv_key := PrivateKey{ evpkey: evpkey - key: eckey ks_flag: .fixed } return priv_key @@ -254,12 +229,8 @@ pub fn PrivateKey.new(opt CurveOptions) !PrivateKey { // sign performs signing the message with the options. By default options, // it will perform hashing before signing the message. pub fn (pv PrivateKey) sign(message []u8, opt SignerOpts) ![]u8 { - if pv.evpkey != unsafe { nil } { - digest := calc_digest_with_evpkey(pv.evpkey, message, opt)! - return sign_digest(pv.evpkey, digest)! - } - digest := calc_digest_with_eckey(pv.key, message, opt)! - return pv.sign_digest(digest)! + digest := calc_digest_with_evpkey(pv.evpkey, message, opt)! + return sign_digest(pv.evpkey, digest)! } // sign_with_options signs message with the options. It will be deprecated, @@ -269,24 +240,6 @@ pub fn (pv PrivateKey) sign_with_options(message []u8, opt SignerOpts) ![]u8 { return pv.sign(message, opt) } -// sign_digest sign a digest with private key. -fn (pv PrivateKey) sign_digest(digest []u8) ![]u8 { - if digest.len == 0 { - return error('Digest cannot be null or empty') - } - mut sig_len := u32(0) - sig_size := C.ECDSA_size(pv.key) - sig := unsafe { malloc(int(sig_size)) } - res := C.ECDSA_sign(0, digest.data, digest.len, sig, &sig_len, pv.key) - if res != 1 { - unsafe { free(sig) } - return error('Failed to sign digest') - } - signed_data := unsafe { sig.vbytes(int(sig_len)) } - unsafe { free(sig) } - return signed_data.clone() -} - // bytes represent private key as bytes. pub fn (pv PrivateKey) bytes() ![]u8 { bn := C.BN_new() @@ -325,70 +278,17 @@ pub fn (pv PrivateKey) seed() ![]u8 { pub fn (pv PrivateKey) public_key() !PublicKey { // Check if EVP_PKEY opaque was availables or not. // TODO: removes this check when its ready - if pv.evpkey != unsafe { nil } { - bo := C.BIO_new(C.BIO_s_mem()) - n := C.i2d_PUBKEY_bio(bo, pv.evpkey) - assert n != 0 - // stores this bio as another key - pbkey := C.d2i_PUBKEY_bio(bo, 0) - // TODO: removes this when its ready to obsolete. - eckey := C.EVP_PKEY_get1_EC_KEY(pbkey) - if eckey == 0 { - C.EC_KEY_free(eckey) - C.EVP_PKEY_free(pbkey) - C.BIO_free_all(bo) - return error('EVP_PKEY_get1_EC_KEY failed') - } - C.BIO_free_all(bo) + bo := C.BIO_new(C.BIO_s_mem()) + n := C.i2d_PUBKEY_bio(bo, pv.evpkey) + assert n != 0 + // stores this bio as another key + pbkey := C.d2i_PUBKEY_bio(bo, 0) + + C.BIO_free_all(bo) - return PublicKey{ - evpkey: pbkey - key: eckey - } - } - // Otherwise, use the old EC_KEY opaque. - // TODO: removes this when its ready to obsolete - // - // There are some issues concerned when returning PublicKey directly using underlying - // `PrivateKey.key`. This private key containing sensitive information inside it, so return - // this without care maybe can lead to some serious security impact. - // See https://discord.com/channels/592103645835821068/592320321995014154/1329261267965448253 - // So, we instead return a new EC_KEY opaque based information availables on private key object - // without private key bits has been set on this new opaque. - group := voidptr(C.EC_KEY_get0_group(pv.key)) - if group == 0 { - return error('Failed to load group from priv_key') - } - nid := C.EC_GROUP_get_curve_name(group) - if nid != nid_prime256v1 && nid != nid_secp384r1 && nid != nid_secp521r1 && nid != nid_secp256k1 { - return error('Get unsupported curve nid') - } - // get public key point from private key opaque - pubkey_point := voidptr(C.EC_KEY_get0_public_key(pv.key)) - if pubkey_point == 0 { - // C.EC_POINT_free(pubkey_point) - // todo: maybe its not set, just calculates new one - return error('Failed to get public key BIGNUM') - } - // creates a new EC_KEY opaque based on the same NID with private key and - // sets public key on it. - pub_key := C.EC_KEY_new_by_curve_name(nid) - np := C.EC_KEY_set_public_key(pub_key, pubkey_point) - if np != 1 { - // C.EC_POINT_free(pubkey_point) - C.EC_KEY_free(pub_key) - return error('Failed to set public key') - } - // performs explicit check - chk := C.EC_KEY_check_key(pub_key) - if chk == 0 { - C.EC_KEY_free(pub_key) - return error('EC_KEY_check_key failed') - } - // OK ? return PublicKey{ - key: pub_key + evpkey: pbkey } } @@ -397,229 +297,44 @@ pub fn (pv PrivateKey) public_key() !PublicKey { // - whether both of private keys lives under the same group (curve), // - compares if two private key bytes was equal. pub fn (priv_key PrivateKey) equal(other PrivateKey) bool { - if priv_key.evpkey != unsafe { nil } && other.evpkey != unsafe { nil } { - eq := C.EVP_PKEY_eq(voidptr(priv_key.evpkey), voidptr(other.evpkey)) - return eq == 1 - } - // TODO: remove this when its ready - group1 := voidptr(C.EC_KEY_get0_group(priv_key.key)) - group2 := voidptr(C.EC_KEY_get0_group(other.key)) - ctx := C.BN_CTX_new() - if ctx == 0 { - return false - } - defer { - C.BN_CTX_free(ctx) - } - gres := C.EC_GROUP_cmp(group1, group2, ctx) - // Its lives on the same group - if gres == 0 { - bn1 := voidptr(C.EC_KEY_get0_private_key(priv_key.key)) - bn2 := voidptr(C.EC_KEY_get0_private_key(other.key)) - res := C.BN_cmp(bn1, bn2) - return res == 0 - } - return false + eq := C.EVP_PKEY_eq(voidptr(priv_key.evpkey), voidptr(other.evpkey)) + return eq == 1 } // free clears out allocated memory for PrivateKey // Dont use PrivateKey after calling `.free()` pub fn (pv &PrivateKey) free() { - C.EC_KEY_free(pv.key) C.EVP_PKEY_free(pv.evpkey) } // PublicKey represents ECDSA public key for verifying message. pub struct PublicKey { - // The new high level of keypair opaque, set to nil now. - evpkey &C.EVP_PKEY = unsafe { nil } - // Remove this when its fully obsoleted by the new one. - key &C.EC_KEY + // The new high level of keypair opaque + evpkey &C.EVP_PKEY } // verify verifies a message with the signature are valid with public key provided . // You should provide it with the same SignerOpts used with the `.sign()` call. // or verify would fail (false). pub fn (pb PublicKey) verify(message []u8, sig []u8, opt SignerOpts) !bool { - if pb.evpkey != unsafe { nil } { - digest := calc_digest_with_evpkey(pb.evpkey, message, opt)! - return verify_signature(pb.evpkey, sig, digest) - } - digest := calc_digest_with_eckey(pb.key, message, opt)! - res := C.ECDSA_verify(0, digest.data, digest.len, sig.data, sig.len, pb.key) - if res == -1 { - return error('Failed to verify signature') - } - return res == 1 + digest := calc_digest_with_evpkey(pb.evpkey, message, opt)! + return verify_signature(pb.evpkey, sig, digest) } // Compare two public keys pub fn (pub_key PublicKey) equal(other PublicKey) bool { - if pub_key.evpkey != unsafe { nil } && other.evpkey != unsafe { nil } { - eq := C.EVP_PKEY_eq(voidptr(pub_key.evpkey), voidptr(other.evpkey)) - return eq == 1 - } - // TODO: check validity of the group - group1 := voidptr(C.EC_KEY_get0_group(pub_key.key)) - group2 := voidptr(C.EC_KEY_get0_group(other.key)) - if group1 == 0 || group2 == 0 { - return false - } - ctx := C.BN_CTX_new() - if ctx == 0 { - return false - } - defer { - C.BN_CTX_free(ctx) - } - gres := C.EC_GROUP_cmp(group1, group2, ctx) - // Its lives on the same group - if gres == 0 { - point1 := voidptr(C.EC_KEY_get0_public_key(pub_key.key)) - point2 := voidptr(C.EC_KEY_get0_public_key(other.key)) - if point1 == 0 || point2 == 0 { - return false - } - res := C.EC_POINT_cmp(group1, point1, point2, ctx) - return res == 0 - } - - return false + eq := C.EVP_PKEY_eq(voidptr(pub_key.evpkey), voidptr(other.evpkey)) + return eq == 1 } // free clears out allocated memory for PublicKey. // Dont use PublicKey after calling `.free()` pub fn (pb &PublicKey) free() { - C.EC_KEY_free(pb.key) C.EVP_PKEY_free(pb.evpkey) } // Helpers // -// new_curve creates a new empty curve based on curve NID, default to prime256v1 (or secp256r1). -fn new_curve(opt CurveOptions) &C.EC_KEY { - mut nid := nid_prime256v1 - match opt.nid { - .prime256v1 { - // do nothing - } - .secp384r1 { - nid = nid_secp384r1 - } - .secp521r1 { - nid = nid_secp521r1 - } - .secp256k1 { - nid = nid_secp256k1 - } - } - return C.EC_KEY_new_by_curve_name(nid) -} - -// Gets recommended hash function of the key. -// Its purposes for hashing message to be signed. -fn recommended_hash(key &C.EC_KEY) !crypto.Hash { - group := voidptr(C.EC_KEY_get0_group(key)) - if group == 0 { - return error('Unable to load group') - } - // gets the bits size of private key group - num_bits := C.EC_GROUP_get_degree(group) - match true { - // use sha256 - num_bits <= 256 { - return .sha256 - } - num_bits > 256 && num_bits <= 384 { - return .sha384 - } - // TODO: what hash should be used if the size is over > 512 bits - num_bits > 384 { - return .sha512 - } - else { - return error('Unsupported bits size') - } - } -} - -fn calc_digest_with_recommended_hash(key &C.EC_KEY, msg []u8) ![]u8 { - h := recommended_hash(key)! - match h { - .sha256 { - return sha256.sum256(msg) - } - .sha384 { - return sha512.sum384(msg) - } - .sha512 { - return sha512.sum512(msg) - } - else { - return error('Unsupported hash') - } - } -} - -// calc_digest_with_eckey tries to calculates digest (hash) of the message based on options provided. -// If the options was .with_no_hash, its has the same behaviour with .with_recommended_hash. -fn calc_digest_with_eckey(key &C.EC_KEY, message []u8, opt SignerOpts) ![]u8 { - if message.len == 0 { - return error('null-length messages') - } - // check key size bits - group := voidptr(C.EC_KEY_get0_group(key)) - if group == 0 { - return error('fail to load group') - } - num_bits := C.EC_GROUP_get_degree(group) - key_size := (num_bits + 7) / 8 - // we're working on mutable copy of SignerOpts, with some issues when make it as a mutable. - // ie, declaring a mutable parameter that accepts a struct with the `@[params]` attribute is not allowed. - mut cfg := opt - match cfg.hash_config { - // There are issues when your message size was exceeds the current key size with .with_no_hash options. - // See https://discord.com/channels/592103645835821068/592114487759470596/1334319744098107423 - // So, we aliased it into .with_recommended_hash - .with_no_hash, .with_recommended_hash { - return calc_digest_with_recommended_hash(key, message)! - } - .with_custom_hash { - if !cfg.allow_custom_hash { - return error('custom hash was not allowed, set it into true') - } - if cfg.custom_hash == unsafe { nil } { - return error('Custom hasher was not defined') - } - // If current Private Key size is bigger then current hash output size, - // by default its not allowed, until set the allow_smaller_size into true - if key_size > cfg.custom_hash.size() { - if !cfg.allow_smaller_size { - return error('Hash into smaller size than current key size was not allowed') - } - } - // we need to reset the custom hash before writes message - cfg.custom_hash.reset() - _ := cfg.custom_hash.write(message)! - digest := cfg.custom_hash.sum([]u8{}) - // NOTES: - // NIST FIPS 186-4 at the end of section 6.4 states that: - // When the length of the output of the hash function is greater than the bit length of n, - // then the leftmost n bits of the hash function output block shall be used in any calculation - // using the hash function output during the generation or verification of a digital signature - // with output of custom_hash was bigger than bit length (key size) - // TODO: - // Maybe better to pick up only required bytes from digest, ie, - // out := digest[..key_size].clone() - // unsafe { digest.free() } - // return out - // Currently, just boildown to the caller - return digest - } - } - return error('Not should be here') -} - // calc_digest_with_evpkey get the digest of the messages under the EVP_PKEY and options fn calc_digest_with_evpkey(key &C.EVP_PKEY, message []u8, opt SignerOpts) ![]u8 { if message.len == 0 { @@ -789,7 +504,7 @@ fn evpkey_from_seed(seed []u8, opt CurveOptions) !&C.EVP_PKEY { } // Build EC_POINT from this BIGNUM and gets bytes represantion of this point // in uncompressed format. - point := ec_point_mult(group, bn)! + point := ec_point_mult(group, bn) or { return err } pub_bytes := point_2_buf(group, point, point_conversion_uncompressed)! // Lets build params builder @@ -797,7 +512,8 @@ fn evpkey_from_seed(seed []u8, opt CurveOptions) !&C.EVP_PKEY { assert param_bld != 0 // push the group, private and public key bytes infos into the builder - n := C.OSSL_PARAM_BLD_push_utf8_string(param_bld, c'group', opt.nid.str().str, 0) + n := C.OSSL_PARAM_BLD_push_utf8_string(param_bld, c'group', voidptr(opt.nid.str().str), + 0) m := C.OSSL_PARAM_BLD_push_BN(param_bld, c'priv', bn) o := C.OSSL_PARAM_BLD_push_octet_string(param_bld, c'pub', pub_bytes.data, pub_bytes.len) if n <= 0 || m <= 0 || o <= 0 { diff --git a/vlib/crypto/ecdsa/ecdsa_test.v b/vlib/crypto/ecdsa/ecdsa_test.v index bb4ccbc1e7..4e7e51a72a 100644 --- a/vlib/crypto/ecdsa/ecdsa_test.v +++ b/vlib/crypto/ecdsa/ecdsa_test.v @@ -34,10 +34,10 @@ fn test_ecdsa_signing_with_recommended_hash_options() { } fn test_generate_key() ! { - // Test key generation + // Test key generation with high level opaque pub_key, priv_key := generate_key() or { panic(err) } - assert pub_key.key != unsafe { nil } - assert priv_key.key != unsafe { nil } + assert pub_key.evpkey != unsafe { nil } + assert priv_key.evpkey != unsafe { nil } priv_key.free() pub_key.free() @@ -176,7 +176,7 @@ fn test_different_keys_not_equal() ! { fn test_private_key_new() ! { priv_key := PrivateKey.new()! assert priv_key.ks_flag == .fixed - size := ec_key_size(priv_key.key)! + size := evp_key_size(priv_key.evpkey)! assert size == 32 pubkey := priv_key.public_key()! diff --git a/vlib/crypto/ecdsa/util.v b/vlib/crypto/ecdsa/util.v index fa645d6ea2..39a84001f2 100644 --- a/vlib/crypto/ecdsa/util.v +++ b/vlib/crypto/ecdsa/util.v @@ -37,26 +37,34 @@ pub fn pubkey_from_bytes(bytes []u8) !PublicKey { return error('Get an nid of non ecPublicKey') } - eckey := C.EVP_PKEY_get1_EC_KEY(pub_key) - if eckey == 0 { - C.EC_KEY_free(eckey) - return error('Failed to get ec key') + // check the key + pctx := C.EVP_PKEY_CTX_new(pub_key, 0) + if pctx == 0 { + C.EVP_PKEY_CTX_free(pctx) + C.EVP_PKEY_free(pub_key) + return error('EVP_PKEY_CTX_new failed') } - // check the group for the supported curve(s) - group := voidptr(C.EC_KEY_get0_group(eckey)) - if group == 0 { - C.EC_GROUP_free(group) - return error('Failed to load group from key') + // performs public-only evpkey check + nck := C.EVP_PKEY_public_check(pctx) + if nck != 1 { + C.EVP_PKEY_CTX_free(pctx) + C.EVP_PKEY_free(pub_key) + return error('EVP_PKEY_check failed') } - nidgroup := C.EC_GROUP_get_curve_name(group) - if nidgroup != nid_prime256v1 && nidgroup != nid_secp384r1 && nidgroup != nid_secp521r1 - && nidgroup != nid_secp256k1 { + // Matching the supported group + gn := key_group_name(pub_key)! + // TODO: using shortname constant + if gn != 'secp256k1' && gn != 'secp384r1' && gn != 'secp521r1' && gn != 'prime256v1' { + C.EVP_PKEY_CTX_free(pctx) + C.EVP_PKEY_free(pub_key) return error('Unsupported group') } - C.EVP_PKEY_free(pub_key) + // Cleans up + C.EVP_PKEY_CTX_free(pctx) + // Its OK to return return PublicKey{ - key: eckey + evpkey: pub_key } } @@ -83,10 +91,13 @@ pub fn pubkey_from_string(s string) !PublicKey { mut evpkey := C.EVP_PKEY_new() bo := C.BIO_new(C.BIO_s_mem()) if bo == 0 { + C.EVP_PKEY_free(evpkey) + C.BIO_free_all(bo) return error('Failed to create BIO_new') } n := C.BIO_write(bo, s.str, s.len) if n <= 0 { + C.EVP_PKEY_free(evpkey) C.BIO_free_all(bo) return error('BIO_write failed') } @@ -104,33 +115,38 @@ pub fn pubkey_from_string(s string) !PublicKey { C.EVP_PKEY_free(evpkey) return error('Get an nid of non ecPublicKey') } - // Gets the ec key - eckey := C.EVP_PKEY_get1_EC_KEY(evpkey) - if eckey == 0 { + // check the key + pctx := C.EVP_PKEY_CTX_new(evpkey, 0) + if pctx == 0 { C.BIO_free_all(bo) - C.EC_KEY_free(eckey) + C.EVP_PKEY_CTX_free(pctx) C.EVP_PKEY_free(evpkey) - return error('Failed to get ec key') + return error('EVP_PKEY_CTX_new failed') } - // check the group for the supported curve(s) - if !is_valid_supported_group(eckey) { + // performs public-only evpkey check + nck := C.EVP_PKEY_public_check(pctx) + if nck != 1 { C.BIO_free_all(bo) - C.EC_KEY_free(eckey) + C.EVP_PKEY_CTX_free(pctx) + C.EVP_PKEY_free(evpkey) + return error('EVP_PKEY_check failed') + } + // Matching the supported group + gn := key_group_name(evpkey)! + // TODO: using shortname constant + if gn != 'secp256k1' && gn != 'secp384r1' && gn != 'secp521r1' && gn != 'prime256v1' { + C.BIO_free_all(bo) + C.EVP_PKEY_CTX_free(pctx) C.EVP_PKEY_free(evpkey) return error('Unsupported group') } - chk := C.EC_KEY_check_key(eckey) - if chk == 0 { - C.BIO_free_all(bo) - C.EC_KEY_free(eckey) - C.EVP_PKEY_free(evpkey) - return error('EC_KEY_check_key failed') - } + // Cleans up C.BIO_free_all(bo) + C.EVP_PKEY_CTX_free(pctx) + // Its OK to return return PublicKey{ evpkey: evpkey - key: eckey } } @@ -146,11 +162,14 @@ pub fn privkey_from_string(s string) !PrivateKey { mut evpkey := C.EVP_PKEY_new() bo := C.BIO_new(C.BIO_s_mem()) if bo == 0 { + C.BIO_free_all(bo) + C.EVP_PKEY_free(evpkey) return error('Failed to create BIO_new') } n := C.BIO_write(bo, s.str, s.len) if n <= 0 { C.BIO_free_all(bo) + C.EVP_PKEY_free(evpkey) return error('BIO_write failed') } evpkey = C.PEM_read_bio_PrivateKey(bo, &evpkey, 0, 0) @@ -168,66 +187,61 @@ pub fn privkey_from_string(s string) !PrivateKey { C.EVP_PKEY_free(evpkey) return error('Get an nid of non ecPublicKey') } - - eckey := C.EVP_PKEY_get1_EC_KEY(evpkey) - if eckey == 0 { + pctx := C.EVP_PKEY_CTX_new(evpkey, 0) + if pctx == 0 { C.BIO_free_all(bo) - C.EC_KEY_free(eckey) + C.EVP_PKEY_CTX_free(pctx) C.EVP_PKEY_free(evpkey) - return error('Failed to get ec key') + return error('EVP_PKEY_CTX_new failed') } - // check the group for the supported curve(s) - if !is_valid_supported_group(eckey) { + // performs evpkey check + nck := C.EVP_PKEY_check(pctx) + if nck != 1 { C.BIO_free_all(bo) - C.EC_KEY_free(eckey) + C.EVP_PKEY_CTX_free(pctx) + C.EVP_PKEY_free(evpkey) + return error('EVP_PKEY_check failed') + } + // Matching the supported group + gn := key_group_name(evpkey)! + // TODO: using shortname constant + if gn != 'secp256k1' && gn != 'secp384r1' && gn != 'secp521r1' && gn != 'prime256v1' { + C.BIO_free_all(bo) + C.EVP_PKEY_CTX_free(pctx) C.EVP_PKEY_free(evpkey) return error('Unsupported group') } - - chk := C.EC_KEY_check_key(eckey) - if chk == 0 { - C.BIO_free_all(bo) - C.EC_KEY_free(eckey) - C.EVP_PKEY_free(evpkey) - return error('EC_KEY_check_key failed') - } - ksize := ec_key_size(eckey)! - + // Cleans up C.BIO_free_all(bo) + C.EVP_PKEY_CTX_free(pctx) // Its OK to return return PrivateKey{ evpkey: evpkey - key: eckey ks_flag: .fixed - ks_size: ksize } } -// Helpers -// -// is_valid_supported_group checks whether this eckey has valid group of supported curve. -@[inline] -fn is_valid_supported_group(eckey &C.EC_KEY) bool { - group := voidptr(C.EC_KEY_get0_group(eckey)) - if group == 0 { - return false - } - nidgroup := C.EC_GROUP_get_curve_name(group) - if nidgroup == nid_prime256v1 || nidgroup == nid_secp384r1 || nidgroup == nid_secp521r1 - || nidgroup == nid_secp256k1 { - return true - } - return false -} - -// key_size get the key size of this ec key -fn ec_key_size(ec_key &C.EC_KEY) !int { - group := voidptr(C.EC_KEY_get0_group(ec_key)) - if group == 0 { - return error('Unable to load group') - } - num_bits := C.EC_GROUP_get_degree(group) +// evp_key_size get the key size of this ec key +fn evp_key_size(key &C.EVP_PKEY) !int { + num_bits := C.EVP_PKEY_get_bits(key) key_size := (num_bits + 7) / 8 + return key_size } + +const default_groupname_size = 25 // short name commonly only take 10-15 length + +// key_group_name returns underlying group name of the key as a string. +fn key_group_name(key &C.EVP_PKEY) !string { + gname := []u8{len: default_groupname_size} + gname_len := usize(0) + mut s := C.EVP_PKEY_get_group_name(key, gname.data, u32(gname.len), &gname_len) + if s == 0 { + unsafe { gname.free() } + return error('fail to get group name') + } + group := gname[..gname_len].bytestr() + unsafe { gname.free() } + return group +}