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)
19static int
20hkdf_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
34static int
35hkdf_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;
69fail:
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
79static int
80kdf(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
114static int
115do_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;
151fail:
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
166int
167fido_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;
197fail:
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