summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authordjm@openbsd.org <djm@openbsd.org>2019-11-26 23:41:23 +0000
committerDamien Miller <djm@mindrot.org>2019-11-27 10:47:28 +1100
commitc6efa8a91af1d4fdb43909a23a0a4ffa012155ad (patch)
tree5908cdd00c3051d889da7c8f8a25750326fd7af1
parent8635afa1cdc21366d61730d943f3cf61861899c8 (diff)
upstream: add dummy security key middleware based on work by
markus@ This will allow us to test U2F/FIDO2 support in OpenSSH without requiring real hardware. ok markus@ OpenBSD-Regress-ID: 88b309464b8850c320cf7513f26d97ee1fdf9aae
-rw-r--r--regress/misc/Makefile2
-rw-r--r--regress/misc/sk-dummy/Makefile67
-rw-r--r--regress/misc/sk-dummy/sk-dummy.c522
3 files changed, 590 insertions, 1 deletions
diff --git a/regress/misc/Makefile b/regress/misc/Makefile
index 14c0c279f..cf95f265c 100644
--- a/regress/misc/Makefile
+++ b/regress/misc/Makefile
@@ -1,3 +1,3 @@
1SUBDIR= kexfuzz 1SUBDIR= kexfuzz sk-dummy
2 2
3.include <bsd.subdir.mk> 3.include <bsd.subdir.mk>
diff --git a/regress/misc/sk-dummy/Makefile b/regress/misc/sk-dummy/Makefile
new file mode 100644
index 000000000..db229aa1f
--- /dev/null
+++ b/regress/misc/sk-dummy/Makefile
@@ -0,0 +1,67 @@
1# $OpenBSD: Makefile,v 1.1 2019/11/26 23:41:23 djm Exp $
2
3.include <bsd.own.mk>
4.include <bsd.obj.mk>
5
6PROG= sk-dummy.so
7NOMAN=
8
9SSHREL=../../../../../usr.bin/ssh
10.PATH: ${.CURDIR}/${SSHREL}
11
12SRCS=sk-dummy.c
13# From usr.bin/ssh
14SRCS+=ed25519.c hash.c ge25519.c fe25519.c sc25519.c verify.c
15SRCS+=digest-openssl.c ssherr.c fatal.c sshbuf.c log.c cleanup.c
16OPENSSL?= yes
17
18CFLAGS+= -fPIC
19
20.if (${OPENSSL:L} == "yes")
21CFLAGS+= -DWITH_OPENSSL
22.endif
23
24# enable warnings
25WARNINGS=Yes
26
27DEBUG=-g
28CFLAGS+= -fstack-protector-all
29CDIAGFLAGS= -Wall
30CDIAGFLAGS+= -Wextra
31CDIAGFLAGS+= -Werror
32CDIAGFLAGS+= -Wchar-subscripts
33CDIAGFLAGS+= -Wcomment
34CDIAGFLAGS+= -Wformat
35CDIAGFLAGS+= -Wformat-security
36CDIAGFLAGS+= -Wimplicit
37CDIAGFLAGS+= -Winline
38CDIAGFLAGS+= -Wmissing-declarations
39CDIAGFLAGS+= -Wmissing-prototypes
40CDIAGFLAGS+= -Wparentheses
41CDIAGFLAGS+= -Wpointer-arith
42CDIAGFLAGS+= -Wreturn-type
43CDIAGFLAGS+= -Wshadow
44CDIAGFLAGS+= -Wsign-compare
45CDIAGFLAGS+= -Wstrict-aliasing
46CDIAGFLAGS+= -Wstrict-prototypes
47CDIAGFLAGS+= -Wswitch
48CDIAGFLAGS+= -Wtrigraphs
49CDIAGFLAGS+= -Wuninitialized
50CDIAGFLAGS+= -Wunused
51CDIAGFLAGS+= -Wno-unused-parameter
52.if ${COMPILER_VERSION:L} != "gcc3"
53CDIAGFLAGS+= -Wold-style-definition
54.endif
55
56CFLAGS+=-I${.CURDIR}/${SSHREL}
57
58.if (${OPENSSL:L} == "yes")
59LDADD+= -lcrypto
60DPADD+= ${LIBCRYPTO}
61.endif
62
63$(PROG): $(OBJS)
64 $(CC) $(LDFLAGS) -shared -o $@ $(OBJS) $(LDADD)
65
66.include <bsd.prog.mk>
67
diff --git a/regress/misc/sk-dummy/sk-dummy.c b/regress/misc/sk-dummy/sk-dummy.c
new file mode 100644
index 000000000..b223b1a0f
--- /dev/null
+++ b/regress/misc/sk-dummy/sk-dummy.c
@@ -0,0 +1,522 @@
1/*
2 * Copyright (c) 2019 Markus Friedl
3 *
4 * Permission to use, copy, modify, and distribute this software for any
5 * purpose with or without fee is hereby granted, provided that the above
6 * copyright notice and this permission notice appear in all copies.
7 *
8 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
9 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
10 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
11 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
12 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
13 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
14 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
15 */
16
17#include <stdint.h>
18#include <stdlib.h>
19#include <string.h>
20#include <stdio.h>
21#include <stddef.h>
22#include <stdarg.h>
23
24#include "crypto_api.h"
25
26#include <openssl/opensslv.h>
27#include <openssl/crypto.h>
28#include <openssl/evp.h>
29#include <openssl/bn.h>
30#include <openssl/ec.h>
31#include <openssl/ecdsa.h>
32#include <openssl/pem.h>
33
34/* #define SK_DEBUG 1 */
35
36/* Compatibility with OpenSSH 1.0.x */
37#if (OPENSSL_VERSION_NUMBER < 0x10100000L)
38#define ECDSA_SIG_get0(sig, pr, ps) \
39 do { \
40 (*pr) = sig->r; \
41 (*ps) = sig->s; \
42 } while (0)
43#endif
44
45#define SK_VERSION_MAJOR 0x00020000 /* current API version */
46
47/* Flags */
48#define SK_USER_PRESENCE_REQD 0x01
49
50/* Algs */
51#define SK_ECDSA 0x00
52#define SK_ED25519 0x01
53
54struct sk_enroll_response {
55 uint8_t *public_key;
56 size_t public_key_len;
57 uint8_t *key_handle;
58 size_t key_handle_len;
59 uint8_t *signature;
60 size_t signature_len;
61 uint8_t *attestation_cert;
62 size_t attestation_cert_len;
63};
64
65struct sk_sign_response {
66 uint8_t flags;
67 uint32_t counter;
68 uint8_t *sig_r;
69 size_t sig_r_len;
70 uint8_t *sig_s;
71 size_t sig_s_len;
72};
73
74/* Return the version of the middleware API */
75uint32_t sk_api_version(void);
76
77/* Enroll a U2F key (private key generation) */
78int sk_enroll(int alg, const uint8_t *challenge, size_t challenge_len,
79 const char *application, uint8_t flags,
80 struct sk_enroll_response **enroll_response);
81
82/* Sign a challenge */
83int sk_sign(int alg, const uint8_t *message, size_t message_len,
84 const char *application, const uint8_t *key_handle, size_t key_handle_len,
85 uint8_t flags, struct sk_sign_response **sign_response);
86
87static void skdebug(const char *func, const char *fmt, ...)
88 __attribute__((__format__ (printf, 2, 3)));
89
90static void
91skdebug(const char *func, const char *fmt, ...)
92{
93#if defined(SK_DEBUG)
94 va_list ap;
95
96 va_start(ap, fmt);
97 fprintf(stderr, "sk-dummy %s: ", func);
98 vfprintf(stderr, fmt, ap);
99 fputc('\n', stderr);
100 va_end(ap);
101#else
102 (void)func; /* XXX */
103 (void)fmt; /* XXX */
104#endif
105}
106
107uint32_t
108sk_api_version(void)
109{
110 return SK_VERSION_MAJOR;
111}
112
113static int
114pack_key_ecdsa(struct sk_enroll_response *response)
115{
116 EC_KEY *key = NULL;
117 const EC_GROUP *g;
118 const EC_POINT *q;
119 int ret = -1;
120 long privlen;
121 BIO *bio = NULL;
122 char *privptr;
123
124 response->public_key = NULL;
125 response->public_key_len = 0;
126 response->key_handle = NULL;
127 response->key_handle_len = 0;
128
129 if ((key = EC_KEY_new_by_curve_name(NID_X9_62_prime256v1)) == NULL) {
130 skdebug(__func__, "EC_KEY_new_by_curve_name");
131 goto out;
132 }
133 if (EC_KEY_generate_key(key) != 1) {
134 skdebug(__func__, "EC_KEY_generate_key");
135 goto out;
136 }
137 EC_KEY_set_asn1_flag(key, OPENSSL_EC_NAMED_CURVE);
138 if ((bio = BIO_new(BIO_s_mem())) == NULL ||
139 (g = EC_KEY_get0_group(key)) == NULL ||
140 (q = EC_KEY_get0_public_key(key)) == NULL) {
141 skdebug(__func__, "couldn't get key parameters");
142 goto out;
143 }
144 response->public_key_len = EC_POINT_point2oct(g, q,
145 POINT_CONVERSION_UNCOMPRESSED, NULL, 0, NULL);
146 if (response->public_key_len == 0 || response->public_key_len > 2048) {
147 skdebug(__func__, "bad pubkey length %zu",
148 response->public_key_len);
149 goto out;
150 }
151 if ((response->public_key = malloc(response->public_key_len)) == NULL) {
152 skdebug(__func__, "malloc pubkey failed");
153 goto out;
154 }
155 if (EC_POINT_point2oct(g, q, POINT_CONVERSION_UNCOMPRESSED,
156 response->public_key, response->public_key_len, NULL) == 0) {
157 skdebug(__func__, "EC_POINT_point2oct failed");
158 goto out;
159 }
160 /* Key handle contains PEM encoded private key */
161 if (!PEM_write_bio_ECPrivateKey(bio, key, NULL, NULL, 0, NULL, NULL)) {
162 skdebug(__func__, "PEM_write_bio_ECPrivateKey failed");
163 goto out;
164 }
165 if ((privlen = BIO_get_mem_data(bio, &privptr)) <= 0) {
166 skdebug(__func__, "BIO_get_mem_data failed");
167 goto out;
168 }
169 if ((response->key_handle = malloc(privlen)) == NULL) {
170 skdebug(__func__, "malloc key_handle failed");
171 goto out;
172 }
173 response->key_handle_len = (size_t)privlen;
174 memcpy(response->key_handle, privptr, response->key_handle_len);
175 /* success */
176 ret = 0;
177 out:
178 if (ret != 0) {
179 if (response->public_key != NULL) {
180 memset(response->public_key, 0,
181 response->public_key_len);
182 free(response->public_key);
183 response->public_key = NULL;
184 }
185 if (response->key_handle != NULL) {
186 memset(response->key_handle, 0,
187 response->key_handle_len);
188 free(response->key_handle);
189 response->key_handle = NULL;
190 }
191 }
192 BIO_free(bio);
193 EC_KEY_free(key);
194 return ret;
195}
196
197static int
198pack_key_ed25519(struct sk_enroll_response *response)
199{
200 int ret = -1;
201 u_char pk[crypto_sign_ed25519_PUBLICKEYBYTES];
202 u_char sk[crypto_sign_ed25519_SECRETKEYBYTES];
203
204 response->public_key = NULL;
205 response->public_key_len = 0;
206 response->key_handle = NULL;
207 response->key_handle_len = 0;
208
209 memset(pk, 0, sizeof(pk));
210 memset(sk, 0, sizeof(sk));
211 crypto_sign_ed25519_keypair(pk, sk);
212
213 response->public_key_len = sizeof(pk);
214 if ((response->public_key = malloc(response->public_key_len)) == NULL) {
215 skdebug(__func__, "malloc pubkey failed");
216 goto out;
217 }
218 memcpy(response->public_key, pk, sizeof(pk));
219 /* Key handle contains sk */
220 response->key_handle_len = sizeof(sk);
221 if ((response->key_handle = malloc(response->key_handle_len)) == NULL) {
222 skdebug(__func__, "malloc key_handle failed");
223 goto out;
224 }
225 memcpy(response->key_handle, sk, sizeof(sk));
226 /* success */
227 ret = 0;
228 out:
229 if (ret != 0)
230 free(response->public_key);
231 return ret;
232}
233
234int
235sk_enroll(int alg, const uint8_t *challenge, size_t challenge_len,
236 const char *application, uint8_t flags,
237 struct sk_enroll_response **enroll_response)
238{
239 struct sk_enroll_response *response = NULL;
240 int ret = -1;
241
242 (void)flags; /* XXX; unused */
243
244 if (enroll_response == NULL) {
245 skdebug(__func__, "enroll_response == NULL");
246 goto out;
247 }
248 *enroll_response = NULL;
249 if ((response = calloc(1, sizeof(*response))) == NULL) {
250 skdebug(__func__, "calloc response failed");
251 goto out;
252 }
253 switch(alg) {
254 case SK_ECDSA:
255 if (pack_key_ecdsa(response) != 0)
256 goto out;
257 break;
258 case SK_ED25519:
259 if (pack_key_ed25519(response) != 0)
260 goto out;
261 break;
262 default:
263 skdebug(__func__, "unsupported key type %d", alg);
264 return -1;
265 }
266 /* Have to return something here */
267 if ((response->signature = calloc(1, 1)) == NULL) {
268 skdebug(__func__, "calloc signature failed");
269 goto out;
270 }
271 response->signature_len = 0;
272
273 *enroll_response = response;
274 response = NULL;
275 ret = 0;
276 out:
277 if (response != NULL) {
278 free(response->public_key);
279 free(response->key_handle);
280 free(response->signature);
281 free(response->attestation_cert);
282 free(response);
283 }
284 return ret;
285}
286
287static void
288dump(const char *preamble, const void *sv, size_t l)
289{
290#ifdef SK_DEBUG
291 const u_char *s = (const u_char *)sv;
292 size_t i;
293
294 fprintf(stderr, "%s (len %zu):\n", preamble, l);
295 for (i = 0; i < l; i++) {
296 if (i % 16 == 0)
297 fprintf(stderr, "%04zu: ", i);
298 fprintf(stderr, "%02x", s[i]);
299 if (i % 16 == 15 || i == l - 1)
300 fprintf(stderr, "\n");
301 }
302#endif
303}
304
305static int
306sig_ecdsa(const uint8_t *message, size_t message_len,
307 const char *application, uint32_t counter, uint8_t flags,
308 const uint8_t *key_handle, size_t key_handle_len,
309 struct sk_sign_response *response)
310{
311 ECDSA_SIG *sig = NULL;
312 const BIGNUM *sig_r, *sig_s;
313 int ret = -1;
314 BIO *bio = NULL;
315 EVP_PKEY *pk = NULL;
316 EC_KEY *ec = NULL;
317 SHA256_CTX ctx;
318 uint8_t apphash[SHA256_DIGEST_LENGTH];
319 uint8_t sighash[SHA256_DIGEST_LENGTH];
320 uint8_t countbuf[4];
321
322 /* Decode EC_KEY from key handle */
323 if ((bio = BIO_new(BIO_s_mem())) == NULL ||
324 BIO_write(bio, key_handle, key_handle_len) != (int)key_handle_len) {
325 skdebug(__func__, "BIO setup failed");
326 goto out;
327 }
328 if ((pk = PEM_read_bio_PrivateKey(bio, NULL, NULL, "")) == NULL) {
329 skdebug(__func__, "PEM_read_bio_PrivateKey failed");
330 goto out;
331 }
332 if (EVP_PKEY_base_id(pk) != EVP_PKEY_EC) {
333 skdebug(__func__, "Not an EC key: %d", EVP_PKEY_base_id(pk));
334 goto out;
335 }
336 if ((ec = EVP_PKEY_get1_EC_KEY(pk)) == NULL) {
337 skdebug(__func__, "EVP_PKEY_get1_EC_KEY failed");
338 goto out;
339 }
340 /* Expect message to be pre-hashed */
341 if (message_len != SHA256_DIGEST_LENGTH) {
342 skdebug(__func__, "bad message len %zu", message_len);
343 goto out;
344 }
345 /* Prepare data to be signed */
346 dump("message", message, message_len);
347 SHA256_Init(&ctx);
348 SHA256_Update(&ctx, application, strlen(application));
349 SHA256_Final(apphash, &ctx);
350 dump("apphash", apphash, sizeof(apphash));
351 countbuf[0] = (counter >> 24) & 0xff;
352 countbuf[1] = (counter >> 16) & 0xff;
353 countbuf[2] = (counter >> 8) & 0xff;
354 countbuf[3] = counter & 0xff;
355 dump("countbuf", countbuf, sizeof(countbuf));
356 dump("flags", &flags, sizeof(flags));
357 SHA256_Init(&ctx);
358 SHA256_Update(&ctx, apphash, sizeof(apphash));
359 SHA256_Update(&ctx, &flags, sizeof(flags));
360 SHA256_Update(&ctx, countbuf, sizeof(countbuf));
361 SHA256_Update(&ctx, message, message_len);
362 SHA256_Final(sighash, &ctx);
363 dump("sighash", sighash, sizeof(sighash));
364 /* create and encode signature */
365 if ((sig = ECDSA_do_sign(sighash, sizeof(sighash), ec)) == NULL) {
366 skdebug(__func__, "ECDSA_do_sign failed");
367 goto out;
368 }
369 ECDSA_SIG_get0(sig, &sig_r, &sig_s);
370 response->sig_r_len = BN_num_bytes(sig_r);
371 response->sig_s_len = BN_num_bytes(sig_s);
372 if ((response->sig_r = calloc(1, response->sig_r_len)) == NULL ||
373 (response->sig_s = calloc(1, response->sig_s_len)) == NULL) {
374 skdebug(__func__, "calloc signature failed");
375 goto out;
376 }
377 BN_bn2bin(sig_r, response->sig_r);
378 BN_bn2bin(sig_s, response->sig_s);
379 ret = 0;
380 out:
381 explicit_bzero(&ctx, sizeof(ctx));
382 explicit_bzero(&apphash, sizeof(apphash));
383 explicit_bzero(&sighash, sizeof(sighash));
384 ECDSA_SIG_free(sig);
385 if (ret != 0) {
386 free(response->sig_r);
387 free(response->sig_s);
388 response->sig_r = NULL;
389 response->sig_s = NULL;
390 }
391 BIO_free(bio);
392 EC_KEY_free(ec);
393 EVP_PKEY_free(pk);
394 return ret;
395}
396
397static int
398sig_ed25519(const uint8_t *message, size_t message_len,
399 const char *application, uint32_t counter, uint8_t flags,
400 const uint8_t *key_handle, size_t key_handle_len,
401 struct sk_sign_response *response)
402{
403 size_t o;
404 int ret = -1;
405 SHA256_CTX ctx;
406 uint8_t apphash[SHA256_DIGEST_LENGTH];
407 uint8_t signbuf[sizeof(apphash) + sizeof(flags) +
408 sizeof(counter) + SHA256_DIGEST_LENGTH];
409 uint8_t sig[crypto_sign_ed25519_BYTES + sizeof(signbuf)];
410 unsigned long long smlen;
411
412 if (key_handle_len != crypto_sign_ed25519_SECRETKEYBYTES) {
413 skdebug(__func__, "bad key handle length %zu", key_handle_len);
414 goto out;
415 }
416 /* Expect message to be pre-hashed */
417 if (message_len != SHA256_DIGEST_LENGTH) {
418 skdebug(__func__, "bad message len %zu", message_len);
419 goto out;
420 }
421 /* Prepare data to be signed */
422 dump("message", message, message_len);
423 SHA256_Init(&ctx);
424 SHA256_Update(&ctx, application, strlen(application));
425 SHA256_Final(apphash, &ctx);
426 dump("apphash", apphash, sizeof(apphash));
427
428 memcpy(signbuf, apphash, sizeof(apphash));
429 o = sizeof(apphash);
430 signbuf[o++] = flags;
431 signbuf[o++] = (counter >> 24) & 0xff;
432 signbuf[o++] = (counter >> 16) & 0xff;
433 signbuf[o++] = (counter >> 8) & 0xff;
434 signbuf[o++] = counter & 0xff;
435 memcpy(signbuf + o, message, message_len);
436 o += message_len;
437 if (o != sizeof(signbuf)) {
438 skdebug(__func__, "bad sign buf len %zu, expected %zu",
439 o, sizeof(signbuf));
440 goto out;
441 }
442 dump("signbuf", signbuf, sizeof(signbuf));
443 /* create and encode signature */
444 smlen = sizeof(signbuf);
445 if (crypto_sign_ed25519(sig, &smlen, signbuf, sizeof(signbuf),
446 key_handle) != 0) {
447 skdebug(__func__, "crypto_sign_ed25519 failed");
448 goto out;
449 }
450 if (smlen <= sizeof(signbuf)) {
451 skdebug(__func__, "bad sign smlen %llu, expected min %zu",
452 smlen, sizeof(signbuf) + 1);
453 goto out;
454 }
455 response->sig_r_len = (size_t)(smlen - sizeof(signbuf));
456 if ((response->sig_r = calloc(1, response->sig_r_len)) == NULL) {
457 skdebug(__func__, "calloc signature failed");
458 goto out;
459 }
460 memcpy(response->sig_r, sig, response->sig_r_len);
461 dump("sig_r", response->sig_r, response->sig_r_len);
462 ret = 0;
463 out:
464 explicit_bzero(&ctx, sizeof(ctx));
465 explicit_bzero(&apphash, sizeof(apphash));
466 explicit_bzero(&signbuf, sizeof(signbuf));
467 explicit_bzero(&sig, sizeof(sig));
468 if (ret != 0) {
469 free(response->sig_r);
470 response->sig_r = NULL;
471 }
472 return ret;
473}
474
475int
476sk_sign(int alg, const uint8_t *message, size_t message_len,
477 const char *application,
478 const uint8_t *key_handle, size_t key_handle_len,
479 uint8_t flags, struct sk_sign_response **sign_response)
480{
481 struct sk_sign_response *response = NULL;
482 int ret = -1;
483
484 if (sign_response == NULL) {
485 skdebug(__func__, "sign_response == NULL");
486 goto out;
487 }
488 *sign_response = NULL;
489 if ((response = calloc(1, sizeof(*response))) == NULL) {
490 skdebug(__func__, "calloc response failed");
491 goto out;
492 }
493 response->flags = flags;
494 response->counter = 0x12345678;
495 switch(alg) {
496 case SK_ECDSA:
497 if (sig_ecdsa(message, message_len, application,
498 response->counter, flags, key_handle, key_handle_len,
499 response) != 0)
500 goto out;
501 break;
502 case SK_ED25519:
503 if (sig_ed25519(message, message_len, application,
504 response->counter, flags, key_handle, key_handle_len,
505 response) != 0)
506 goto out;
507 break;
508 default:
509 skdebug(__func__, "unsupported key type %d", alg);
510 return -1;
511 }
512 *sign_response = response;
513 response = NULL;
514 ret = 0;
515 out:
516 if (response != NULL) {
517 free(response->sig_r);
518 free(response->sig_s);
519 free(response);
520 }
521 return ret;
522}