]>
Commit | Line | Data |
---|---|---|
756b198d DSH |
1 | /* |
2 | * Copyright 2006-2016 The OpenSSL Project Authors. All Rights Reserved. | |
3 | * | |
4 | * Licensed under the OpenSSL license (the "License"). You may not use | |
5 | * this file except in compliance with the License. You can obtain a copy | |
6 | * in the file LICENSE in the source distribution or at | |
7 | * https://www.openssl.org/source/license.html | |
8 | */ | |
9 | ||
10 | #include <stdio.h> | |
11 | #include "internal/cryptlib.h" | |
12 | #include <openssl/x509.h> | |
13 | #include <openssl/ec.h> | |
14 | #include <openssl/rand.h> | |
15 | #include "internal/asn1_int.h" | |
16 | #include "internal/evp_int.h" | |
17 | #include "ec_lcl.h" | |
18 | ||
19 | #define X25519_KEYLEN 32 | |
20 | #define X25519_BITS 253 | |
21 | #define X25519_SECURITY_BITS 128 | |
22 | ||
8ecade8b DSH |
23 | #define ED25519_SIGSIZE 64 |
24 | ||
756b198d DSH |
25 | typedef struct { |
26 | unsigned char pubkey[X25519_KEYLEN]; | |
27 | unsigned char *privkey; | |
28 | } X25519_KEY; | |
29 | ||
30 | typedef enum { | |
31 | X25519_PUBLIC, | |
32 | X25519_PRIVATE, | |
33 | X25519_KEYGEN | |
34 | } ecx_key_op_t; | |
35 | ||
36 | /* Setup EVP_PKEY using public, private or generation */ | |
42a3008a | 37 | static int ecx_key_op(EVP_PKEY *pkey, int id, const X509_ALGOR *palg, |
756b198d DSH |
38 | const unsigned char *p, int plen, ecx_key_op_t op) |
39 | { | |
5d6aaf8a | 40 | X25519_KEY *xkey; |
756b198d DSH |
41 | |
42 | if (op != X25519_KEYGEN) { | |
5d6aaf8a DSH |
43 | if (palg != NULL) { |
44 | int ptype; | |
45 | ||
46 | /* Algorithm parameters must be absent */ | |
47 | X509_ALGOR_get0(NULL, &ptype, NULL, palg); | |
48 | if (ptype != V_ASN1_UNDEF) { | |
49 | ECerr(EC_F_ECX_KEY_OP, EC_R_INVALID_ENCODING); | |
50 | return 0; | |
51 | } | |
756b198d DSH |
52 | } |
53 | ||
54 | if (p == NULL || plen != X25519_KEYLEN) { | |
55 | ECerr(EC_F_ECX_KEY_OP, EC_R_INVALID_ENCODING); | |
56 | return 0; | |
57 | } | |
58 | } | |
59 | ||
60 | xkey = OPENSSL_zalloc(sizeof(*xkey)); | |
61 | if (xkey == NULL) { | |
62 | ECerr(EC_F_ECX_KEY_OP, ERR_R_MALLOC_FAILURE); | |
63 | return 0; | |
64 | } | |
65 | ||
66 | if (op == X25519_PUBLIC) { | |
67 | memcpy(xkey->pubkey, p, plen); | |
68 | } else { | |
69 | xkey->privkey = OPENSSL_secure_malloc(X25519_KEYLEN); | |
70 | if (xkey->privkey == NULL) { | |
71 | ECerr(EC_F_ECX_KEY_OP, ERR_R_MALLOC_FAILURE); | |
72 | OPENSSL_free(xkey); | |
73 | return 0; | |
74 | } | |
75 | if (op == X25519_KEYGEN) { | |
76 | if (RAND_bytes(xkey->privkey, X25519_KEYLEN) <= 0) { | |
77 | OPENSSL_secure_free(xkey->privkey); | |
78 | OPENSSL_free(xkey); | |
79 | return 0; | |
80 | } | |
42a3008a | 81 | if (id == NID_X25519) { |
8ecade8b DSH |
82 | xkey->privkey[0] &= 248; |
83 | xkey->privkey[31] &= 127; | |
84 | xkey->privkey[31] |= 64; | |
85 | } | |
756b198d DSH |
86 | } else { |
87 | memcpy(xkey->privkey, p, X25519_KEYLEN); | |
88 | } | |
42a3008a | 89 | if (id == NID_X25519) |
8ecade8b DSH |
90 | X25519_public_from_private(xkey->pubkey, xkey->privkey); |
91 | else | |
92 | ED25519_public_from_private(xkey->pubkey, xkey->privkey); | |
756b198d DSH |
93 | } |
94 | ||
42a3008a | 95 | EVP_PKEY_assign(pkey, id, xkey); |
756b198d DSH |
96 | return 1; |
97 | } | |
98 | ||
99 | static int ecx_pub_encode(X509_PUBKEY *pk, const EVP_PKEY *pkey) | |
100 | { | |
101 | const X25519_KEY *xkey = pkey->pkey.ptr; | |
102 | unsigned char *penc; | |
103 | ||
104 | if (xkey == NULL) { | |
105 | ECerr(EC_F_ECX_PUB_ENCODE, EC_R_INVALID_KEY); | |
106 | return 0; | |
107 | } | |
108 | ||
109 | penc = OPENSSL_memdup(xkey->pubkey, X25519_KEYLEN); | |
110 | if (penc == NULL) { | |
111 | ECerr(EC_F_ECX_PUB_ENCODE, ERR_R_MALLOC_FAILURE); | |
112 | return 0; | |
113 | } | |
114 | ||
8ecade8b DSH |
115 | if (!X509_PUBKEY_set0_param(pk, OBJ_nid2obj(pkey->ameth->pkey_id), |
116 | V_ASN1_UNDEF, NULL, penc, X25519_KEYLEN)) { | |
756b198d DSH |
117 | OPENSSL_free(penc); |
118 | ECerr(EC_F_ECX_PUB_ENCODE, ERR_R_MALLOC_FAILURE); | |
119 | return 0; | |
120 | } | |
121 | return 1; | |
122 | } | |
123 | ||
124 | static int ecx_pub_decode(EVP_PKEY *pkey, X509_PUBKEY *pubkey) | |
125 | { | |
126 | const unsigned char *p; | |
127 | int pklen; | |
128 | X509_ALGOR *palg; | |
129 | ||
130 | if (!X509_PUBKEY_get0_param(NULL, &p, &pklen, &palg, pubkey)) | |
131 | return 0; | |
42a3008a DSH |
132 | return ecx_key_op(pkey, pkey->ameth->pkey_id, palg, p, pklen, |
133 | X25519_PUBLIC); | |
756b198d DSH |
134 | } |
135 | ||
136 | static int ecx_pub_cmp(const EVP_PKEY *a, const EVP_PKEY *b) | |
137 | { | |
138 | const X25519_KEY *akey = a->pkey.ptr; | |
139 | const X25519_KEY *bkey = b->pkey.ptr; | |
140 | ||
141 | if (akey == NULL || bkey == NULL) | |
142 | return -2; | |
143 | return !CRYPTO_memcmp(akey->pubkey, bkey->pubkey, X25519_KEYLEN); | |
144 | } | |
145 | ||
245c6bc3 | 146 | static int ecx_priv_decode(EVP_PKEY *pkey, const PKCS8_PRIV_KEY_INFO *p8) |
756b198d DSH |
147 | { |
148 | const unsigned char *p; | |
149 | int plen; | |
150 | ASN1_OCTET_STRING *oct = NULL; | |
245c6bc3 | 151 | const X509_ALGOR *palg; |
756b198d DSH |
152 | int rv; |
153 | ||
154 | if (!PKCS8_pkey_get0(NULL, &p, &plen, &palg, p8)) | |
155 | return 0; | |
156 | ||
157 | oct = d2i_ASN1_OCTET_STRING(NULL, &p, plen); | |
158 | if (oct == NULL) { | |
159 | p = NULL; | |
160 | plen = 0; | |
161 | } else { | |
17ebf85a | 162 | p = ASN1_STRING_get0_data(oct); |
756b198d DSH |
163 | plen = ASN1_STRING_length(oct); |
164 | } | |
165 | ||
42a3008a | 166 | rv = ecx_key_op(pkey, pkey->ameth->pkey_id, palg, p, plen, X25519_PRIVATE); |
756b198d DSH |
167 | ASN1_OCTET_STRING_free(oct); |
168 | return rv; | |
169 | } | |
170 | ||
171 | static int ecx_priv_encode(PKCS8_PRIV_KEY_INFO *p8, const EVP_PKEY *pkey) | |
172 | { | |
173 | const X25519_KEY *xkey = pkey->pkey.ptr; | |
174 | ASN1_OCTET_STRING oct; | |
175 | unsigned char *penc = NULL; | |
176 | int penclen; | |
177 | ||
178 | if (xkey == NULL || xkey->privkey == NULL) { | |
179 | ECerr(EC_F_ECX_PRIV_ENCODE, EC_R_INVALID_PRIVATE_KEY); | |
180 | return 0; | |
181 | } | |
182 | ||
183 | oct.data = xkey->privkey; | |
184 | oct.length = X25519_KEYLEN; | |
185 | oct.flags = 0; | |
186 | ||
187 | penclen = i2d_ASN1_OCTET_STRING(&oct, &penc); | |
188 | if (penclen < 0) { | |
189 | ECerr(EC_F_ECX_PRIV_ENCODE, ERR_R_MALLOC_FAILURE); | |
190 | return 0; | |
191 | } | |
192 | ||
8ecade8b | 193 | if (!PKCS8_pkey_set0(p8, OBJ_nid2obj(pkey->ameth->pkey_id), 0, |
756b198d DSH |
194 | V_ASN1_UNDEF, NULL, penc, penclen)) { |
195 | OPENSSL_clear_free(penc, penclen); | |
196 | ECerr(EC_F_ECX_PRIV_ENCODE, ERR_R_MALLOC_FAILURE); | |
197 | return 0; | |
198 | } | |
199 | ||
200 | return 1; | |
201 | } | |
202 | ||
203 | static int ecx_size(const EVP_PKEY *pkey) | |
204 | { | |
205 | return X25519_KEYLEN; | |
206 | } | |
207 | ||
208 | static int ecx_bits(const EVP_PKEY *pkey) | |
209 | { | |
210 | return X25519_BITS; | |
211 | } | |
212 | ||
213 | static int ecx_security_bits(const EVP_PKEY *pkey) | |
214 | { | |
215 | return X25519_SECURITY_BITS; | |
216 | } | |
217 | ||
218 | static void ecx_free(EVP_PKEY *pkey) | |
219 | { | |
220 | X25519_KEY *xkey = pkey->pkey.ptr; | |
221 | ||
222 | if (xkey) | |
223 | OPENSSL_secure_free(xkey->privkey); | |
224 | OPENSSL_free(xkey); | |
225 | } | |
226 | ||
227 | /* "parameters" are always equal */ | |
228 | static int ecx_cmp_parameters(const EVP_PKEY *a, const EVP_PKEY *b) | |
229 | { | |
230 | return 1; | |
231 | } | |
232 | ||
233 | static int ecx_key_print(BIO *bp, const EVP_PKEY *pkey, int indent, | |
234 | ASN1_PCTX *ctx, ecx_key_op_t op) | |
235 | { | |
236 | const X25519_KEY *xkey = pkey->pkey.ptr; | |
237 | ||
8ecade8b DSH |
238 | const char *nm = OBJ_nid2ln(pkey->ameth->pkey_id); |
239 | ||
756b198d DSH |
240 | if (op == X25519_PRIVATE) { |
241 | if (xkey == NULL || xkey->privkey == NULL) { | |
242 | if (BIO_printf(bp, "%*s<INVALID PRIVATE KEY>\n", indent, "") <= 0) | |
243 | return 0; | |
244 | return 1; | |
245 | } | |
8ecade8b | 246 | if (BIO_printf(bp, "%*s%s Private-Key:\n", indent, "", nm) <= 0) |
756b198d DSH |
247 | return 0; |
248 | if (BIO_printf(bp, "%*spriv:\n", indent, "") <= 0) | |
249 | return 0; | |
250 | if (ASN1_buf_print(bp, xkey->privkey, X25519_KEYLEN, indent + 4) == 0) | |
251 | return 0; | |
252 | } else { | |
253 | if (xkey == NULL) { | |
254 | if (BIO_printf(bp, "%*s<INVALID PUBLIC KEY>\n", indent, "") <= 0) | |
255 | return 0; | |
256 | return 1; | |
257 | } | |
8ecade8b | 258 | if (BIO_printf(bp, "%*s%s Public-Key:\n", indent, "", nm) <= 0) |
756b198d DSH |
259 | return 0; |
260 | } | |
261 | if (BIO_printf(bp, "%*spub:\n", indent, "") <= 0) | |
262 | return 0; | |
263 | if (ASN1_buf_print(bp, xkey->pubkey, X25519_KEYLEN, indent + 4) == 0) | |
264 | return 0; | |
265 | return 1; | |
266 | } | |
267 | ||
268 | static int ecx_priv_print(BIO *bp, const EVP_PKEY *pkey, int indent, | |
269 | ASN1_PCTX *ctx) | |
270 | { | |
271 | return ecx_key_print(bp, pkey, indent, ctx, X25519_PRIVATE); | |
272 | } | |
273 | ||
274 | static int ecx_pub_print(BIO *bp, const EVP_PKEY *pkey, int indent, | |
275 | ASN1_PCTX *ctx) | |
276 | { | |
277 | return ecx_key_print(bp, pkey, indent, ctx, X25519_PUBLIC); | |
278 | } | |
279 | ||
280 | static int ecx_ctrl(EVP_PKEY *pkey, int op, long arg1, void *arg2) | |
281 | { | |
5d6aaf8a DSH |
282 | switch (op) { |
283 | ||
284 | case ASN1_PKEY_CTRL_SET1_TLS_ENCPT: | |
42a3008a | 285 | return ecx_key_op(pkey, NID_X25519, NULL, arg2, arg1, X25519_PUBLIC); |
5d6aaf8a DSH |
286 | |
287 | case ASN1_PKEY_CTRL_GET1_TLS_ENCPT: | |
288 | if (pkey->pkey.ptr != NULL) { | |
289 | const X25519_KEY *xkey = pkey->pkey.ptr; | |
290 | unsigned char **ppt = arg2; | |
291 | *ppt = OPENSSL_memdup(xkey->pubkey, X25519_KEYLEN); | |
292 | if (*ppt != NULL) | |
293 | return X25519_KEYLEN; | |
294 | } | |
295 | return 0; | |
296 | ||
297 | case ASN1_PKEY_CTRL_DEFAULT_MD_NID: | |
298 | *(int *)arg2 = NID_sha256; | |
299 | return 2; | |
300 | ||
301 | default: | |
302 | return -2; | |
303 | ||
304 | } | |
756b198d DSH |
305 | } |
306 | ||
307 | const EVP_PKEY_ASN1_METHOD ecx25519_asn1_meth = { | |
308 | NID_X25519, | |
309 | NID_X25519, | |
310 | 0, | |
311 | "X25519", | |
312 | "OpenSSL X25519 algorithm", | |
313 | ||
314 | ecx_pub_decode, | |
315 | ecx_pub_encode, | |
316 | ecx_pub_cmp, | |
317 | ecx_pub_print, | |
318 | ||
319 | ecx_priv_decode, | |
320 | ecx_priv_encode, | |
321 | ecx_priv_print, | |
322 | ||
323 | ecx_size, | |
324 | ecx_bits, | |
325 | ecx_security_bits, | |
326 | ||
327 | 0, 0, 0, 0, | |
328 | ecx_cmp_parameters, | |
329 | 0, 0, | |
330 | ||
331 | ecx_free, | |
332 | ecx_ctrl, | |
333 | NULL, | |
334 | NULL | |
335 | }; | |
336 | ||
8ecade8b DSH |
337 | static int ecd_size(const EVP_PKEY *pkey) |
338 | { | |
339 | return ED25519_SIGSIZE; | |
340 | } | |
341 | ||
9f98fbad DSH |
342 | static int ecd_item_verify(EVP_MD_CTX *ctx, const ASN1_ITEM *it, void *asn, |
343 | X509_ALGOR *sigalg, ASN1_BIT_STRING *str, | |
344 | EVP_PKEY *pkey) | |
345 | { | |
346 | const ASN1_OBJECT *obj; | |
347 | int ptype; | |
348 | ||
349 | X509_ALGOR_get0(&obj, &ptype, NULL, sigalg); | |
350 | /* Sanity check: make sure it is ED25519 with absent parameters */ | |
351 | if (OBJ_obj2nid(obj) != NID_ED25519 || ptype != V_ASN1_UNDEF) { | |
352 | ECerr(EC_F_ECD_ITEM_VERIFY, EC_R_INVALID_ENCODING); | |
353 | return 0; | |
354 | } | |
355 | ||
356 | if (!EVP_DigestVerifyInit(ctx, NULL, NULL, NULL, pkey)) | |
357 | return 0; | |
358 | ||
359 | return 2; | |
360 | } | |
361 | ||
362 | static int ecd_item_sign(EVP_MD_CTX *ctx, const ASN1_ITEM *it, void *asn, | |
363 | X509_ALGOR *alg1, X509_ALGOR *alg2, | |
364 | ASN1_BIT_STRING *str) | |
365 | { | |
366 | /* Set algorithms identifiers */ | |
367 | X509_ALGOR_set0(alg1, OBJ_nid2obj(NID_ED25519), V_ASN1_UNDEF, NULL); | |
368 | if (alg2) | |
369 | X509_ALGOR_set0(alg2, OBJ_nid2obj(NID_ED25519), V_ASN1_UNDEF, NULL); | |
370 | /* Algorithm idetifiers set: carry on as normal */ | |
371 | return 3; | |
372 | } | |
373 | ||
684c41c8 DSH |
374 | static int ecd_sig_info_set(X509_SIG_INFO *siginf, const X509_ALGOR *alg, |
375 | const ASN1_STRING *sig) | |
376 | { | |
377 | X509_SIG_INFO_set(siginf, NID_undef, NID_ED25519, X25519_SECURITY_BITS, | |
378 | X509_SIG_INFO_TLS); | |
379 | return 1; | |
380 | } | |
381 | ||
8ecade8b DSH |
382 | const EVP_PKEY_ASN1_METHOD ed25519_asn1_meth = { |
383 | NID_ED25519, | |
384 | NID_ED25519, | |
385 | 0, | |
386 | "ED25519", | |
387 | "OpenSSL ED25519 algorithm", | |
388 | ||
389 | ecx_pub_decode, | |
390 | ecx_pub_encode, | |
391 | ecx_pub_cmp, | |
392 | ecx_pub_print, | |
393 | ||
394 | ecx_priv_decode, | |
395 | ecx_priv_encode, | |
396 | ecx_priv_print, | |
397 | ||
398 | ecd_size, | |
399 | ecx_bits, | |
400 | ecx_security_bits, | |
401 | ||
402 | 0, 0, 0, 0, | |
403 | ecx_cmp_parameters, | |
404 | 0, 0, | |
405 | ||
406 | ecx_free, | |
407 | 0, | |
408 | NULL, | |
9f98fbad DSH |
409 | NULL, |
410 | ecd_item_verify, | |
684c41c8 DSH |
411 | ecd_item_sign, |
412 | ecd_sig_info_set | |
8ecade8b DSH |
413 | }; |
414 | ||
756b198d DSH |
415 | static int pkey_ecx_keygen(EVP_PKEY_CTX *ctx, EVP_PKEY *pkey) |
416 | { | |
42a3008a | 417 | return ecx_key_op(pkey, ctx->pmeth->pkey_id, NULL, NULL, 0, X25519_KEYGEN); |
756b198d DSH |
418 | } |
419 | ||
420 | static int pkey_ecx_derive(EVP_PKEY_CTX *ctx, unsigned char *key, | |
421 | size_t *keylen) | |
422 | { | |
423 | const X25519_KEY *pkey, *peerkey; | |
424 | ||
425 | if (ctx->pkey == NULL || ctx->peerkey == NULL) { | |
426 | ECerr(EC_F_PKEY_ECX_DERIVE, EC_R_KEYS_NOT_SET); | |
427 | return 0; | |
428 | } | |
429 | pkey = ctx->pkey->pkey.ptr; | |
430 | peerkey = ctx->peerkey->pkey.ptr; | |
431 | if (pkey == NULL || pkey->privkey == NULL) { | |
432 | ECerr(EC_F_PKEY_ECX_DERIVE, EC_R_INVALID_PRIVATE_KEY); | |
433 | return 0; | |
434 | } | |
435 | if (peerkey == NULL) { | |
436 | ECerr(EC_F_PKEY_ECX_DERIVE, EC_R_INVALID_PEER_KEY); | |
437 | return 0; | |
438 | } | |
439 | *keylen = X25519_KEYLEN; | |
440 | if (key != NULL && X25519(key, pkey->privkey, peerkey->pubkey) == 0) | |
441 | return 0; | |
442 | return 1; | |
443 | } | |
444 | ||
445 | static int pkey_ecx_ctrl(EVP_PKEY_CTX *ctx, int type, int p1, void *p2) | |
446 | { | |
447 | /* Only need to handle peer key for derivation */ | |
448 | if (type == EVP_PKEY_CTRL_PEER_KEY) | |
449 | return 1; | |
450 | return -2; | |
451 | } | |
452 | ||
453 | const EVP_PKEY_METHOD ecx25519_pkey_meth = { | |
454 | NID_X25519, | |
455 | 0, 0, 0, 0, 0, 0, 0, | |
456 | pkey_ecx_keygen, | |
457 | 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, | |
458 | pkey_ecx_derive, | |
459 | pkey_ecx_ctrl, | |
460 | 0 | |
461 | }; | |
42a3008a DSH |
462 | |
463 | static int pkey_ecd_digestsign(EVP_MD_CTX *ctx, unsigned char *sig, | |
464 | size_t *siglen, const unsigned char *tbs, | |
465 | size_t tbslen) | |
466 | { | |
467 | const X25519_KEY *edkey = EVP_MD_CTX_pkey_ctx(ctx)->pkey->pkey.ptr; | |
468 | ||
469 | if (sig == NULL) { | |
470 | *siglen = ED25519_SIGSIZE; | |
471 | return 1; | |
472 | } | |
473 | if (*siglen < ED25519_SIGSIZE) { | |
474 | ECerr(EC_F_PKEY_ECD_DIGESTSIGN, EC_R_BUFFER_TOO_SMALL); | |
475 | return 0; | |
476 | } | |
477 | ||
478 | if (ED25519_sign(sig, tbs, tbslen, edkey->pubkey, edkey->privkey) == 0) | |
479 | return 0; | |
480 | *siglen = ED25519_SIGSIZE; | |
481 | return 1; | |
482 | } | |
483 | ||
484 | static int pkey_ecd_digestverify(EVP_MD_CTX *ctx, const unsigned char *sig, | |
485 | size_t siglen, const unsigned char *tbs, | |
486 | size_t tbslen) | |
487 | { | |
488 | const X25519_KEY *edkey = EVP_MD_CTX_pkey_ctx(ctx)->pkey->pkey.ptr; | |
489 | ||
490 | if (siglen != ED25519_SIGSIZE) | |
491 | return 0; | |
492 | ||
493 | return ED25519_verify(tbs, tbslen, sig, edkey->pubkey); | |
494 | } | |
495 | ||
496 | static int pkey_ecd_ctrl(EVP_PKEY_CTX *ctx, int type, int p1, void *p2) | |
497 | { | |
498 | switch (type) { | |
499 | case EVP_PKEY_CTRL_MD: | |
500 | /* Only NULL allowed as digest */ | |
501 | if (p2 == NULL) | |
502 | return 1; | |
503 | ECerr(EC_F_PKEY_ECD_CTRL, EC_R_INVALID_DIGEST_TYPE); | |
504 | return 0; | |
505 | ||
506 | case EVP_PKEY_CTRL_DIGESTINIT: | |
507 | return 1; | |
508 | } | |
509 | return -2; | |
510 | } | |
511 | ||
512 | const EVP_PKEY_METHOD ed25519_pkey_meth = { | |
513 | NID_ED25519, EVP_PKEY_FLAG_SIGCTX_CUSTOM, | |
514 | 0, 0, 0, 0, 0, 0, | |
515 | pkey_ecx_keygen, | |
516 | 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, | |
517 | pkey_ecd_ctrl, | |
518 | 0, | |
519 | pkey_ecd_digestsign, | |
520 | pkey_ecd_digestverify | |
521 | }; |