diff options
Diffstat (limited to 'src/es256.c')
-rw-r--r-- | src/es256.c | 434 |
1 files changed, 434 insertions, 0 deletions
diff --git a/src/es256.c b/src/es256.c new file mode 100644 index 0000000..c8fd9f4 --- /dev/null +++ b/src/es256.c | |||
@@ -0,0 +1,434 @@ | |||
1 | /* | ||
2 | * Copyright (c) 2018 Yubico AB. All rights reserved. | ||
3 | * Use of this source code is governed by a BSD-style | ||
4 | * license that can be found in the LICENSE file. | ||
5 | */ | ||
6 | |||
7 | #include <openssl/bn.h> | ||
8 | #include <openssl/ec.h> | ||
9 | #include <openssl/evp.h> | ||
10 | #include <openssl/obj_mac.h> | ||
11 | |||
12 | #include <string.h> | ||
13 | #include "fido.h" | ||
14 | #include "fido/es256.h" | ||
15 | |||
16 | static int | ||
17 | decode_coord(const cbor_item_t *item, void *xy, size_t xy_len) | ||
18 | { | ||
19 | if (cbor_isa_bytestring(item) == false || | ||
20 | cbor_bytestring_is_definite(item) == false || | ||
21 | cbor_bytestring_length(item) != xy_len) { | ||
22 | fido_log_debug("%s: cbor type", __func__); | ||
23 | return (-1); | ||
24 | } | ||
25 | |||
26 | memcpy(xy, cbor_bytestring_handle(item), xy_len); | ||
27 | |||
28 | return (0); | ||
29 | } | ||
30 | |||
31 | static int | ||
32 | decode_pubkey_point(const cbor_item_t *key, const cbor_item_t *val, void *arg) | ||
33 | { | ||
34 | es256_pk_t *k = arg; | ||
35 | |||
36 | if (cbor_isa_negint(key) == false || | ||
37 | cbor_int_get_width(key) != CBOR_INT_8) | ||
38 | return (0); /* ignore */ | ||
39 | |||
40 | switch (cbor_get_uint8(key)) { | ||
41 | case 1: /* x coordinate */ | ||
42 | return (decode_coord(val, &k->x, sizeof(k->x))); | ||
43 | case 2: /* y coordinate */ | ||
44 | return (decode_coord(val, &k->y, sizeof(k->y))); | ||
45 | } | ||
46 | |||
47 | return (0); /* ignore */ | ||
48 | } | ||
49 | |||
50 | int | ||
51 | es256_pk_decode(const cbor_item_t *item, es256_pk_t *k) | ||
52 | { | ||
53 | if (cbor_isa_map(item) == false || | ||
54 | cbor_map_is_definite(item) == false || | ||
55 | cbor_map_iter(item, k, decode_pubkey_point) < 0) { | ||
56 | fido_log_debug("%s: cbor type", __func__); | ||
57 | return (-1); | ||
58 | } | ||
59 | |||
60 | return (0); | ||
61 | } | ||
62 | |||
63 | cbor_item_t * | ||
64 | es256_pk_encode(const es256_pk_t *pk, int ecdh) | ||
65 | { | ||
66 | cbor_item_t *item = NULL; | ||
67 | struct cbor_pair argv[5]; | ||
68 | int alg; | ||
69 | int ok = -1; | ||
70 | |||
71 | memset(argv, 0, sizeof(argv)); | ||
72 | |||
73 | if ((item = cbor_new_definite_map(5)) == NULL) | ||
74 | goto fail; | ||
75 | |||
76 | /* kty */ | ||
77 | if ((argv[0].key = cbor_build_uint8(1)) == NULL || | ||
78 | (argv[0].value = cbor_build_uint8(2)) == NULL || | ||
79 | !cbor_map_add(item, argv[0])) | ||
80 | goto fail; | ||
81 | |||
82 | /* | ||
83 | * "The COSEAlgorithmIdentifier used is -25 (ECDH-ES + | ||
84 | * HKDF-256) although this is NOT the algorithm actually | ||
85 | * used. Setting this to a different value may result in | ||
86 | * compatibility issues." | ||
87 | */ | ||
88 | if (ecdh) | ||
89 | alg = COSE_ECDH_ES256; | ||
90 | else | ||
91 | alg = COSE_ES256; | ||
92 | |||
93 | /* alg */ | ||
94 | if ((argv[1].key = cbor_build_uint8(3)) == NULL || | ||
95 | (argv[1].value = cbor_build_negint8(-alg - 1)) == NULL || | ||
96 | !cbor_map_add(item, argv[1])) | ||
97 | goto fail; | ||
98 | |||
99 | /* crv */ | ||
100 | if ((argv[2].key = cbor_build_negint8(0)) == NULL || | ||
101 | (argv[2].value = cbor_build_uint8(1)) == NULL || | ||
102 | !cbor_map_add(item, argv[2])) | ||
103 | goto fail; | ||
104 | |||
105 | /* x */ | ||
106 | if ((argv[3].key = cbor_build_negint8(1)) == NULL || | ||
107 | (argv[3].value = cbor_build_bytestring(pk->x, | ||
108 | sizeof(pk->x))) == NULL || !cbor_map_add(item, argv[3])) | ||
109 | goto fail; | ||
110 | |||
111 | /* y */ | ||
112 | if ((argv[4].key = cbor_build_negint8(2)) == NULL || | ||
113 | (argv[4].value = cbor_build_bytestring(pk->y, | ||
114 | sizeof(pk->y))) == NULL || !cbor_map_add(item, argv[4])) | ||
115 | goto fail; | ||
116 | |||
117 | ok = 0; | ||
118 | fail: | ||
119 | if (ok < 0) { | ||
120 | if (item != NULL) { | ||
121 | cbor_decref(&item); | ||
122 | item = NULL; | ||
123 | } | ||
124 | } | ||
125 | |||
126 | for (size_t i = 0; i < 5; i++) { | ||
127 | if (argv[i].key) | ||
128 | cbor_decref(&argv[i].key); | ||
129 | if (argv[i].value) | ||
130 | cbor_decref(&argv[i].value); | ||
131 | } | ||
132 | |||
133 | return (item); | ||
134 | } | ||
135 | |||
136 | es256_sk_t * | ||
137 | es256_sk_new(void) | ||
138 | { | ||
139 | return (calloc(1, sizeof(es256_sk_t))); | ||
140 | } | ||
141 | |||
142 | void | ||
143 | es256_sk_free(es256_sk_t **skp) | ||
144 | { | ||
145 | es256_sk_t *sk; | ||
146 | |||
147 | if (skp == NULL || (sk = *skp) == NULL) | ||
148 | return; | ||
149 | |||
150 | explicit_bzero(sk, sizeof(*sk)); | ||
151 | free(sk); | ||
152 | |||
153 | *skp = NULL; | ||
154 | } | ||
155 | |||
156 | es256_pk_t * | ||
157 | es256_pk_new(void) | ||
158 | { | ||
159 | return (calloc(1, sizeof(es256_pk_t))); | ||
160 | } | ||
161 | |||
162 | void | ||
163 | es256_pk_free(es256_pk_t **pkp) | ||
164 | { | ||
165 | es256_pk_t *pk; | ||
166 | |||
167 | if (pkp == NULL || (pk = *pkp) == NULL) | ||
168 | return; | ||
169 | |||
170 | explicit_bzero(pk, sizeof(*pk)); | ||
171 | free(pk); | ||
172 | |||
173 | *pkp = NULL; | ||
174 | } | ||
175 | |||
176 | int | ||
177 | es256_pk_from_ptr(es256_pk_t *pk, const void *ptr, size_t len) | ||
178 | { | ||
179 | if (len < sizeof(*pk)) | ||
180 | return (FIDO_ERR_INVALID_ARGUMENT); | ||
181 | |||
182 | memcpy(pk, ptr, sizeof(*pk)); | ||
183 | |||
184 | return (FIDO_OK); | ||
185 | } | ||
186 | |||
187 | int | ||
188 | es256_pk_set_x(es256_pk_t *pk, const unsigned char *x) | ||
189 | { | ||
190 | memcpy(pk->x, x, sizeof(pk->x)); | ||
191 | |||
192 | return (0); | ||
193 | } | ||
194 | |||
195 | int | ||
196 | es256_pk_set_y(es256_pk_t *pk, const unsigned char *y) | ||
197 | { | ||
198 | memcpy(pk->y, y, sizeof(pk->y)); | ||
199 | |||
200 | return (0); | ||
201 | } | ||
202 | |||
203 | int | ||
204 | es256_sk_create(es256_sk_t *key) | ||
205 | { | ||
206 | EVP_PKEY_CTX *pctx = NULL; | ||
207 | EVP_PKEY_CTX *kctx = NULL; | ||
208 | EVP_PKEY *p = NULL; | ||
209 | EVP_PKEY *k = NULL; | ||
210 | const EC_KEY *ec; | ||
211 | const BIGNUM *d; | ||
212 | const int nid = NID_X9_62_prime256v1; | ||
213 | int n; | ||
214 | int ok = -1; | ||
215 | |||
216 | if ((pctx = EVP_PKEY_CTX_new_id(EVP_PKEY_EC, NULL)) == NULL || | ||
217 | EVP_PKEY_paramgen_init(pctx) <= 0 || | ||
218 | EVP_PKEY_CTX_set_ec_paramgen_curve_nid(pctx, nid) <= 0 || | ||
219 | EVP_PKEY_paramgen(pctx, &p) <= 0) { | ||
220 | fido_log_debug("%s: EVP_PKEY_paramgen", __func__); | ||
221 | goto fail; | ||
222 | } | ||
223 | |||
224 | if ((kctx = EVP_PKEY_CTX_new(p, NULL)) == NULL || | ||
225 | EVP_PKEY_keygen_init(kctx) <= 0 || EVP_PKEY_keygen(kctx, &k) <= 0) { | ||
226 | fido_log_debug("%s: EVP_PKEY_keygen", __func__); | ||
227 | goto fail; | ||
228 | } | ||
229 | |||
230 | if ((ec = EVP_PKEY_get0_EC_KEY(k)) == NULL || | ||
231 | (d = EC_KEY_get0_private_key(ec)) == NULL || | ||
232 | (n = BN_num_bytes(d)) < 0 || (size_t)n > sizeof(key->d) || | ||
233 | (n = BN_bn2bin(d, key->d)) < 0 || (size_t)n > sizeof(key->d)) { | ||
234 | fido_log_debug("%s: EC_KEY_get0_private_key", __func__); | ||
235 | goto fail; | ||
236 | } | ||
237 | |||
238 | ok = 0; | ||
239 | fail: | ||
240 | if (p != NULL) | ||
241 | EVP_PKEY_free(p); | ||
242 | if (k != NULL) | ||
243 | EVP_PKEY_free(k); | ||
244 | if (pctx != NULL) | ||
245 | EVP_PKEY_CTX_free(pctx); | ||
246 | if (kctx != NULL) | ||
247 | EVP_PKEY_CTX_free(kctx); | ||
248 | |||
249 | return (ok); | ||
250 | } | ||
251 | |||
252 | EVP_PKEY * | ||
253 | es256_pk_to_EVP_PKEY(const es256_pk_t *k) | ||
254 | { | ||
255 | BN_CTX *bnctx = NULL; | ||
256 | EC_KEY *ec = NULL; | ||
257 | EC_POINT *q = NULL; | ||
258 | EVP_PKEY *pkey = NULL; | ||
259 | BIGNUM *x = NULL; | ||
260 | BIGNUM *y = NULL; | ||
261 | const EC_GROUP *g = NULL; | ||
262 | const int nid = NID_X9_62_prime256v1; | ||
263 | int ok = -1; | ||
264 | |||
265 | if ((bnctx = BN_CTX_new()) == NULL || | ||
266 | (x = BN_CTX_get(bnctx)) == NULL || | ||
267 | (y = BN_CTX_get(bnctx)) == NULL) | ||
268 | goto fail; | ||
269 | |||
270 | if (BN_bin2bn(k->x, sizeof(k->x), x) == NULL || | ||
271 | BN_bin2bn(k->y, sizeof(k->y), y) == NULL) { | ||
272 | fido_log_debug("%s: BN_bin2bn", __func__); | ||
273 | goto fail; | ||
274 | } | ||
275 | |||
276 | if ((ec = EC_KEY_new_by_curve_name(nid)) == NULL || | ||
277 | (g = EC_KEY_get0_group(ec)) == NULL) { | ||
278 | fido_log_debug("%s: EC_KEY init", __func__); | ||
279 | goto fail; | ||
280 | } | ||
281 | |||
282 | if ((q = EC_POINT_new(g)) == NULL || | ||
283 | EC_POINT_set_affine_coordinates_GFp(g, q, x, y, bnctx) == 0 || | ||
284 | EC_KEY_set_public_key(ec, q) == 0) { | ||
285 | fido_log_debug("%s: EC_KEY_set_public_key", __func__); | ||
286 | goto fail; | ||
287 | } | ||
288 | |||
289 | if ((pkey = EVP_PKEY_new()) == NULL || | ||
290 | EVP_PKEY_assign_EC_KEY(pkey, ec) == 0) { | ||
291 | fido_log_debug("%s: EVP_PKEY_assign_EC_KEY", __func__); | ||
292 | goto fail; | ||
293 | } | ||
294 | |||
295 | ec = NULL; /* at this point, ec belongs to evp */ | ||
296 | |||
297 | ok = 0; | ||
298 | fail: | ||
299 | if (bnctx != NULL) | ||
300 | BN_CTX_free(bnctx); | ||
301 | if (ec != NULL) | ||
302 | EC_KEY_free(ec); | ||
303 | if (q != NULL) | ||
304 | EC_POINT_free(q); | ||
305 | if (ok < 0 && pkey != NULL) { | ||
306 | EVP_PKEY_free(pkey); | ||
307 | pkey = NULL; | ||
308 | } | ||
309 | |||
310 | return (pkey); | ||
311 | } | ||
312 | |||
313 | int | ||
314 | es256_pk_from_EC_KEY(es256_pk_t *pk, const EC_KEY *ec) | ||
315 | { | ||
316 | BN_CTX *ctx = NULL; | ||
317 | BIGNUM *x = NULL; | ||
318 | BIGNUM *y = NULL; | ||
319 | const EC_POINT *q = NULL; | ||
320 | const EC_GROUP *g = NULL; | ||
321 | int ok = FIDO_ERR_INTERNAL; | ||
322 | int n; | ||
323 | |||
324 | if ((q = EC_KEY_get0_public_key(ec)) == NULL || | ||
325 | (g = EC_KEY_get0_group(ec)) == NULL) | ||
326 | goto fail; | ||
327 | |||
328 | if ((ctx = BN_CTX_new()) == NULL || | ||
329 | (x = BN_CTX_get(ctx)) == NULL || | ||
330 | (y = BN_CTX_get(ctx)) == NULL) | ||
331 | goto fail; | ||
332 | |||
333 | if (EC_POINT_get_affine_coordinates_GFp(g, q, x, y, ctx) == 0 || | ||
334 | (n = BN_num_bytes(x)) < 0 || (size_t)n > sizeof(pk->x) || | ||
335 | (n = BN_num_bytes(y)) < 0 || (size_t)n > sizeof(pk->y)) { | ||
336 | fido_log_debug("%s: EC_POINT_get_affine_coordinates_GFp", | ||
337 | __func__); | ||
338 | goto fail; | ||
339 | } | ||
340 | |||
341 | if ((n = BN_bn2bin(x, pk->x)) < 0 || (size_t)n > sizeof(pk->x) || | ||
342 | (n = BN_bn2bin(y, pk->y)) < 0 || (size_t)n > sizeof(pk->y)) { | ||
343 | fido_log_debug("%s: BN_bn2bin", __func__); | ||
344 | goto fail; | ||
345 | } | ||
346 | |||
347 | ok = FIDO_OK; | ||
348 | fail: | ||
349 | if (ctx != NULL) | ||
350 | BN_CTX_free(ctx); | ||
351 | |||
352 | return (ok); | ||
353 | } | ||
354 | |||
355 | EVP_PKEY * | ||
356 | es256_sk_to_EVP_PKEY(const es256_sk_t *k) | ||
357 | { | ||
358 | BN_CTX *bnctx = NULL; | ||
359 | EC_KEY *ec = NULL; | ||
360 | EVP_PKEY *pkey = NULL; | ||
361 | BIGNUM *d = NULL; | ||
362 | const int nid = NID_X9_62_prime256v1; | ||
363 | int ok = -1; | ||
364 | |||
365 | if ((bnctx = BN_CTX_new()) == NULL || (d = BN_CTX_get(bnctx)) == NULL || | ||
366 | BN_bin2bn(k->d, sizeof(k->d), d) == NULL) { | ||
367 | fido_log_debug("%s: BN_bin2bn", __func__); | ||
368 | goto fail; | ||
369 | } | ||
370 | |||
371 | if ((ec = EC_KEY_new_by_curve_name(nid)) == NULL || | ||
372 | EC_KEY_set_private_key(ec, d) == 0) { | ||
373 | fido_log_debug("%s: EC_KEY_set_private_key", __func__); | ||
374 | goto fail; | ||
375 | } | ||
376 | |||
377 | if ((pkey = EVP_PKEY_new()) == NULL || | ||
378 | EVP_PKEY_assign_EC_KEY(pkey, ec) == 0) { | ||
379 | fido_log_debug("%s: EVP_PKEY_assign_EC_KEY", __func__); | ||
380 | goto fail; | ||
381 | } | ||
382 | |||
383 | ec = NULL; /* at this point, ec belongs to evp */ | ||
384 | |||
385 | ok = 0; | ||
386 | fail: | ||
387 | if (bnctx != NULL) | ||
388 | BN_CTX_free(bnctx); | ||
389 | if (ec != NULL) | ||
390 | EC_KEY_free(ec); | ||
391 | if (ok < 0 && pkey != NULL) { | ||
392 | EVP_PKEY_free(pkey); | ||
393 | pkey = NULL; | ||
394 | } | ||
395 | |||
396 | return (pkey); | ||
397 | } | ||
398 | |||
399 | int | ||
400 | es256_derive_pk(const es256_sk_t *sk, es256_pk_t *pk) | ||
401 | { | ||
402 | BIGNUM *d = NULL; | ||
403 | EC_KEY *ec = NULL; | ||
404 | EC_POINT *q = NULL; | ||
405 | const EC_GROUP *g = NULL; | ||
406 | const int nid = NID_X9_62_prime256v1; | ||
407 | int ok = -1; | ||
408 | |||
409 | if ((d = BN_bin2bn(sk->d, (int)sizeof(sk->d), NULL)) == NULL || | ||
410 | (ec = EC_KEY_new_by_curve_name(nid)) == NULL || | ||
411 | (g = EC_KEY_get0_group(ec)) == NULL || | ||
412 | (q = EC_POINT_new(g)) == NULL) { | ||
413 | fido_log_debug("%s: get", __func__); | ||
414 | goto fail; | ||
415 | } | ||
416 | |||
417 | if (EC_POINT_mul(g, q, d, NULL, NULL, NULL) == 0 || | ||
418 | EC_KEY_set_public_key(ec, q) == 0 || | ||
419 | es256_pk_from_EC_KEY(pk, ec) != FIDO_OK) { | ||
420 | fido_log_debug("%s: set", __func__); | ||
421 | goto fail; | ||
422 | } | ||
423 | |||
424 | ok = 0; | ||
425 | fail: | ||
426 | if (d != NULL) | ||
427 | BN_clear_free(d); | ||
428 | if (q != NULL) | ||
429 | EC_POINT_free(q); | ||
430 | if (ec != NULL) | ||
431 | EC_KEY_free(ec); | ||
432 | |||
433 | return (ok); | ||
434 | } | ||