]>
Commit | Line | Data |
---|---|---|
35bc60cc | 1 | /* |
20f74adb | 2 | * Copyright (C) 2018 Tobias Brunner |
35bc60cc AS |
3 | * Copyright (C) 2016 Andreas Steffen |
4 | * HSR Hochschule fuer Technik Rapperswil | |
5 | * | |
6 | * This program is free software; you can redistribute it and/or modify it | |
7 | * under the terms of the GNU General Public License as published by the | |
8 | * Free Software Foundation; either version 2 of the License, or (at your | |
9 | * option) any later version. See <http://www.fsf.org/copyleft/gpl.txt>. | |
10 | * | |
11 | * This program is distributed in the hope that it will be useful, but | |
12 | * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY | |
13 | * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License | |
14 | * for more details. | |
15 | */ | |
16 | ||
17 | #include "curve25519_public_key.h" | |
d47ad3d6 | 18 | #include "ref10/ref10.h" |
35bc60cc AS |
19 | |
20 | #include <asn1/asn1.h> | |
21 | #include <asn1/asn1_parser.h> | |
22 | #include <asn1/oid.h> | |
23 | ||
24 | typedef struct private_curve25519_public_key_t private_curve25519_public_key_t; | |
25 | ||
26 | /** | |
27 | * Private data structure with signing context. | |
28 | */ | |
29 | struct private_curve25519_public_key_t { | |
30 | /** | |
31 | * Public interface for this signer. | |
32 | */ | |
33 | curve25519_public_key_t public; | |
34 | ||
35 | /** | |
36 | * Ed25519 public key | |
37 | */ | |
38 | chunk_t pubkey; | |
39 | ||
40 | /** | |
41 | * Reference counter | |
42 | */ | |
43 | refcount_t ref; | |
44 | }; | |
45 | ||
46 | METHOD(public_key_t, get_type, key_type_t, | |
47 | private_curve25519_public_key_t *this) | |
48 | { | |
49 | return KEY_ED25519; | |
50 | } | |
51 | ||
2571898d TB |
52 | /* L = 2^252+27742317777372353535851937790883648493 in little-endian form */ |
53 | static chunk_t curve25519_order = chunk_from_chars( | |
54 | 0xed, 0xd3, 0xf5, 0x5c, 0x1a, 0x63, 0x12, 0x58, | |
55 | 0xd6, 0x9c, 0xf7, 0xa2, 0xde, 0xf9, 0xde, 0x14, | |
56 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, | |
57 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10); | |
58 | ||
35bc60cc AS |
59 | METHOD(public_key_t, verify, bool, |
60 | private_curve25519_public_key_t *this, signature_scheme_t scheme, | |
a413571f | 61 | void *params, chunk_t data, chunk_t signature) |
35bc60cc | 62 | { |
d47ad3d6 AS |
63 | hasher_t *hasher; |
64 | uint8_t d = 0, k[HASH_SIZE_SHA512], r[32], *sig; | |
65 | int i; | |
66 | ge_p3 A; | |
67 | ge_p2 R; | |
68 | ||
35bc60cc AS |
69 | if (scheme != SIGN_ED25519) |
70 | { | |
71 | DBG1(DBG_LIB, "signature scheme %N not supported by Ed25519", | |
72 | signature_scheme_names, scheme); | |
73 | return FALSE; | |
74 | } | |
35bc60cc | 75 | |
d47ad3d6 AS |
76 | if (signature.len != 64) |
77 | { | |
78 | DBG1(DBG_LIB, "size of Ed25519 signature is not 64 bytes"); | |
79 | return FALSE; | |
80 | } | |
81 | sig = signature.ptr; | |
82 | ||
83 | if (sig[63] & 0xe0) | |
84 | { | |
85 | DBG1(DBG_LIB, "the three most significant bits of Ed25519 signature " | |
86 | "are not zero"); | |
87 | return FALSE; | |
88 | } | |
89 | ||
90 | if (ge_frombytes_negate_vartime(&A, this->pubkey.ptr) != 0) | |
91 | { | |
92 | return FALSE; | |
93 | } | |
35bc60cc | 94 | |
d47ad3d6 AS |
95 | /* check for all-zeroes public key */ |
96 | for (i = 0; i < 32; i++) | |
97 | { | |
98 | d |= this->pubkey.ptr[i]; | |
99 | } | |
100 | if (!d) | |
101 | { | |
102 | return FALSE; | |
103 | } | |
2571898d TB |
104 | /* make sure 0 <= s < L, as per RFC 8032, section 5.1.7 to prevent signature |
105 | * malleability. Due to the three-bit check above (forces s < 2^253) there | |
106 | * is not that much room, but adding L once works with most signatures */ | |
107 | for (i = 31; ; i--) | |
108 | { | |
109 | if (sig[i+32] < curve25519_order.ptr[i]) | |
110 | { | |
111 | break; | |
112 | } | |
113 | else if (sig[i+32] > curve25519_order.ptr[i] || i == 0) | |
114 | { | |
115 | return FALSE; | |
116 | } | |
117 | } | |
d47ad3d6 AS |
118 | |
119 | hasher = lib->crypto->create_hasher(lib->crypto, HASH_SHA512); | |
120 | if (!hasher) | |
121 | { | |
122 | return FALSE; | |
123 | } | |
124 | if (!hasher->get_hash(hasher, chunk_create(sig, 32), NULL) || | |
125 | !hasher->get_hash(hasher, this->pubkey, NULL) || | |
126 | !hasher->get_hash(hasher, data, k)) | |
127 | { | |
128 | hasher->destroy(hasher); | |
129 | return FALSE; | |
130 | } | |
131 | hasher->destroy(hasher); | |
132 | ||
133 | sc_reduce(k); | |
134 | ge_double_scalarmult_vartime(&R, k, &A, sig + 32); | |
135 | ge_tobytes(r, &R); | |
136 | ||
137 | return memeq_const(sig, r, 32); | |
138 | } | |
35bc60cc AS |
139 | |
140 | METHOD(public_key_t, encrypt_, bool, | |
141 | private_curve25519_public_key_t *this, encryption_scheme_t scheme, | |
142 | chunk_t plain, chunk_t *crypto) | |
143 | { | |
144 | DBG1(DBG_LIB, "encryption scheme %N not supported", encryption_scheme_names, | |
145 | scheme); | |
146 | return FALSE; | |
147 | } | |
148 | ||
149 | METHOD(public_key_t, get_keysize, int, | |
150 | private_curve25519_public_key_t *this) | |
151 | { | |
152 | return 8 * ED25519_KEY_LEN; | |
153 | } | |
154 | ||
155 | METHOD(public_key_t, get_encoding, bool, | |
156 | private_curve25519_public_key_t *this, cred_encoding_type_t type, | |
157 | chunk_t *encoding) | |
158 | { | |
159 | bool success = TRUE; | |
160 | ||
161 | *encoding = curve25519_public_key_info_encode(this->pubkey); | |
162 | ||
163 | if (type != PUBKEY_SPKI_ASN1_DER) | |
164 | { | |
165 | chunk_t asn1_encoding = *encoding; | |
166 | ||
167 | success = lib->encoding->encode(lib->encoding, type, | |
168 | NULL, encoding, CRED_PART_EDDSA_PUB_ASN1_DER, | |
169 | asn1_encoding, CRED_PART_END); | |
170 | chunk_clear(&asn1_encoding); | |
171 | } | |
172 | return success; | |
173 | } | |
174 | ||
175 | METHOD(public_key_t, get_fingerprint, bool, | |
176 | private_curve25519_public_key_t *this, cred_encoding_type_t type, | |
177 | chunk_t *fp) | |
178 | { | |
179 | bool success; | |
180 | ||
181 | if (lib->encoding->get_cache(lib->encoding, type, this, fp)) | |
182 | { | |
183 | return TRUE; | |
184 | } | |
185 | success = curve25519_public_key_fingerprint(this->pubkey, type, fp); | |
186 | if (success) | |
187 | { | |
188 | lib->encoding->cache(lib->encoding, type, this, *fp); | |
189 | } | |
190 | return success; | |
191 | } | |
192 | ||
193 | METHOD(public_key_t, get_ref, public_key_t*, | |
194 | private_curve25519_public_key_t *this) | |
195 | { | |
196 | ref_get(&this->ref); | |
197 | return &this->public.key; | |
198 | } | |
199 | ||
200 | METHOD(public_key_t, destroy, void, | |
201 | private_curve25519_public_key_t *this) | |
202 | { | |
203 | if (ref_put(&this->ref)) | |
204 | { | |
205 | lib->encoding->clear_cache(lib->encoding, this); | |
206 | free(this->pubkey.ptr); | |
207 | free(this); | |
208 | } | |
209 | } | |
210 | ||
211 | /** | |
212 | * ASN.1 definition of an Ed25519 public key | |
213 | */ | |
214 | static const asn1Object_t pubkeyObjects[] = { | |
215 | { 0, "subjectPublicKeyInfo",ASN1_SEQUENCE, ASN1_NONE }, /* 0 */ | |
216 | { 1, "algorithm", ASN1_EOC, ASN1_RAW }, /* 1 */ | |
217 | { 1, "subjectPublicKey", ASN1_BIT_STRING, ASN1_BODY }, /* 2 */ | |
218 | { 0, "exit", ASN1_EOC, ASN1_EXIT } | |
219 | }; | |
220 | ||
221 | #define ED25519_SUBJECT_PUBLIC_KEY_ALGORITHM 1 | |
222 | #define ED25519_SUBJECT_PUBLIC_KEY 2 | |
223 | ||
20f74adb TB |
224 | /** |
225 | * Parse the ASN.1-encoded subjectPublicKeyInfo | |
226 | */ | |
227 | static bool parse_public_key_info(private_curve25519_public_key_t *this, | |
228 | chunk_t blob) | |
229 | { | |
230 | asn1_parser_t *parser; | |
231 | chunk_t object; | |
232 | bool success = FALSE; | |
233 | int objectID, oid; | |
234 | ||
235 | parser = asn1_parser_create(pubkeyObjects, blob); | |
236 | ||
237 | while (parser->iterate(parser, &objectID, &object)) | |
238 | { | |
239 | switch (objectID) | |
240 | { | |
241 | case ED25519_SUBJECT_PUBLIC_KEY_ALGORITHM: | |
242 | { | |
243 | oid = asn1_parse_algorithmIdentifier(object, | |
244 | parser->get_level(parser) + 1, NULL); | |
245 | if (oid != OID_ED25519) | |
246 | { | |
247 | goto end; | |
248 | } | |
249 | break; | |
250 | } | |
251 | case ED25519_SUBJECT_PUBLIC_KEY: | |
252 | { | |
253 | /* encoded as an ASN1 BIT STRING */ | |
254 | if (object.len != 1 + ED25519_KEY_LEN) | |
255 | { | |
256 | goto end; | |
257 | } | |
258 | this->pubkey = chunk_clone(chunk_skip(object, 1)); | |
259 | break; | |
260 | } | |
261 | } | |
262 | } | |
263 | success = parser->success(parser); | |
264 | ||
265 | end: | |
266 | parser->destroy(parser); | |
267 | return success; | |
268 | } | |
269 | ||
35bc60cc AS |
270 | /** |
271 | * See header. | |
272 | */ | |
273 | curve25519_public_key_t *curve25519_public_key_load(key_type_t type, | |
274 | va_list args) | |
275 | { | |
276 | private_curve25519_public_key_t *this; | |
20f74adb | 277 | chunk_t asn1 = chunk_empty, blob = chunk_empty; |
35bc60cc AS |
278 | |
279 | while (TRUE) | |
280 | { | |
281 | switch (va_arg(args, builder_part_t)) | |
282 | { | |
283 | case BUILD_BLOB_ASN1_DER: | |
20f74adb TB |
284 | asn1 = va_arg(args, chunk_t); |
285 | continue; | |
286 | case BUILD_EDDSA_PUB: | |
35bc60cc AS |
287 | blob = va_arg(args, chunk_t); |
288 | continue; | |
289 | case BUILD_END: | |
290 | break; | |
291 | default: | |
292 | return NULL; | |
293 | } | |
294 | break; | |
295 | } | |
296 | ||
297 | INIT(this, | |
298 | .public = { | |
299 | .key = { | |
300 | .get_type = _get_type, | |
301 | .verify = _verify, | |
302 | .encrypt = _encrypt_, | |
303 | .equals = public_key_equals, | |
304 | .get_keysize = _get_keysize, | |
305 | .get_fingerprint = _get_fingerprint, | |
306 | .has_fingerprint = public_key_has_fingerprint, | |
307 | .get_encoding = _get_encoding, | |
308 | .get_ref = _get_ref, | |
309 | .destroy = _destroy, | |
310 | }, | |
311 | }, | |
312 | .ref = 1, | |
313 | ); | |
314 | ||
20f74adb | 315 | if (blob.len == ED25519_KEY_LEN) |
35bc60cc | 316 | { |
20f74adb | 317 | this->pubkey = chunk_clone(blob); |
35bc60cc | 318 | } |
20f74adb | 319 | else if (!asn1.len || !parse_public_key_info(this, asn1)) |
35bc60cc AS |
320 | { |
321 | destroy(this); | |
322 | return NULL; | |
323 | } | |
324 | return &this->public; | |
325 | } | |
326 | ||
327 | /** | |
328 | * See header. | |
329 | */ | |
330 | chunk_t curve25519_public_key_info_encode(chunk_t pubkey) | |
331 | { | |
332 | return asn1_wrap(ASN1_SEQUENCE, "mm", | |
333 | asn1_wrap(ASN1_SEQUENCE, "m", | |
334 | asn1_build_known_oid(OID_ED25519)), | |
335 | asn1_bitstring("c", pubkey)); | |
336 | } | |
337 | ||
338 | /** | |
339 | * See header. | |
340 | */ | |
341 | bool curve25519_public_key_fingerprint(chunk_t pubkey, | |
342 | cred_encoding_type_t type, chunk_t *fp) | |
343 | { | |
344 | hasher_t *hasher; | |
345 | chunk_t key; | |
346 | ||
347 | switch (type) | |
348 | { | |
349 | case KEYID_PUBKEY_SHA1: | |
350 | key = chunk_clone(pubkey); | |
351 | break; | |
352 | case KEYID_PUBKEY_INFO_SHA1: | |
353 | key = curve25519_public_key_info_encode(pubkey); | |
354 | break; | |
355 | default: | |
356 | return FALSE; | |
357 | } | |
358 | ||
359 | hasher = lib->crypto->create_hasher(lib->crypto, HASH_SHA1); | |
360 | if (!hasher || !hasher->allocate_hash(hasher, key, fp)) | |
361 | { | |
362 | DBG1(DBG_LIB, "SHA1 hash algorithm not supported, " | |
363 | "fingerprinting failed"); | |
364 | DESTROY_IF(hasher); | |
365 | free(key.ptr); | |
366 | return FALSE; | |
367 | } | |
368 | hasher->destroy(hasher); | |
369 | free(key.ptr); | |
370 | return TRUE; | |
371 | } |