diff --git a/vlib/crypto/ecdsa/ecdsa.c.v b/vlib/crypto/ecdsa/ecdsa.c.v index f3f624cd98ab56..4e155606cfea4b 100644 --- a/vlib/crypto/ecdsa/ecdsa.c.v +++ b/vlib/crypto/ecdsa/ecdsa.c.v @@ -31,6 +31,17 @@ 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 +// EVP_PKEY Context +@[typedef] +struct C.EVP_PKEY_CTX {} + +fn C.EVP_PKEY_CTX_new_id(id int, e voidptr) &C.EVP_PKEY_CTX +fn C.EVP_PKEY_keygen_init(ctx &C.EVP_PKEY_CTX) int +fn C.EVP_PKEY_keygen(ctx &C.EVP_PKEY_CTX, ppkey &&C.EVP_PKEY) int +fn C.EVP_PKEY_CTX_set_ec_paramgen_curve_nid(ctx &C.EVP_PKEY_CTX, nid int) int +fn C.EVP_PKEY_CTX_set_ec_param_enc(ctx &C.EVP_PKEY_CTX, param_enc int) int +fn C.EVP_PKEY_CTX_free(ctx &C.EVP_PKEY_CTX) + // Elliptic curve keypair declarations @[typedef] struct C.EC_KEY {} diff --git a/vlib/crypto/ecdsa/ecdsa.v b/vlib/crypto/ecdsa/ecdsa.v index d77d5092c1e34f..ce1e36513e8ca3 100644 --- a/vlib/crypto/ecdsa/ecdsa.v +++ b/vlib/crypto/ecdsa/ecdsa.v @@ -26,8 +26,12 @@ const nid_secp256k1 = C.NID_secp256k1 // #define NID_X9_62_id_ecPublicKey 408 const nid_ec_publickey = C.NID_X9_62_id_ecPublicKey +// C.EVP_PKEY_EC = NID_X9_62_id_ecPublicKey +const nid_evp_pkey_ec = C.EVP_PKEY_EC +// we only support this +const openssl_ec_named_curve = C.OPENSSL_EC_NAMED_CURVE -// The list of supported curve(s) +// Nid is an enumeration of the supported curves pub enum Nid { prime256v1 secp384r1 @@ -46,7 +50,27 @@ pub mut: fixed_size bool } -// enum flag to allow flexible PrivateKey size +// HashConfig is an enumeration of the possible options for key signing (verifying). +pub enum HashConfig { + with_recommended_hash + with_no_hash + with_custom_hash +} + +// SignerOpts represents configuration options to drive signing and verifying process. +@[params] +pub struct SignerOpts { +pub mut: + // default to .with_recommended_hash + hash_config HashConfig = .with_recommended_hash + // make sense when HashConfig != with_recommended_hash + allow_smaller_size bool + allow_custom_hash bool + // set to non-nil if allow_custom_hash was true + custom_hash &hash.Hash = unsafe { nil } +} + +// KeyFlag is an enumeration of possible options to support flexible of PrivateKey key size. enum KeyFlag { // flexible flag to allow flexible-size of seed bytes flexible @@ -54,61 +78,6 @@ enum KeyFlag { fixed } -// 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 -mut: - // ks_flag with .flexible value allowing - // flexible-size seed bytes as key. - // When it is `.fixed`, it will use the underlying key size. - ks_flag KeyFlag = .flexible - // ks_size stores size of the seed bytes when ks_flag was .flexible. - // You should set it to a non zero value - ks_size int -} - -// 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 -} - -// PrivateKey.new creates a new key pair. By default, it would create a prime256v1 based key. -pub fn PrivateKey.new(opt CurveOptions) !PrivateKey { - // creates new empty key - ec_key := new_curve(opt) - if ec_key == 0 { - C.EC_KEY_free(ec_key) - return error('Failed to create new EC_KEY') - } - // Generates new public and private key for the supplied ec_key object. - res := C.EC_KEY_generate_key(ec_key) - if res != 1 { - C.EC_KEY_free(ec_key) - return error('Failed to generate EC_KEY') - } - // performs explicit check - chk := C.EC_KEY_check_key(ec_key) - if chk == 0 { - C.EC_KEY_free(ec_key) - return error('EC_KEY_check_key failed') - } - // when using default EC_KEY_generate_key, its using underlying curve key size - // and discarded opt.fixed_size flag when its not set. - priv_key := PrivateKey{ - key: ec_key - ks_flag: .fixed - } - return priv_key -} - // generate_key generates a new key pair. If opt was not provided, its default to prime256v1 curve. // If you want another curve, use in the following manner: `pubkey, pivkey := ecdsa.generate_key(nid: .secp384r1)!` pub fn generate_key(opt CurveOptions) !(PublicKey, PrivateKey) { @@ -250,7 +219,7 @@ pub fn new_key_from_seed(seed []u8, opt CurveOptions) !PrivateKey { // EC_KEY_check_key return 1 on success or 0 on error. chk := C.EC_KEY_check_key(ec_key) if chk == 0 { - key_free(ec_key) + C.EC_KEY_free(ec_key) return error('EC_KEY_check_key failed') } C.EC_POINT_free(pub_key_point) @@ -271,6 +240,99 @@ pub fn new_key_from_seed(seed []u8, opt CurveOptions) !PrivateKey { return pvkey } +// 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 +mut: + // ks_flag with .flexible value allowing + // flexible-size seed bytes as key. + // When it is `.fixed`, it will use the underlying key size. + ks_flag KeyFlag = .flexible + // ks_size stores size of the seed bytes when ks_flag was .flexible. + // You should set it to a non zero value + ks_size int +} + +// PrivateKey.new creates a new key pair. By default, it would create a prime256v1 based key. +// Dont forget to call `.free()` after finish with your key. +pub fn PrivateKey.new(opt CurveOptions) !PrivateKey { + // Default to prime256v1 based key + mut group_nid := nid_prime256v1 + match opt.nid { + .prime256v1 {} + .secp384r1 { + group_nid = nid_secp384r1 + } + .secp521r1 { + group_nid = nid_secp521r1 + } + .secp256k1 { + group_nid = nid_secp256k1 + } + } + // New high level keypair generator + evpkey := C.EVP_PKEY_new() + pctx := C.EVP_PKEY_CTX_new_id(nid_evp_pkey_ec, 0) + if pctx == 0 { + C.EVP_PKEY_free(evpkey) + C.EVP_PKEY_CTX_free(pctx) + return error('C.EVP_PKEY_CTX_new_id failed') + } + nt := C.EVP_PKEY_keygen_init(pctx) + if nt <= 0 { + C.EVP_PKEY_free(evpkey) + C.EVP_PKEY_CTX_free(pctx) + return error('EVP_PKEY_keygen_init failed') + } + // set the group (curve) + cn := C.EVP_PKEY_CTX_set_ec_paramgen_curve_nid(pctx, group_nid) + if cn <= 0 { + C.EVP_PKEY_free(evpkey) + C.EVP_PKEY_CTX_free(pctx) + return error('EVP_PKEY_CTX_set_ec_paramgen_curve_nid') + } + // explicitly only allowing named curve, likely its the default on 3.0. + pn := C.EVP_PKEY_CTX_set_ec_param_enc(pctx, openssl_ec_named_curve) + if pn <= 0 { + C.EVP_PKEY_free(evpkey) + C.EVP_PKEY_CTX_free(pctx) + return error('EVP_PKEY_CTX_set_ec_param_enc failed') + } + // generates keypair + nr := C.EVP_PKEY_keygen(pctx, &evpkey) + if nr <= 0 { + C.EVP_PKEY_free(evpkey) + C.EVP_PKEY_CTX_free(pctx) + 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 +} + // 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 { @@ -303,18 +365,6 @@ fn (priv_key PrivateKey) sign_message(message []u8) ![]u8 { return signed_data.clone() } -// 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 (pub_key PublicKey) verify(message []u8, sig []u8, opt SignerOpts) !bool { - digest := calc_digest(pub_key.key, message, opt)! - res := C.ECDSA_verify(0, digest.data, digest.len, sig.data, sig.len, pub_key.key) - if res == -1 { - return error('Failed to verify signature') - } - return res == 1 -} - // bytes represent private key as bytes. pub fn (priv_key PrivateKey) bytes() ![]u8 { bn := voidptr(C.EC_KEY_get0_private_key(priv_key.key)) @@ -413,6 +463,33 @@ pub fn (priv_key PrivateKey) equal(other PrivateKey) bool { return false } +// 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 +} + +// 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 (pub_key PublicKey) verify(message []u8, sig []u8, opt SignerOpts) !bool { + digest := calc_digest(pub_key.key, message, opt)! + res := C.ECDSA_verify(0, digest.data, digest.len, sig.data, sig.len, pub_key.key) + if res == -1 { + return error('Failed to verify signature') + } + return res == 1 +} + // Compare two public keys pub fn (pub_key PublicKey) equal(other PublicKey) bool { // TODO: check validity of the group @@ -443,6 +520,13 @@ pub fn (pub_key PublicKey) equal(other PublicKey) bool { return false } +// 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). @@ -492,24 +576,6 @@ fn recommended_hash(key &C.EC_KEY) !crypto.Hash { } } -pub enum HashConfig { - with_recommended_hash - with_no_hash - with_custom_hash -} - -@[params] -pub struct SignerOpts { -pub mut: - // default to .with_recommended_hash - hash_config HashConfig = .with_recommended_hash - // make sense when HashConfig != with_recommended_hash - allow_smaller_size bool - allow_custom_hash bool - // set to non-nil if allow_custom_hash was true - custom_hash &hash.Hash = unsafe { nil } -} - fn calc_digest_with_recommended_hash(key &C.EC_KEY, msg []u8) ![]u8 { h := recommended_hash(key)! match h { @@ -591,15 +657,3 @@ fn calc_digest(key &C.EC_KEY, message []u8, opt SignerOpts) ![]u8 { fn key_free(ec_key &C.EC_KEY) { C.EC_KEY_free(ec_key) } - -// free clears out allocated memory for PublicKey. -// Dont use PublicKey after calling `.free()` -pub fn (pb &PublicKey) free() { - C.EC_KEY_free(pb.key) -} - -// free clears out allocated memory for PrivateKey -// Dont use PrivateKey after calling `.free()` -pub fn (pv &PrivateKey) free() { - C.EC_KEY_free(pv.key) -}