xref: /openbsd/lib/libfido2/src/ecdh.c (revision 274d7c50)
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/evp.h>
8 #include <openssl/sha.h>
9 
10 #include "fido.h"
11 #include "fido/es256.h"
12 
13 static int
14 do_ecdh(const es256_sk_t *sk, const es256_pk_t *pk, fido_blob_t **ecdh)
15 {
16 	EVP_PKEY	*pk_evp = NULL;
17 	EVP_PKEY	*sk_evp = NULL;
18 	EVP_PKEY_CTX	*ctx = NULL;
19 	fido_blob_t	*secret = NULL;
20 	int		 ok = -1;
21 
22 	*ecdh = NULL;
23 
24 	/* allocate blobs for secret & ecdh */
25 	if ((secret = fido_blob_new()) == NULL ||
26 	    (*ecdh = fido_blob_new()) == NULL)
27 		goto fail;
28 
29 	/* wrap the keys as openssl objects */
30 	if ((pk_evp = es256_pk_to_EVP_PKEY(pk)) == NULL ||
31 	    (sk_evp = es256_sk_to_EVP_PKEY(sk)) == NULL) {
32 		log_debug("%s: es256_to_EVP_PKEY", __func__);
33 		goto fail;
34 	}
35 
36 	/* set ecdh parameters */
37 	if ((ctx = EVP_PKEY_CTX_new(sk_evp, NULL)) == NULL ||
38 	    EVP_PKEY_derive_init(ctx) <= 0 ||
39 	    EVP_PKEY_derive_set_peer(ctx, pk_evp) <= 0) {
40 		log_debug("%s: EVP_PKEY_derive_init", __func__);
41 		goto fail;
42 	}
43 
44 	/* perform ecdh */
45 	if (EVP_PKEY_derive(ctx, NULL, &secret->len) <= 0 ||
46 	    (secret->ptr = calloc(1, secret->len)) == NULL ||
47 	    EVP_PKEY_derive(ctx, secret->ptr, &secret->len) <= 0) {
48 		log_debug("%s: EVP_PKEY_derive", __func__);
49 		goto fail;
50 	}
51 
52 	/* use sha256 as a kdf on the resulting secret */
53 	(*ecdh)->len = SHA256_DIGEST_LENGTH;
54 	if (((*ecdh)->ptr = calloc(1, (*ecdh)->len)) == NULL ||
55 	    SHA256(secret->ptr, secret->len, (*ecdh)->ptr) != (*ecdh)->ptr) {
56 		log_debug("%s: sha256", __func__);
57 		goto fail;
58 	}
59 
60 	ok = 0;
61 fail:
62 	if (pk_evp != NULL)
63 		EVP_PKEY_free(pk_evp);
64 	if (sk_evp != NULL)
65 		EVP_PKEY_free(sk_evp);
66 	if (ctx != NULL)
67 		EVP_PKEY_CTX_free(ctx);
68 	if (ok < 0)
69 		fido_blob_free(ecdh);
70 
71 	fido_blob_free(&secret);
72 
73 	return (ok);
74 }
75 
76 int
77 fido_do_ecdh(fido_dev_t *dev, es256_pk_t **pk, fido_blob_t **ecdh)
78 {
79 	es256_sk_t	*sk = NULL; /* our private key */
80 	es256_pk_t	*ak = NULL; /* authenticator's public key */
81 	int		 r;
82 
83 	*pk = NULL; /* our public key; returned */
84 	*ecdh = NULL; /* shared ecdh secret; returned */
85 
86 	if ((sk = es256_sk_new()) == NULL || (*pk = es256_pk_new()) == NULL) {
87 		r = FIDO_ERR_INTERNAL;
88 		goto fail;
89 	}
90 
91 	if (es256_sk_create(sk) < 0 || es256_derive_pk(sk, *pk) < 0) {
92 		log_debug("%s: es256_derive_pk", __func__);
93 		r = FIDO_ERR_INTERNAL;
94 		goto fail;
95 	}
96 
97 	if ((ak = es256_pk_new()) == NULL ||
98 	    fido_dev_authkey(dev, ak) != FIDO_OK) {
99 		log_debug("%s: fido_dev_authkey", __func__);
100 		r = FIDO_ERR_INTERNAL;
101 		goto fail;
102 	}
103 
104 	if (do_ecdh(sk, ak, ecdh) < 0) {
105 		log_debug("%s: do_ecdh", __func__);
106 		r = FIDO_ERR_INTERNAL;
107 		goto fail;
108 	}
109 
110 	r = FIDO_OK;
111 fail:
112 	es256_sk_free(&sk);
113 	es256_pk_free(&ak);
114 
115 	if (r != FIDO_OK) {
116 		es256_pk_free(pk);
117 		fido_blob_free(ecdh);
118 	}
119 
120 	return (r);
121 }
122