xref: /freebsd/contrib/libfido2/src/ecdh.c (revision 37c8ee8847faa53432809cae2ecc11b80c4eab2f)
1 /*
2  * Copyright (c) 2018-2021 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 #if defined(LIBRESSL_VERSION_NUMBER)
10 #include <openssl/hkdf.h>
11 #else
12 #include <openssl/kdf.h>
13 #endif
14 
15 #include "fido.h"
16 #include "fido/es256.h"
17 
18 #if defined(LIBRESSL_VERSION_NUMBER)
19 static int
20 hkdf_sha256(uint8_t *key, const char *info, const fido_blob_t *secret)
21 {
22 	const EVP_MD *md;
23 	uint8_t salt[32];
24 
25 	memset(salt, 0, sizeof(salt));
26 	if ((md = EVP_sha256()) == NULL ||
27 	    HKDF(key, SHA256_DIGEST_LENGTH, md, secret->ptr, secret->len, salt,
28 	    sizeof(salt), (const uint8_t *)info, strlen(info)) != 1)
29 		return -1;
30 
31 	return 0;
32 }
33 #else
34 static int
35 hkdf_sha256(uint8_t *key, char *info, fido_blob_t *secret)
36 {
37 	const EVP_MD *const_md;
38 	EVP_MD *md = NULL;
39 	EVP_PKEY_CTX *ctx = NULL;
40 	size_t keylen = SHA256_DIGEST_LENGTH;
41 	uint8_t	salt[32];
42 	int ok = -1;
43 
44 	memset(salt, 0, sizeof(salt));
45 	if (secret->len > INT_MAX || strlen(info) > INT_MAX) {
46 		fido_log_debug("%s: invalid param", __func__);
47 		goto fail;
48 	}
49 	if ((const_md = EVP_sha256()) == NULL ||
50 	    (md = EVP_MD_meth_dup(const_md)) == NULL ||
51 	    (ctx = EVP_PKEY_CTX_new_id(EVP_PKEY_HKDF, NULL)) == NULL) {
52 		fido_log_debug("%s: init", __func__);
53 		goto fail;
54 	}
55 	if (EVP_PKEY_derive_init(ctx) < 1 ||
56 	    EVP_PKEY_CTX_set_hkdf_md(ctx, md) < 1 ||
57 	    EVP_PKEY_CTX_set1_hkdf_salt(ctx, salt, sizeof(salt)) < 1 ||
58 	    EVP_PKEY_CTX_set1_hkdf_key(ctx, secret->ptr, (int)secret->len) < 1 ||
59 	    EVP_PKEY_CTX_add1_hkdf_info(ctx, (void *)info, (int)strlen(info)) < 1) {
60 		fido_log_debug("%s: EVP_PKEY_CTX", __func__);
61 		goto fail;
62 	}
63 	if (EVP_PKEY_derive(ctx, key, &keylen) < 1) {
64 		fido_log_debug("%s: EVP_PKEY_derive", __func__);
65 		goto fail;
66 	}
67 
68 	ok = 0;
69 fail:
70 	if (md != NULL)
71 		EVP_MD_meth_free(md);
72 	if (ctx != NULL)
73 		EVP_PKEY_CTX_free(ctx);
74 
75 	return ok;
76 }
77 #endif /* defined(LIBRESSL_VERSION_NUMBER) */
78 
79 static int
80 kdf(uint8_t prot, fido_blob_t *key, /* const */ fido_blob_t *secret)
81 {
82 	char hmac_info[] = "CTAP2 HMAC key"; /* const */
83 	char aes_info[] = "CTAP2 AES key"; /* const */
84 
85 	switch (prot) {
86 	case CTAP_PIN_PROTOCOL1:
87 		/* use sha256 on the resulting secret */
88 		key->len = SHA256_DIGEST_LENGTH;
89 		if ((key->ptr = calloc(1, key->len)) == NULL ||
90 		    SHA256(secret->ptr, secret->len, key->ptr) != key->ptr) {
91 			fido_log_debug("%s: SHA256", __func__);
92 			return -1;
93 		}
94 		break;
95 	case CTAP_PIN_PROTOCOL2:
96 		/* use two instances of hkdf-sha256 on the resulting secret */
97 		key->len = 2 * SHA256_DIGEST_LENGTH;
98 		if ((key->ptr = calloc(1, key->len)) == NULL ||
99 		    hkdf_sha256(key->ptr, hmac_info, secret) < 0 ||
100 		    hkdf_sha256(key->ptr + SHA256_DIGEST_LENGTH, aes_info,
101 		    secret) < 0) {
102 			fido_log_debug("%s: hkdf", __func__);
103 			return -1;
104 		}
105 		break;
106 	default:
107 		fido_log_debug("%s: unknown pin protocol %u", __func__, prot);
108 		return -1;
109 	}
110 
111 	return 0;
112 }
113 
114 static int
115 do_ecdh(const fido_dev_t *dev, const es256_sk_t *sk, const es256_pk_t *pk,
116     fido_blob_t **ecdh)
117 {
118 	EVP_PKEY *pk_evp = NULL;
119 	EVP_PKEY *sk_evp = NULL;
120 	EVP_PKEY_CTX *ctx = NULL;
121 	fido_blob_t *secret = NULL;
122 	int ok = -1;
123 
124 	*ecdh = NULL;
125 	if ((secret = fido_blob_new()) == NULL ||
126 	    (*ecdh = fido_blob_new()) == NULL)
127 		goto fail;
128 	if ((pk_evp = es256_pk_to_EVP_PKEY(pk)) == NULL ||
129 	    (sk_evp = es256_sk_to_EVP_PKEY(sk)) == NULL) {
130 		fido_log_debug("%s: es256_to_EVP_PKEY", __func__);
131 		goto fail;
132 	}
133 	if ((ctx = EVP_PKEY_CTX_new(sk_evp, NULL)) == NULL ||
134 	    EVP_PKEY_derive_init(ctx) <= 0 ||
135 	    EVP_PKEY_derive_set_peer(ctx, pk_evp) <= 0) {
136 		fido_log_debug("%s: EVP_PKEY_derive_init", __func__);
137 		goto fail;
138 	}
139 	if (EVP_PKEY_derive(ctx, NULL, &secret->len) <= 0 ||
140 	    (secret->ptr = calloc(1, secret->len)) == NULL ||
141 	    EVP_PKEY_derive(ctx, secret->ptr, &secret->len) <= 0) {
142 		fido_log_debug("%s: EVP_PKEY_derive", __func__);
143 		goto fail;
144 	}
145 	if (kdf(fido_dev_get_pin_protocol(dev), *ecdh, secret) < 0) {
146 		fido_log_debug("%s: kdf", __func__);
147 		goto fail;
148 	}
149 
150 	ok = 0;
151 fail:
152 	if (pk_evp != NULL)
153 		EVP_PKEY_free(pk_evp);
154 	if (sk_evp != NULL)
155 		EVP_PKEY_free(sk_evp);
156 	if (ctx != NULL)
157 		EVP_PKEY_CTX_free(ctx);
158 	if (ok < 0)
159 		fido_blob_free(ecdh);
160 
161 	fido_blob_free(&secret);
162 
163 	return ok;
164 }
165 
166 int
167 fido_do_ecdh(fido_dev_t *dev, es256_pk_t **pk, fido_blob_t **ecdh, int *ms)
168 {
169 	es256_sk_t *sk = NULL; /* our private key */
170 	es256_pk_t *ak = NULL; /* authenticator's public key */
171 	int r;
172 
173 	*pk = NULL;
174 	*ecdh = NULL;
175 	if ((sk = es256_sk_new()) == NULL || (*pk = es256_pk_new()) == NULL) {
176 		r = FIDO_ERR_INTERNAL;
177 		goto fail;
178 	}
179 	if (es256_sk_create(sk) < 0 || es256_derive_pk(sk, *pk) < 0) {
180 		fido_log_debug("%s: es256_derive_pk", __func__);
181 		r = FIDO_ERR_INTERNAL;
182 		goto fail;
183 	}
184 	if ((ak = es256_pk_new()) == NULL ||
185 	    fido_dev_authkey(dev, ak, ms) != FIDO_OK) {
186 		fido_log_debug("%s: fido_dev_authkey", __func__);
187 		r = FIDO_ERR_INTERNAL;
188 		goto fail;
189 	}
190 	if (do_ecdh(dev, sk, ak, ecdh) < 0) {
191 		fido_log_debug("%s: do_ecdh", __func__);
192 		r = FIDO_ERR_INTERNAL;
193 		goto fail;
194 	}
195 
196 	r = FIDO_OK;
197 fail:
198 	es256_sk_free(&sk);
199 	es256_pk_free(&ak);
200 
201 	if (r != FIDO_OK) {
202 		es256_pk_free(pk);
203 		fido_blob_free(ecdh);
204 	}
205 
206 	return r;
207 }
208