@@ -31,12 +31,30 @@ const nid_evp_pkey_ec = C.EVP_PKEY_EC
31
31
// we only support this
32
32
const openssl_ec_named_curve = C.OPENSSL_EC_NAMED_CURVE
33
33
34
+ // https://docs.openssl.org/3.0/man3/EVP_PKEY_fromdata/#selections
35
+ const evp_pkey_keypair = C.EVP_PKEY_KEYPAIR
36
+
37
+ // POINT_CONVERSION FORAMT
38
+ const point_conversion_uncompressed = 4
39
+
34
40
// Nid is an enumeration of the supported curves
35
41
pub enum Nid {
36
- prime256 v1
37
- secp384 r1
38
- secp521 r1
39
- secp256 k1
42
+ prime256v1 = C.NID_X9_62_ prime256 v1
43
+ secp384r1 = C.NID_secp384 r1
44
+ secp521r1 = C.NID_secp521 r1
45
+ secp256k1 = C.NID_secp256 k1
46
+ }
47
+
48
+ // we need this group (cruve) name representation to pass them into needed routines
49
+ fn (nid Nid) str () string {
50
+ match nid {
51
+ // TODO: maybe better relies on info from underlying C defined constants,
52
+ // ie, #define SN_X9_62_prime256v1 "prime256v1" etc
53
+ .prime256 v1 { return 'prime256v1' }
54
+ .secp384 r1 { return 'secp384r1' }
55
+ .secp521 r1 { return 'secp521r1' }
56
+ .secp256 k1 { return 'secp256k1' }
57
+ }
40
58
}
41
59
42
60
@[params]
@@ -103,92 +121,32 @@ pub fn new_key_from_seed(seed []u8, opt CurveOptions) !PrivateKey {
103
121
if seed.len == 0 {
104
122
return error ('Seed with null-length was not allowed' )
105
123
}
106
- // Create a new EC_KEY object with the specified curve
107
- ec_key := new_curve (opt)
108
- if ec_key == 0 {
109
- C.EC_KEY_free (ec_key)
110
- return error ('Failed to create new EC_KEY' )
111
- }
112
- // Retrieve the EC_GROUP object associated with the EC_KEY
113
- // Note: cast with voidptr() to allow -cstrict checks to pass
114
- group := voidptr (C.EC_KEY_get0_group (ec_key))
115
- if group == 0 {
116
- C.EC_KEY_free (ec_key)
117
- return error ('Unable to load group' )
118
- }
119
- // Adds early check for upper size, so, we dont hit unnecessary
120
- // call to math intensive calculation, conversion and checking routines.
121
- num_bits := C.EC_GROUP_get_degree (group)
124
+ evpkey := evpkey_from_seed (seed, opt)!
125
+ num_bits := C.EVP_PKEY_get_bits (evpkey)
122
126
key_size := (num_bits + 7 ) / 8
123
127
if seed.len > key_size {
124
- C.EC_KEY_free (ec_key )
128
+ C.EVP_PKEY_free (evpkey )
125
129
return error ('Seed length exceeds key size' )
126
130
}
127
131
// Check if its using fixed key size or flexible one
128
132
if opt.fixed_size {
129
133
if seed.len != key_size {
130
- C.EC_KEY_free (ec_key )
134
+ C.EVP_PKEY_free (evpkey )
131
135
return error ('seed size doesnt match with curve key size' )
132
136
}
133
137
}
134
- // Convert the seed bytes into a BIGNUM
135
- bn := C.BN_bin2bn (seed.data, seed.len, 0 )
136
- if bn == 0 {
137
- C.EC_KEY_free (ec_key)
138
- return error ('Failed to create BIGNUM from seed' )
139
- }
140
- // Set the BIGNUM as the private key in the EC_KEY object
141
- mut res := C.EC_KEY_set_private_key (ec_key, bn)
142
- if res != 1 {
143
- C.BN_free (bn)
144
- C.EC_KEY_free (ec_key)
145
- return error ('Failed to set private key' )
146
- }
147
- // Now compute the public key
148
- //
149
- // Create a new EC_POINT object for the public key
150
- pub_key_point := C.EC_POINT_new (group)
151
- // Create a new BN_CTX object for efficient BIGNUM operations
152
- ctx := C.BN_CTX_new ()
153
- if ctx == 0 {
154
- C.EC_POINT_free (pub_key_point)
155
- C.BN_free (bn)
156
- C.EC_KEY_free (ec_key)
157
- return error ('Failed to create BN_CTX' )
158
- }
159
- defer {
160
- C.BN_CTX_free (ctx)
161
- }
162
- // Perform the point multiplication to compute the public key: pub_key_point = bn * G
163
- res = C.EC_POINT_mul (group, pub_key_point, bn, 0 , 0 , ctx)
164
- if res != 1 {
165
- C.EC_POINT_free (pub_key_point)
166
- C.BN_free (bn)
167
- C.EC_KEY_free (ec_key)
168
- return error ('Failed to compute public key' )
169
- }
170
- // Set the computed public key in the EC_KEY object
171
- res = C.EC_KEY_set_public_key (ec_key, pub_key_point)
172
- if res != 1 {
173
- C.EC_POINT_free (pub_key_point)
174
- C.BN_free (bn)
175
- C.EC_KEY_free (ec_key)
176
- return error ('Failed to set public key' )
177
- }
178
- // Add key check
179
- // EC_KEY_check_key return 1 on success or 0 on error.
180
- chk := C.EC_KEY_check_key (ec_key)
181
- if chk == 0 {
182
- C.EC_KEY_free (ec_key)
183
- return error ('EC_KEY_check_key failed' )
138
+ // TODO: remove this when its ready to go out
139
+ eckey := C.EVP_PKEY_get1_EC_KEY (evpkey)
140
+ if eckey == 0 {
141
+ C.EC_KEY_free (eckey)
142
+ C.EVP_PKEY_free (evpkey)
143
+ return error ('EVP_PKEY_get1_EC_KEY failed' )
184
144
}
185
- C.EC_POINT_free (pub_key_point)
186
- C.BN_free (bn)
187
-
188
145
mut pvkey := PrivateKey{
189
- key: ec_key
146
+ key: eckey
147
+ evpkey: evpkey
190
148
}
191
- // we set the flag information on the key
149
+
192
150
if opt.fixed_size {
193
151
// using fixed one
194
152
pvkey.ks_flag = .fixed
@@ -435,6 +393,11 @@ pub fn (pv PrivateKey) public_key() !PublicKey {
435
393
// - whether both of private keys lives under the same group (curve),
436
394
// - compares if two private key bytes was equal.
437
395
pub fn (priv_key PrivateKey) equal (other PrivateKey) bool {
396
+ if priv_key.evpkey != unsafe { nil } && other.evpkey != unsafe { nil } {
397
+ eq := C.EVP_PKEY_eq (voidptr (priv_key.evpkey), voidptr (other.evpkey))
398
+ return eq == 1
399
+ }
400
+ // TODO: remove this when its ready
438
401
group1 := voidptr (C.EC_KEY_get0_group (priv_key.key))
439
402
group2 := voidptr (C.EC_KEY_get0_group (other.key))
440
403
ctx := C.BN_CTX_new ()
@@ -488,6 +451,10 @@ pub fn (pb PublicKey) verify(message []u8, sig []u8, opt SignerOpts) !bool {
488
451
489
452
// Compare two public keys
490
453
pub fn (pub_key PublicKey) equal (other PublicKey) bool {
454
+ if pub_key.evpkey != unsafe { nil } && other.evpkey != unsafe { nil } {
455
+ eq := C.EVP_PKEY_eq (voidptr (pub_key.evpkey), voidptr (other.evpkey))
456
+ return eq == 1
457
+ }
491
458
// TODO: check validity of the group
492
459
group1 := voidptr (C.EC_KEY_get0_group (pub_key.key))
493
460
group2 := voidptr (C.EC_KEY_get0_group (other.key))
@@ -759,7 +726,7 @@ fn calc_digest_with_md(msg []u8, md &C.EVP_MD) ![]u8 {
759
726
upd := C.EVP_DigestUpdate (ctx, msg.data, msg.len)
760
727
assert upd == 1
761
728
762
- size := usize (C.EVP_MD_get_size (md))
729
+ size := u32 (C.EVP_MD_get_size (md))
763
730
out := []u8 {len: int (size)}
764
731
765
732
fin := C.EVP_DigestFinal (ctx, out.data, & size)
@@ -797,3 +764,138 @@ fn default_digest(key &C.EVP_PKEY) !&C.EVP_MD {
797
764
}
798
765
return error ('should not here' )
799
766
}
767
+
768
+ // Build EVP_PKEY from raw seed of bytes and options.
769
+ fn evpkey_from_seed (seed []u8 , opt CurveOptions) ! & C.EVP_PKEY {
770
+ // This routine mostly comes from the official docs with adds some checking at
771
+ // https://docs.openssl.org/3.0/man3/EVP_PKEY_fromdata/#creating-an-ecc-keypair-using-raw-key-data
772
+ //
773
+ // convert the seed bytes to BIGNUM.
774
+ bn := C.BN_bin2bn (seed.data, seed.len, 0 )
775
+ if bn == 0 {
776
+ C.BN_free (bn)
777
+ return error ('BN_bin2bn failed from seed' )
778
+ }
779
+ // build the group (curve) from the options.
780
+ group := C.EC_GROUP_new_by_curve_name (int (opt.nid))
781
+ if group == 0 {
782
+ C.EC_GROUP_free (group)
783
+ C.BN_free (bn)
784
+ return error ('EC_GROUP_new_by_curve_name failed' )
785
+ }
786
+ // Build EC_POINT from this BIGNUM and gets bytes represantion of this point
787
+ // in uncompressed format.
788
+ point := ec_point_mult (group, bn)!
789
+ pub_bytes := point_2_buf (group, point, point_conversion_uncompressed)!
790
+
791
+ // Lets build params builder
792
+ param_bld := C.OSSL_PARAM_BLD_new ()
793
+ assert param_bld != 0
794
+
795
+ // push the group, private and public key bytes infos into the builder
796
+ n := C.OSSL_PARAM_BLD_push_utf8_string (param_bld, c 'group' , opt.nid.str ().str, 0 )
797
+ m := C.OSSL_PARAM_BLD_push_BN (param_bld, c 'priv' , bn)
798
+ o := C.OSSL_PARAM_BLD_push_octet_string (param_bld, c 'pub' , pub_bytes.data, pub_bytes.len)
799
+ if n < = 0 || m < = 0 || o < = 0 {
800
+ C.EC_POINT_free (point)
801
+ C.BN_free (bn)
802
+ C.EC_GROUP_free (group)
803
+ C.OSSL_PARAM_BLD_free (param_bld)
804
+ return error ('OSSL_PARAM_BLD_push FAILED' )
805
+ }
806
+ // Setup the new key
807
+ mut pkey := C.EVP_PKEY_new ()
808
+ assert pkey != 0
809
+
810
+ // build parameter, initialize and build the key from params
811
+ params := C.OSSL_PARAM_BLD_to_param (param_bld)
812
+ pctx := C.EVP_PKEY_CTX_new_id (nid_evp_pkey_ec, 0 )
813
+ if params == 0 || pctx == 0 {
814
+ C.EC_POINT_free (point)
815
+ C.BN_free (bn)
816
+ C.EC_GROUP_free (group)
817
+ C.OSSL_PARAM_BLD_free (param_bld)
818
+ C.OSSL_PARAM_free (params)
819
+ C.EVP_PKEY_free (pkey)
820
+ if pctx == 0 {
821
+ C.EVP_PKEY_CTX_free (pctx)
822
+ }
823
+ return error ('EVP_PKEY_CTX_new or OSSL_PARAM_BLD_to_param failed' )
824
+ }
825
+ // initialize key and build the key from builded params context.
826
+ p := C.EVP_PKEY_fromdata_init (pctx)
827
+ q := C.EVP_PKEY_fromdata (pctx, & pkey, evp_pkey_keypair, params)
828
+ if p < = 0 || q < = 0 {
829
+ C.EC_POINT_free (point)
830
+ C.BN_free (bn)
831
+ C.EC_GROUP_free (group)
832
+ C.OSSL_PARAM_BLD_free (param_bld)
833
+ C.OSSL_PARAM_free (params)
834
+ C.EVP_PKEY_free (pkey)
835
+ C.EVP_PKEY_CTX_free (pctx)
836
+ return error ('EVP_PKEY_fromdata failed' )
837
+ }
838
+ // After this step, we have build the key in pkey
839
+ // TODO: right way to check the builded key
840
+
841
+ // Cleans up
842
+ C.EC_POINT_free (point)
843
+ C.BN_free (bn)
844
+ C.EC_GROUP_free (group)
845
+ C.OSSL_PARAM_BLD_free (param_bld)
846
+ C.OSSL_PARAM_free (params)
847
+ C.EVP_PKEY_CTX_free (pctx)
848
+
849
+ return pkey
850
+ }
851
+
852
+ // ec_point_mult performs point multiplications, point = bn * generator
853
+ fn ec_point_mult (group & C.EC_GROUP, bn & C.BIGNUM) ! & C.EC_POINT {
854
+ // Create a new EC_POINT object for the public key
855
+ point := C.EC_POINT_new (group)
856
+ // Create a new BN_CTX object for efficient BIGNUM operations
857
+ ctx := C.BN_CTX_new ()
858
+ if ctx == 0 {
859
+ C.EC_POINT_free (point)
860
+ C.BN_CTX_free (ctx)
861
+ return error ('Failed to create BN_CTX' )
862
+ }
863
+
864
+ // Perform the point multiplication to compute the public key: point = bn * G
865
+ res := C.EC_POINT_mul (group, point, bn, 0 , 0 , ctx)
866
+ if res != 1 {
867
+ C.EC_POINT_free (point)
868
+ C.BN_CTX_free (ctx)
869
+ return error ('Failed to compute public key' )
870
+ }
871
+ C.BN_CTX_free (ctx)
872
+ return point
873
+ }
874
+
875
+ // maximum key size we supported was 64 bytes.
876
+ const default_point_bufsize = 160 // 2 * 64 + 1 + extra
877
+
878
+ // point_2_buf gets bytes representation of the EC_POINT
879
+ fn point_2_buf (group & C.EC_GROUP, point & C.EC_POINT, fmt int ) ! []u8 {
880
+ ctx := C.BN_CTX_new ()
881
+ pbuf := []u8 {len: default_point_bufsize}
882
+ // Notes from the docs:
883
+ // EC_POINT_point2buf() allocates a buffer of suitable length and writes an EC_POINT to it in octet format.
884
+ // The allocated buffer is written to *pbuf and its length is returned.
885
+ // The caller must free up the allocated buffer with a call to OPENSSL_free().
886
+ // Since the allocated buffer value is written to *pbuf the pbuf parameter MUST NOT be NULL.
887
+ // So, we explicitly call `.OPENSSL_free` on the allocated buffer.
888
+ n := C.EC_POINT_point2buf (group, point, fmt, voidptr (& pbuf.data), ctx)
889
+ if n < = 0 {
890
+ C.BN_CTX_free (ctx)
891
+ C.OPENSSL_free (voidptr (& pbuf.data))
892
+ return error ('Get null length of buf' )
893
+ }
894
+ // Gets the copy of the result with the correct length
895
+ result := pbuf[..n].clone ()
896
+
897
+ C.OPENSSL_free (voidptr (pbuf.data))
898
+ C.BN_CTX_free (ctx)
899
+
900
+ return result
901
+ }
0 commit comments