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