mirror of
https://github.com/vlang/v.git
synced 2025-08-03 17:57:59 -04:00
crypto.ecdsa: migrate ecdsa.PrivateKey.new()
to use a high level API (#23640)
This commit is contained in:
parent
4f85b35bb9
commit
d30598bbca
@ -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 {}
|
||||
|
@ -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)
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user