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