Skip to content

Commit d30598b

Browse files
authored
crypto.ecdsa: migrate ecdsa.PrivateKey.new() to use a high level API (#23640)
1 parent 4f85b35 commit d30598b

File tree

2 files changed

+165
-100
lines changed

2 files changed

+165
-100
lines changed

vlib/crypto/ecdsa/ecdsa.c.v

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,17 @@ fn C.EVP_PKEY_free(key &C.EVP_PKEY)
3131
fn C.EVP_PKEY_get1_EC_KEY(pkey &C.EVP_PKEY) &C.EC_KEY
3232
fn C.EVP_PKEY_base_id(key &C.EVP_PKEY) int
3333

34+
// EVP_PKEY Context
35+
@[typedef]
36+
struct C.EVP_PKEY_CTX {}
37+
38+
fn C.EVP_PKEY_CTX_new_id(id int, e voidptr) &C.EVP_PKEY_CTX
39+
fn C.EVP_PKEY_keygen_init(ctx &C.EVP_PKEY_CTX) int
40+
fn C.EVP_PKEY_keygen(ctx &C.EVP_PKEY_CTX, ppkey &&C.EVP_PKEY) int
41+
fn C.EVP_PKEY_CTX_set_ec_paramgen_curve_nid(ctx &C.EVP_PKEY_CTX, nid int) int
42+
fn C.EVP_PKEY_CTX_set_ec_param_enc(ctx &C.EVP_PKEY_CTX, param_enc int) int
43+
fn C.EVP_PKEY_CTX_free(ctx &C.EVP_PKEY_CTX)
44+
3445
// Elliptic curve keypair declarations
3546
@[typedef]
3647
struct C.EC_KEY {}

vlib/crypto/ecdsa/ecdsa.v

Lines changed: 154 additions & 100 deletions
Original file line numberDiff line numberDiff line change
@@ -26,8 +26,12 @@ const nid_secp256k1 = C.NID_secp256k1
2626

2727
// #define NID_X9_62_id_ecPublicKey 408
2828
const nid_ec_publickey = C.NID_X9_62_id_ecPublicKey
29+
// C.EVP_PKEY_EC = NID_X9_62_id_ecPublicKey
30+
const nid_evp_pkey_ec = C.EVP_PKEY_EC
31+
// we only support this
32+
const openssl_ec_named_curve = C.OPENSSL_EC_NAMED_CURVE
2933

30-
// The list of supported curve(s)
34+
// Nid is an enumeration of the supported curves
3135
pub enum Nid {
3236
prime256v1
3337
secp384r1
@@ -46,69 +50,34 @@ pub mut:
4650
fixed_size bool
4751
}
4852

49-
// enum flag to allow flexible PrivateKey size
53+
// HashConfig is an enumeration of the possible options for key signing (verifying).
54+
pub enum HashConfig {
55+
with_recommended_hash
56+
with_no_hash
57+
with_custom_hash
58+
}
59+
60+
// SignerOpts represents configuration options to drive signing and verifying process.
61+
@[params]
62+
pub struct SignerOpts {
63+
pub mut:
64+
// default to .with_recommended_hash
65+
hash_config HashConfig = .with_recommended_hash
66+
// make sense when HashConfig != with_recommended_hash
67+
allow_smaller_size bool
68+
allow_custom_hash bool
69+
// set to non-nil if allow_custom_hash was true
70+
custom_hash &hash.Hash = unsafe { nil }
71+
}
72+
73+
// KeyFlag is an enumeration of possible options to support flexible of PrivateKey key size.
5074
enum KeyFlag {
5175
// flexible flag to allow flexible-size of seed bytes
5276
flexible
5377
// fixed flag for using underlying curve key size
5478
fixed
5579
}
5680

57-
// PrivateKey represents ECDSA private key. Actually its a key pair,
58-
// contains private key and public key parts.
59-
pub struct PrivateKey {
60-
// The new high level of keypair opaque, set to nil now.
61-
evpkey &C.EVP_PKEY = unsafe { nil }
62-
// TODO: when all has been migrated to the new one,
63-
// removes this low level declarations.
64-
key &C.EC_KEY
65-
mut:
66-
// ks_flag with .flexible value allowing
67-
// flexible-size seed bytes as key.
68-
// When it is `.fixed`, it will use the underlying key size.
69-
ks_flag KeyFlag = .flexible
70-
// ks_size stores size of the seed bytes when ks_flag was .flexible.
71-
// You should set it to a non zero value
72-
ks_size int
73-
}
74-
75-
// PublicKey represents ECDSA public key for verifying message.
76-
pub struct PublicKey {
77-
// The new high level of keypair opaque, set to nil now.
78-
evpkey &C.EVP_PKEY = unsafe { nil }
79-
// Remove this when its fully obsoleted by the new one.
80-
key &C.EC_KEY
81-
}
82-
83-
// PrivateKey.new creates a new key pair. By default, it would create a prime256v1 based key.
84-
pub fn PrivateKey.new(opt CurveOptions) !PrivateKey {
85-
// creates new empty key
86-
ec_key := new_curve(opt)
87-
if ec_key == 0 {
88-
C.EC_KEY_free(ec_key)
89-
return error('Failed to create new EC_KEY')
90-
}
91-
// Generates new public and private key for the supplied ec_key object.
92-
res := C.EC_KEY_generate_key(ec_key)
93-
if res != 1 {
94-
C.EC_KEY_free(ec_key)
95-
return error('Failed to generate EC_KEY')
96-
}
97-
// performs explicit check
98-
chk := C.EC_KEY_check_key(ec_key)
99-
if chk == 0 {
100-
C.EC_KEY_free(ec_key)
101-
return error('EC_KEY_check_key failed')
102-
}
103-
// when using default EC_KEY_generate_key, its using underlying curve key size
104-
// and discarded opt.fixed_size flag when its not set.
105-
priv_key := PrivateKey{
106-
key: ec_key
107-
ks_flag: .fixed
108-
}
109-
return priv_key
110-
}
111-
11281
// generate_key generates a new key pair. If opt was not provided, its default to prime256v1 curve.
11382
// If you want another curve, use in the following manner: `pubkey, pivkey := ecdsa.generate_key(nid: .secp384r1)!`
11483
pub fn generate_key(opt CurveOptions) !(PublicKey, PrivateKey) {
@@ -250,7 +219,7 @@ pub fn new_key_from_seed(seed []u8, opt CurveOptions) !PrivateKey {
250219
// EC_KEY_check_key return 1 on success or 0 on error.
251220
chk := C.EC_KEY_check_key(ec_key)
252221
if chk == 0 {
253-
key_free(ec_key)
222+
C.EC_KEY_free(ec_key)
254223
return error('EC_KEY_check_key failed')
255224
}
256225
C.EC_POINT_free(pub_key_point)
@@ -271,6 +240,99 @@ pub fn new_key_from_seed(seed []u8, opt CurveOptions) !PrivateKey {
271240
return pvkey
272241
}
273242

243+
// PrivateKey represents ECDSA private key. Actually its a key pair,
244+
// contains private key and public key parts.
245+
pub struct PrivateKey {
246+
// The new high level of keypair opaque, set to nil now.
247+
evpkey &C.EVP_PKEY = unsafe { nil }
248+
// TODO: when all has been migrated to the new one,
249+
// removes this low level declarations.
250+
key &C.EC_KEY
251+
mut:
252+
// ks_flag with .flexible value allowing
253+
// flexible-size seed bytes as key.
254+
// When it is `.fixed`, it will use the underlying key size.
255+
ks_flag KeyFlag = .flexible
256+
// ks_size stores size of the seed bytes when ks_flag was .flexible.
257+
// You should set it to a non zero value
258+
ks_size int
259+
}
260+
261+
// PrivateKey.new creates a new key pair. By default, it would create a prime256v1 based key.
262+
// Dont forget to call `.free()` after finish with your key.
263+
pub fn PrivateKey.new(opt CurveOptions) !PrivateKey {
264+
// Default to prime256v1 based key
265+
mut group_nid := nid_prime256v1
266+
match opt.nid {
267+
.prime256v1 {}
268+
.secp384r1 {
269+
group_nid = nid_secp384r1
270+
}
271+
.secp521r1 {
272+
group_nid = nid_secp521r1
273+
}
274+
.secp256k1 {
275+
group_nid = nid_secp256k1
276+
}
277+
}
278+
// New high level keypair generator
279+
evpkey := C.EVP_PKEY_new()
280+
pctx := C.EVP_PKEY_CTX_new_id(nid_evp_pkey_ec, 0)
281+
if pctx == 0 {
282+
C.EVP_PKEY_free(evpkey)
283+
C.EVP_PKEY_CTX_free(pctx)
284+
return error('C.EVP_PKEY_CTX_new_id failed')
285+
}
286+
nt := C.EVP_PKEY_keygen_init(pctx)
287+
if nt <= 0 {
288+
C.EVP_PKEY_free(evpkey)
289+
C.EVP_PKEY_CTX_free(pctx)
290+
return error('EVP_PKEY_keygen_init failed')
291+
}
292+
// set the group (curve)
293+
cn := C.EVP_PKEY_CTX_set_ec_paramgen_curve_nid(pctx, group_nid)
294+
if cn <= 0 {
295+
C.EVP_PKEY_free(evpkey)
296+
C.EVP_PKEY_CTX_free(pctx)
297+
return error('EVP_PKEY_CTX_set_ec_paramgen_curve_nid')
298+
}
299+
// explicitly only allowing named curve, likely its the default on 3.0.
300+
pn := C.EVP_PKEY_CTX_set_ec_param_enc(pctx, openssl_ec_named_curve)
301+
if pn <= 0 {
302+
C.EVP_PKEY_free(evpkey)
303+
C.EVP_PKEY_CTX_free(pctx)
304+
return error('EVP_PKEY_CTX_set_ec_param_enc failed')
305+
}
306+
// generates keypair
307+
nr := C.EVP_PKEY_keygen(pctx, &evpkey)
308+
if nr <= 0 {
309+
C.EVP_PKEY_free(evpkey)
310+
C.EVP_PKEY_CTX_free(pctx)
311+
return error('EVP_PKEY_keygen failed')
312+
}
313+
314+
// EVP_PKEY_get1_EC_KEY was deprecated in 3.0. Its used here for compatibility purposes
315+
// to support the old key function.
316+
// TODO: removes this when its ready to obsolete.
317+
eckey := C.EVP_PKEY_get1_EC_KEY(evpkey)
318+
if eckey == 0 {
319+
C.EVP_PKEY_CTX_free(pctx)
320+
C.EC_KEY_free(eckey)
321+
C.EVP_PKEY_free(evpkey)
322+
return error('EVP_PKEY_get1_EC_KEY failed')
323+
}
324+
// Cleans up the context
325+
C.EVP_PKEY_CTX_free(pctx)
326+
// when using default this function, its using underlying curve key size
327+
// and discarded opt.fixed_size flag when its not set.
328+
priv_key := PrivateKey{
329+
evpkey: evpkey
330+
key: eckey
331+
ks_flag: .fixed
332+
}
333+
return priv_key
334+
}
335+
274336
// sign performs signing the message with the options. By default options,
275337
// it will perform hashing before signing the message.
276338
pub fn (pv PrivateKey) sign(message []u8, opt SignerOpts) ![]u8 {
@@ -303,18 +365,6 @@ fn (priv_key PrivateKey) sign_message(message []u8) ![]u8 {
303365
return signed_data.clone()
304366
}
305367

306-
// verify verifies a message with the signature are valid with public key provided .
307-
// You should provide it with the same SignerOpts used with the `.sign()` call.
308-
// or verify would fail (false).
309-
pub fn (pub_key PublicKey) verify(message []u8, sig []u8, opt SignerOpts) !bool {
310-
digest := calc_digest(pub_key.key, message, opt)!
311-
res := C.ECDSA_verify(0, digest.data, digest.len, sig.data, sig.len, pub_key.key)
312-
if res == -1 {
313-
return error('Failed to verify signature')
314-
}
315-
return res == 1
316-
}
317-
318368
// bytes represent private key as bytes.
319369
pub fn (priv_key PrivateKey) bytes() ![]u8 {
320370
bn := voidptr(C.EC_KEY_get0_private_key(priv_key.key))
@@ -413,6 +463,33 @@ pub fn (priv_key PrivateKey) equal(other PrivateKey) bool {
413463
return false
414464
}
415465

466+
// free clears out allocated memory for PrivateKey
467+
// Dont use PrivateKey after calling `.free()`
468+
pub fn (pv &PrivateKey) free() {
469+
C.EC_KEY_free(pv.key)
470+
C.EVP_PKEY_free(pv.evpkey)
471+
}
472+
473+
// PublicKey represents ECDSA public key for verifying message.
474+
pub struct PublicKey {
475+
// The new high level of keypair opaque, set to nil now.
476+
evpkey &C.EVP_PKEY = unsafe { nil }
477+
// Remove this when its fully obsoleted by the new one.
478+
key &C.EC_KEY
479+
}
480+
481+
// verify verifies a message with the signature are valid with public key provided .
482+
// You should provide it with the same SignerOpts used with the `.sign()` call.
483+
// or verify would fail (false).
484+
pub fn (pub_key PublicKey) verify(message []u8, sig []u8, opt SignerOpts) !bool {
485+
digest := calc_digest(pub_key.key, message, opt)!
486+
res := C.ECDSA_verify(0, digest.data, digest.len, sig.data, sig.len, pub_key.key)
487+
if res == -1 {
488+
return error('Failed to verify signature')
489+
}
490+
return res == 1
491+
}
492+
416493
// Compare two public keys
417494
pub fn (pub_key PublicKey) equal(other PublicKey) bool {
418495
// TODO: check validity of the group
@@ -443,6 +520,13 @@ pub fn (pub_key PublicKey) equal(other PublicKey) bool {
443520
return false
444521
}
445522

523+
// free clears out allocated memory for PublicKey.
524+
// Dont use PublicKey after calling `.free()`
525+
pub fn (pb &PublicKey) free() {
526+
C.EC_KEY_free(pb.key)
527+
C.EVP_PKEY_free(pb.evpkey)
528+
}
529+
446530
// Helpers
447531
//
448532
// 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 {
492576
}
493577
}
494578

495-
pub enum HashConfig {
496-
with_recommended_hash
497-
with_no_hash
498-
with_custom_hash
499-
}
500-
501-
@[params]
502-
pub struct SignerOpts {
503-
pub mut:
504-
// default to .with_recommended_hash
505-
hash_config HashConfig = .with_recommended_hash
506-
// make sense when HashConfig != with_recommended_hash
507-
allow_smaller_size bool
508-
allow_custom_hash bool
509-
// set to non-nil if allow_custom_hash was true
510-
custom_hash &hash.Hash = unsafe { nil }
511-
}
512-
513579
fn calc_digest_with_recommended_hash(key &C.EC_KEY, msg []u8) ![]u8 {
514580
h := recommended_hash(key)!
515581
match h {
@@ -591,15 +657,3 @@ fn calc_digest(key &C.EC_KEY, message []u8, opt SignerOpts) ![]u8 {
591657
fn key_free(ec_key &C.EC_KEY) {
592658
C.EC_KEY_free(ec_key)
593659
}
594-
595-
// free clears out allocated memory for PublicKey.
596-
// Dont use PublicKey after calling `.free()`
597-
pub fn (pb &PublicKey) free() {
598-
C.EC_KEY_free(pb.key)
599-
}
600-
601-
// free clears out allocated memory for PrivateKey
602-
// Dont use PrivateKey after calling `.free()`
603-
pub fn (pv &PrivateKey) free() {
604-
C.EC_KEY_free(pv.key)
605-
}

0 commit comments

Comments
 (0)