v2 / vlib / crypto / ecdsa / util.v
247 lines · 233 sloc · 6.94 KB · 07c796b670d9e498ccb25605af189617f61ec295
Raw
1module ecdsa
2
3// pubkey_from_bytes loads ECDSA Public Key from bytes array.
4// The bytes of data should be a valid of ASN.1 DER serialized SubjectPublicKeyInfo structrue of RFC 5480.
5// Otherwise, its should an error.
6// Typically, you can load the bytes from pem formatted of ecdsa public key.
7//
8// Examples:
9// ```codeblock
10// import crypto.pem
11// import crypto.ecdsa
12//
13// const pubkey_sample = '-----BEGIN PUBLIC KEY-----
14// MHYwEAYHKoZIzj0CAQYFK4EEACIDYgAE+P3rhFkT1fXHYbY3CpcBdh6xTC74MQFx
15// cftNVD3zEPVzo//OalIVatY162ksg8uRWBdvFFuHZ9OMVXkbjwWwhcXP7qmI9rOS
16// LR3AGUldy+bBpV2nT306qCIwgUAMeOJP
17// -----END PUBLIC KEY-----'
18//
19// block, _ := pem.decode(pubkey_sample) or { panic(err) }
20// pubkey := ecdsa.pubkey_from_bytes(block.data)!
21// ```
22pub fn pubkey_from_bytes(bytes []u8) !PublicKey {
23 if bytes.len == 0 {
24 return error('Invalid bytes')
25 }
26 mut pub_key := C.EVP_PKEY_new()
27 pub_key = C.d2i_PUBKEY(&pub_key, voidptr(&bytes.data), bytes.len)
28 if pub_key == 0 {
29 C.EVP_PKEY_free(pub_key)
30 return error('Error loading public key')
31 }
32 // Get the NID of this pubkey, and check if the pubkey object was
33 // have the correct NID of ec public key type, ie, NID_X9_62_id_ecPublicKey
34 nid := C.EVP_PKEY_base_id(pub_key)
35 if nid != nid_ec_publickey {
36 C.EVP_PKEY_free(pub_key)
37 return error('Get an nid of non ecPublicKey')
38 }
39
40 // check the key
41 pctx := C.EVP_PKEY_CTX_new(pub_key, 0)
42 if pctx == 0 {
43 C.EVP_PKEY_CTX_free(pctx)
44 C.EVP_PKEY_free(pub_key)
45 return error('EVP_PKEY_CTX_new failed')
46 }
47 // performs public-only evpkey check
48 nck := C.EVP_PKEY_public_check(pctx)
49 if nck != 1 {
50 C.EVP_PKEY_CTX_free(pctx)
51 C.EVP_PKEY_free(pub_key)
52 return error('EVP_PKEY_check failed')
53 }
54 // Matching the supported group
55 gn := key_group_name(pub_key)!
56 // TODO: using shortname constant
57 if gn != 'secp256k1' && gn != 'secp384r1' && gn != 'secp521r1' && gn != 'prime256v1' {
58 C.EVP_PKEY_CTX_free(pctx)
59 C.EVP_PKEY_free(pub_key)
60 return error('Unsupported group')
61 }
62 // Cleans up
63 C.EVP_PKEY_CTX_free(pctx)
64
65 // Its OK to return
66 return PublicKey{
67 evpkey: pub_key
68 }
69}
70
71// bytes gets the bytes of public key.
72pub fn (pbk PublicKey) bytes() ![]u8 {
73 mut ppub := &u8(unsafe { nil })
74 n := C.EVP_PKEY_get1_encoded_public_key(pbk.evpkey, &ppub)
75 if n <= 0 {
76 C.OPENSSL_free(voidptr(ppub))
77 return error('EVP_PKEY_get1_encoded_public_key failed')
78 }
79 out := unsafe { ppub.vbytes(int(n)).clone() }
80 // ppub should be freed by calling `OPENSSL_free` or memleak happens.
81 C.OPENSSL_free(voidptr(ppub))
82
83 return out
84}
85
86// pubkey_from_string loads a PublicKey from valid PEM-formatted string in s.
87pub fn pubkey_from_string(s string) !PublicKey {
88 if s.len == 0 {
89 return error('Null length string was not allowed')
90 }
91 mut evpkey := C.EVP_PKEY_new()
92 bo := C.BIO_new(C.BIO_s_mem())
93 if bo == 0 {
94 C.EVP_PKEY_free(evpkey)
95 C.BIO_free_all(bo)
96 return error('Failed to create BIO_new')
97 }
98 n := C.BIO_write(bo, s.str, s.len)
99 if n <= 0 {
100 C.EVP_PKEY_free(evpkey)
101 C.BIO_free_all(bo)
102 return error('BIO_write failed')
103 }
104 evpkey = C.PEM_read_bio_PUBKEY(bo, &evpkey, 0, 0)
105 if evpkey == 0 {
106 C.BIO_free_all(bo)
107 C.EVP_PKEY_free(evpkey)
108 return error('Error loading key')
109 }
110 // Get the NID of this key, and check if the key object was
111 // have the correct NID of ec public key type, ie, NID_X9_62_id_ecPublicKey
112 nid := C.EVP_PKEY_base_id(evpkey)
113 if nid != nid_ec_publickey {
114 C.BIO_free_all(bo)
115 C.EVP_PKEY_free(evpkey)
116 return error('Get an nid of non ecPublicKey')
117 }
118 // check the key
119 pctx := C.EVP_PKEY_CTX_new(evpkey, 0)
120 if pctx == 0 {
121 C.BIO_free_all(bo)
122 C.EVP_PKEY_CTX_free(pctx)
123 C.EVP_PKEY_free(evpkey)
124 return error('EVP_PKEY_CTX_new failed')
125 }
126 // performs public-only evpkey check
127 nck := C.EVP_PKEY_public_check(pctx)
128 if nck != 1 {
129 C.BIO_free_all(bo)
130 C.EVP_PKEY_CTX_free(pctx)
131 C.EVP_PKEY_free(evpkey)
132 return error('EVP_PKEY_check failed')
133 }
134 // Matching the supported group
135 gn := key_group_name(evpkey)!
136 // TODO: using shortname constant
137 if gn != 'secp256k1' && gn != 'secp384r1' && gn != 'secp521r1' && gn != 'prime256v1' {
138 C.BIO_free_all(bo)
139 C.EVP_PKEY_CTX_free(pctx)
140 C.EVP_PKEY_free(evpkey)
141 return error('Unsupported group')
142 }
143 // Cleans up
144 C.BIO_free_all(bo)
145 C.EVP_PKEY_CTX_free(pctx)
146
147 // Its OK to return
148 return PublicKey{
149 evpkey: evpkey
150 }
151}
152
153// privkey_from_string loads a PrivateKey from valid PEM-formatted string in s.
154// Underlying wrapper support for old SECG and PKCS8 private key format, but this was not heavily tested.
155// This routine does not support for the PKCS8 EncryptedPrivateKeyInfo format.
156// See [ecdsa_seed_test.v](https://github.com/vlang/v/blob/master/vlib/crypto/ecdsa/example/ecdsa_seed_test.v) file
157// for example of usage.
158pub fn privkey_from_string(s string) !PrivateKey {
159 if s.len == 0 {
160 return error('null string was not allowed')
161 }
162 mut evpkey := C.EVP_PKEY_new()
163 bo := C.BIO_new(C.BIO_s_mem())
164 if bo == 0 {
165 C.BIO_free_all(bo)
166 C.EVP_PKEY_free(evpkey)
167 return error('Failed to create BIO_new')
168 }
169 n := C.BIO_write(bo, s.str, s.len)
170 if n <= 0 {
171 C.BIO_free_all(bo)
172 C.EVP_PKEY_free(evpkey)
173 return error('BIO_write failed')
174 }
175 evpkey = C.PEM_read_bio_PrivateKey(bo, &evpkey, 0, 0)
176 if evpkey == 0 {
177 C.BIO_free_all(bo)
178 C.EVP_PKEY_free(evpkey)
179 return error('Error loading key')
180 }
181
182 // Get the NID of this key, and check if the key object was
183 // have the correct NID of ec public key type, ie, NID_X9_62_id_ecPublicKey
184 nid := C.EVP_PKEY_base_id(evpkey)
185 if nid != nid_ec_publickey {
186 C.BIO_free_all(bo)
187 C.EVP_PKEY_free(evpkey)
188 return error('Get an nid of non ecPublicKey')
189 }
190 pctx := C.EVP_PKEY_CTX_new(evpkey, 0)
191 if pctx == 0 {
192 C.BIO_free_all(bo)
193 C.EVP_PKEY_CTX_free(pctx)
194 C.EVP_PKEY_free(evpkey)
195 return error('EVP_PKEY_CTX_new failed')
196 }
197 // performs evpkey check
198 nck := C.EVP_PKEY_check(pctx)
199 if nck != 1 {
200 C.BIO_free_all(bo)
201 C.EVP_PKEY_CTX_free(pctx)
202 C.EVP_PKEY_free(evpkey)
203 return error('EVP_PKEY_check failed')
204 }
205 // Matching the supported group
206 gn := key_group_name(evpkey)!
207 // TODO: using shortname constant
208 if gn != 'secp256k1' && gn != 'secp384r1' && gn != 'secp521r1' && gn != 'prime256v1' {
209 C.BIO_free_all(bo)
210 C.EVP_PKEY_CTX_free(pctx)
211 C.EVP_PKEY_free(evpkey)
212 return error('Unsupported group')
213 }
214 // Cleans up
215 C.BIO_free_all(bo)
216 C.EVP_PKEY_CTX_free(pctx)
217
218 // Its OK to return
219 return PrivateKey{
220 evpkey: evpkey
221 ks_flag: .fixed
222 }
223}
224
225// evp_key_size get the key size of this ec key
226fn evp_key_size(key &C.EVP_PKEY) !int {
227 num_bits := C.EVP_PKEY_get_bits(key)
228 key_size := (num_bits + 7) / 8
229
230 return key_size
231}
232
233const default_groupname_size = 25 // short name commonly only take 10-15 length
234
235// key_group_name returns underlying group name of the key as a string.
236fn key_group_name(key &C.EVP_PKEY) !string {
237 gname := []u8{len: default_groupname_size}
238 gname_len := usize(0)
239 mut s := C.EVP_PKEY_get_group_name(key, gname.data, u32(gname.len), &gname_len)
240 if s == 0 {
241 unsafe { gname.free() }
242 return error('fail to get group name')
243 }
244 group := gname[..int(gname_len)].bytestr()
245 unsafe { gname.free() }
246 return group
247}
248