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