@@ -31,12 +31,22 @@ 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_key_parameters = C.EVP_PKEY_KEY_PARAMETERS
36
+ const evp_pkey_public_key = C.EVP_PKEY_PUBLIC_KEY
37
+ const evp_pkey_keypair = C.EVP_PKEY_KEYPAIR
38
+
39
+ // POINT_CONVERSION FORAMT
40
+ const point_conversion_compressed = 2
41
+ const point_conversion_uncompressed = 4
42
+ const point_conversion_hybrid = 6
43
+
34
44
// Nid is an enumeration of the supported curves
35
45
pub enum Nid {
36
- prime256 v1
37
- secp384 r1
38
- secp521 r1
39
- secp256 k1
46
+ prime256v1 = C.NID_X 9_62_ prime 256 v 1
47
+ secp384r1 = C.NID_secp 384 r 1
48
+ secp521r1 = C.NID_secp 521 r 1
49
+ secp256k1 = C.NID_secp 256 k 1
40
50
}
41
51
42
52
@[params]
@@ -103,92 +113,32 @@ pub fn new_key_from_seed(seed []u8, opt CurveOptions) !PrivateKey {
103
113
if seed.len == 0 {
104
114
return error ('Seed with null-length was not allowed' )
105
115
}
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)
116
+ evpkey := evpkey_from_seed_with (seed, opt)!
117
+ num_bits := C.EVP_PKEY_get_bits (evpkey)
122
118
key_size := (num_bits + 7 ) / 8
123
119
if seed.len > key_size {
124
- C.EC_KEY_free (ec_key )
120
+ C.EVP_PKEY_free (evpkey )
125
121
return error ('Seed length exceeds key size' )
126
122
}
127
123
// Check if its using fixed key size or flexible one
128
124
if opt.fixed_size {
129
125
if seed.len != key_size {
130
- C.EC_KEY_free (ec_key )
126
+ C.EVP_PKEY_free (evpkey )
131
127
return error ('seed size doesnt match with curve key size' )
132
128
}
133
129
}
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' )
130
+ // TODO: remove this when its ready to go out
131
+ eckey := C.EVP_PKEY_get1_EC_KEY (evpkey)
132
+ if eckey == 0 {
133
+ C.EC_KEY_free (eckey)
134
+ C.EVP_PKEY_free (evpkey)
135
+ return error ('EVP_PKEY_get1_EC_KEY failed' )
184
136
}
185
- C.EC_POINT_free (pub_key_point)
186
- C.BN_free (bn)
187
-
188
137
mut pvkey := PrivateKey{
189
- key: ec_key
138
+ key: eckey
139
+ evpkey: evpkey
190
140
}
191
- // we set the flag information on the key
141
+
192
142
if opt.fixed_size {
193
143
// using fixed one
194
144
pvkey.ks_flag = .fixed
@@ -435,6 +385,11 @@ pub fn (pv PrivateKey) public_key() !PublicKey {
435
385
// - whether both of private keys lives under the same group (curve),
436
386
// - compares if two private key bytes was equal.
437
387
pub fn (priv_key PrivateKey) equal (other PrivateKey) bool {
388
+ if priv_key.evpkey != unsafe { nil } && other.evpkey != unsafe { nil } {
389
+ eq := C.EVP_PKEY_eq (voidptr (priv_key.evpkey), voidptr (other.evpkey))
390
+ return eq == 1
391
+ }
392
+ // TODO: remove this when its ready
438
393
group1 := voidptr (C.EC_KEY_get0_group (priv_key.key))
439
394
group2 := voidptr (C.EC_KEY_get0_group (other.key))
440
395
ctx := C.BN_CTX_new ()
@@ -488,6 +443,10 @@ pub fn (pb PublicKey) verify(message []u8, sig []u8, opt SignerOpts) !bool {
488
443
489
444
// Compare two public keys
490
445
pub fn (pub_key PublicKey) equal (other PublicKey) bool {
446
+ if pub_key.evpkey != unsafe { nil } && other.evpkey != unsafe { nil } {
447
+ eq := C.EVP_PKEY_eq (voidptr (pub_key.evpkey), voidptr (other.evpkey))
448
+ return eq == 1
449
+ }
491
450
// TODO: check validity of the group
492
451
group1 := voidptr (C.EC_KEY_get0_group (pub_key.key))
493
452
group2 := voidptr (C.EC_KEY_get0_group (other.key))
@@ -759,7 +718,7 @@ fn calc_digest_with_md(msg []u8, md &C.EVP_MD) ![]u8 {
759
718
upd := C.EVP_DigestUpdate (ctx, msg.data, msg.len)
760
719
assert upd == 1
761
720
762
- size := usize (C.EVP_MD_get_size (md))
721
+ size := u32 (C.EVP_MD_get_size (md))
763
722
out := []u8 {len: int (size)}
764
723
765
724
fin := C.EVP_DigestFinal (ctx, out.data, & size)
@@ -797,3 +756,140 @@ fn default_digest(key &C.EVP_PKEY) !&C.EVP_MD {
797
756
}
798
757
return error ('should not here' )
799
758
}
759
+
760
+ // Build EVP_PKEY from raw seed of bytes and options.
761
+ fn evpkey_from_seed_with (seed []u8 , opt CurveOptions) ! & C.EVP_PKEY {
762
+ // This routine mostly comes from the official docs with adds some checking at
763
+ // https://docs.openssl.org/3.0/man3/EVP_PKEY_fromdata/#creating-an-ecc-keypair-using-raw-key-data
764
+ //
765
+ // convert the seed bytes to BIGNUM.
766
+ bn := C.BN_bin2bn (seed.data, seed.len, 0 )
767
+ if bn == 0 {
768
+ C.BN_free (bn)
769
+ return error ('BN_bin2bn failed from seed' )
770
+ }
771
+ // build the group (curve) from the options.
772
+ group := C.EC_GROUP_new_by_curve_name (int (opt.nid))
773
+ if group == 0 {
774
+ C.EC_GROUP_free (group)
775
+ C.BN_free (bn)
776
+ return error ('EC_GROUP_new_by_curve_name failed' )
777
+ }
778
+ // Build EC_POINT from this BIGNUM and gets bytes represantion of this point
779
+ // in uncompressed format.
780
+ point := ec_point_mult (group, bn)!
781
+ pub_bytes := point_2_buf (group, point, point_conversion_uncompressed)!
782
+
783
+ // Lets build params builder
784
+ param_bld := C.OSSL_PARAM_BLD_new ()
785
+ assert param_bld != 0
786
+
787
+ // push the group, private and public key bytes infos into the builder
788
+ n := C.OSSL_PARAM_BLD_push_utf8_string (param_bld, voidptr ('group' .str), voidptr (opt.nid.str ().str),
789
+ 0 )
790
+ m := C.OSSL_PARAM_BLD_push_BN (param_bld, voidptr ('priv' .str), bn)
791
+ o := C.OSSL_PARAM_BLD_push_octet_string (param_bld, voidptr ('pub' .str), pub_bytes.data,
792
+ pub_bytes.len)
793
+ if n < = 0 || m < = 0 || o < = 0 {
794
+ C.EC_POINT_free (point)
795
+ C.BN_free (bn)
796
+ C.EC_GROUP_free (group)
797
+ C.OSSL_PARAM_BLD_free (param_bld)
798
+ return error ('OSSL_PARAM_BLD_push FAILED' )
799
+ }
800
+ // Setup the new key
801
+ mut pkey := C.EVP_PKEY_new ()
802
+ assert pkey != 0
803
+
804
+ // build parameter, initialize and build the key from params
805
+ params := C.OSSL_PARAM_BLD_to_param (param_bld)
806
+ pctx := C.EVP_PKEY_CTX_new_id (nid_evp_pkey_ec, 0 )
807
+ if params == 0 || pctx == 0 {
808
+ C.EC_POINT_free (point)
809
+ C.BN_free (bn)
810
+ C.EC_GROUP_free (group)
811
+ C.OSSL_PARAM_BLD_free (param_bld)
812
+ C.OSSL_PARAM_free (params)
813
+ C.EVP_PKEY_free (pkey)
814
+ if pctx == 0 {
815
+ C.EVP_PKEY_CTX_free (pctx)
816
+ }
817
+ return error ('EVP_PKEY_CTX_new or OSSL_PARAM_BLD_to_param failed' )
818
+ }
819
+ // initialize key and build the key from builded params context.
820
+ p := C.EVP_PKEY_fromdata_init (pctx)
821
+ q := C.EVP_PKEY_fromdata (pctx, & pkey, evp_pkey_keypair, params)
822
+ if p < = 0 || q < = 0 {
823
+ C.EC_POINT_free (point)
824
+ C.BN_free (bn)
825
+ C.EC_GROUP_free (group)
826
+ C.OSSL_PARAM_BLD_free (param_bld)
827
+ C.OSSL_PARAM_free (params)
828
+ C.EVP_PKEY_free (pkey)
829
+ C.EVP_PKEY_CTX_free (pctx)
830
+ return error ('EVP_PKEY_fromdata(_init) failed' )
831
+ }
832
+ // After this step, we have build the key in pkey
833
+ // TODO: right way to check the builded key
834
+
835
+ // Cleans up
836
+ C.EC_POINT_free (point)
837
+ C.BN_free (bn)
838
+ C.EC_GROUP_free (group)
839
+ C.OSSL_PARAM_BLD_free (param_bld)
840
+ C.OSSL_PARAM_free (params)
841
+ C.EVP_PKEY_CTX_free (pctx)
842
+
843
+ return pkey
844
+ }
845
+
846
+ // ec_point_mult performs point multiplications, point = bn * generator
847
+ fn ec_point_mult (group & C.EC_GROUP, bn & C.BIGNUM) ! & C.EC_POINT {
848
+ // Create a new EC_POINT object for the public key
849
+ point := C.EC_POINT_new (group)
850
+ // Create a new BN_CTX object for efficient BIGNUM operations
851
+ ctx := C.BN_CTX_new ()
852
+ if ctx == 0 {
853
+ C.EC_POINT_free (point)
854
+ C.BN_CTX_free (ctx)
855
+ return error ('Failed to create BN_CTX' )
856
+ }
857
+
858
+ // Perform the point multiplication to compute the public key: point = bn * G
859
+ res := C.EC_POINT_mul (group, point, bn, 0 , 0 , ctx)
860
+ if res != 1 {
861
+ C.EC_POINT_free (point)
862
+ C.BN_CTX_free (ctx)
863
+ return error ('Failed to compute public key' )
864
+ }
865
+ C.BN_CTX_free (ctx)
866
+ return point
867
+ }
868
+
869
+ // maximum key size we supported was 64 bytes.
870
+ const default_point_bufsize = 160 // 2 * 64 + 1 + extra
871
+
872
+ // point_2_buf gets bytes representation of the EC_POINT
873
+ fn point_2_buf (group & C.EC_GROUP, point & C.EC_POINT, fmt int ) ! []u8 {
874
+ ctx := C.BN_CTX_new ()
875
+ pbuf := []u8 {len: default_point_bufsize}
876
+ // Notes from the docs:
877
+ // EC_POINT_point2buf() allocates a buffer of suitable length and writes an EC_POINT to it in octet format.
878
+ // The allocated buffer is written to *pbuf and its length is returned.
879
+ // The caller must free up the allocated buffer with a call to OPENSSL_free().
880
+ // Since the allocated buffer value is written to *pbuf the pbuf parameter MUST NOT be NULL.
881
+ // So, we explicitly call `.OPENSSL_free` on the allocated buffer.
882
+ n := C.EC_POINT_point2buf (group, point, fmt, voidptr (& pbuf.data), ctx)
883
+ if n < = 0 {
884
+ C.BN_CTX_free (ctx)
885
+ C.OPENSSL_free (voidptr (& pbuf.data))
886
+ return error ('Get null length of buf' )
887
+ }
888
+ // Gets the copy of the result with the correct length
889
+ result := pbuf[..n].clone ()
890
+
891
+ C.OPENSSL_free (voidptr (pbuf.data))
892
+ C.BN_CTX_free (ctx)
893
+
894
+ return result
895
+ }
0 commit comments