1// SPDX-License-Identifier: GPL-2.0
2/*
3 * Copyright (c) 2020 Hannes Reinecke, SUSE Linux
4 */
5
6#include <linux/module.h>
7#include <linux/crc32.h>
8#include <linux/base64.h>
9#include <linux/prandom.h>
10#include <linux/scatterlist.h>
11#include <asm/unaligned.h>
12#include <crypto/hash.h>
13#include <crypto/dh.h>
14#include <linux/nvme.h>
15#include <linux/nvme-auth.h>
16
17static u32 nvme_dhchap_seqnum;
18static DEFINE_MUTEX(nvme_dhchap_mutex);
19
20u32 nvme_auth_get_seqnum(void)
21{
22	u32 seqnum;
23
24	mutex_lock(&nvme_dhchap_mutex);
25	if (!nvme_dhchap_seqnum)
26		nvme_dhchap_seqnum = get_random_u32();
27	else {
28		nvme_dhchap_seqnum++;
29		if (!nvme_dhchap_seqnum)
30			nvme_dhchap_seqnum++;
31	}
32	seqnum = nvme_dhchap_seqnum;
33	mutex_unlock(&nvme_dhchap_mutex);
34	return seqnum;
35}
36EXPORT_SYMBOL_GPL(nvme_auth_get_seqnum);
37
38static struct nvme_auth_dhgroup_map {
39	const char name[16];
40	const char kpp[16];
41} dhgroup_map[] = {
42	[NVME_AUTH_DHGROUP_NULL] = {
43		.name = "null", .kpp = "null" },
44	[NVME_AUTH_DHGROUP_2048] = {
45		.name = "ffdhe2048", .kpp = "ffdhe2048(dh)" },
46	[NVME_AUTH_DHGROUP_3072] = {
47		.name = "ffdhe3072", .kpp = "ffdhe3072(dh)" },
48	[NVME_AUTH_DHGROUP_4096] = {
49		.name = "ffdhe4096", .kpp = "ffdhe4096(dh)" },
50	[NVME_AUTH_DHGROUP_6144] = {
51		.name = "ffdhe6144", .kpp = "ffdhe6144(dh)" },
52	[NVME_AUTH_DHGROUP_8192] = {
53		.name = "ffdhe8192", .kpp = "ffdhe8192(dh)" },
54};
55
56const char *nvme_auth_dhgroup_name(u8 dhgroup_id)
57{
58	if (dhgroup_id >= ARRAY_SIZE(dhgroup_map))
59		return NULL;
60	return dhgroup_map[dhgroup_id].name;
61}
62EXPORT_SYMBOL_GPL(nvme_auth_dhgroup_name);
63
64const char *nvme_auth_dhgroup_kpp(u8 dhgroup_id)
65{
66	if (dhgroup_id >= ARRAY_SIZE(dhgroup_map))
67		return NULL;
68	return dhgroup_map[dhgroup_id].kpp;
69}
70EXPORT_SYMBOL_GPL(nvme_auth_dhgroup_kpp);
71
72u8 nvme_auth_dhgroup_id(const char *dhgroup_name)
73{
74	int i;
75
76	if (!dhgroup_name || !strlen(dhgroup_name))
77		return NVME_AUTH_DHGROUP_INVALID;
78	for (i = 0; i < ARRAY_SIZE(dhgroup_map); i++) {
79		if (!strlen(dhgroup_map[i].name))
80			continue;
81		if (!strncmp(dhgroup_map[i].name, dhgroup_name,
82			     strlen(dhgroup_map[i].name)))
83			return i;
84	}
85	return NVME_AUTH_DHGROUP_INVALID;
86}
87EXPORT_SYMBOL_GPL(nvme_auth_dhgroup_id);
88
89static struct nvme_dhchap_hash_map {
90	int len;
91	const char hmac[15];
92	const char digest[8];
93} hash_map[] = {
94	[NVME_AUTH_HASH_SHA256] = {
95		.len = 32,
96		.hmac = "hmac(sha256)",
97		.digest = "sha256",
98	},
99	[NVME_AUTH_HASH_SHA384] = {
100		.len = 48,
101		.hmac = "hmac(sha384)",
102		.digest = "sha384",
103	},
104	[NVME_AUTH_HASH_SHA512] = {
105		.len = 64,
106		.hmac = "hmac(sha512)",
107		.digest = "sha512",
108	},
109};
110
111const char *nvme_auth_hmac_name(u8 hmac_id)
112{
113	if (hmac_id >= ARRAY_SIZE(hash_map))
114		return NULL;
115	return hash_map[hmac_id].hmac;
116}
117EXPORT_SYMBOL_GPL(nvme_auth_hmac_name);
118
119const char *nvme_auth_digest_name(u8 hmac_id)
120{
121	if (hmac_id >= ARRAY_SIZE(hash_map))
122		return NULL;
123	return hash_map[hmac_id].digest;
124}
125EXPORT_SYMBOL_GPL(nvme_auth_digest_name);
126
127u8 nvme_auth_hmac_id(const char *hmac_name)
128{
129	int i;
130
131	if (!hmac_name || !strlen(hmac_name))
132		return NVME_AUTH_HASH_INVALID;
133
134	for (i = 0; i < ARRAY_SIZE(hash_map); i++) {
135		if (!strlen(hash_map[i].hmac))
136			continue;
137		if (!strncmp(hash_map[i].hmac, hmac_name,
138			     strlen(hash_map[i].hmac)))
139			return i;
140	}
141	return NVME_AUTH_HASH_INVALID;
142}
143EXPORT_SYMBOL_GPL(nvme_auth_hmac_id);
144
145size_t nvme_auth_hmac_hash_len(u8 hmac_id)
146{
147	if (hmac_id >= ARRAY_SIZE(hash_map))
148		return 0;
149	return hash_map[hmac_id].len;
150}
151EXPORT_SYMBOL_GPL(nvme_auth_hmac_hash_len);
152
153u32 nvme_auth_key_struct_size(u32 key_len)
154{
155	struct nvme_dhchap_key key;
156
157	return struct_size(&key, key, key_len);
158}
159EXPORT_SYMBOL_GPL(nvme_auth_key_struct_size);
160
161struct nvme_dhchap_key *nvme_auth_extract_key(unsigned char *secret,
162					      u8 key_hash)
163{
164	struct nvme_dhchap_key *key;
165	unsigned char *p;
166	u32 crc;
167	int ret, key_len;
168	size_t allocated_len = strlen(secret);
169
170	/* Secret might be affixed with a ':' */
171	p = strrchr(secret, ':');
172	if (p)
173		allocated_len = p - secret;
174	key = nvme_auth_alloc_key(allocated_len, 0);
175	if (!key)
176		return ERR_PTR(-ENOMEM);
177
178	key_len = base64_decode(secret, allocated_len, key->key);
179	if (key_len < 0) {
180		pr_debug("base64 key decoding error %d\n",
181			 key_len);
182		ret = key_len;
183		goto out_free_secret;
184	}
185
186	if (key_len != 36 && key_len != 52 &&
187	    key_len != 68) {
188		pr_err("Invalid key len %d\n", key_len);
189		ret = -EINVAL;
190		goto out_free_secret;
191	}
192
193	/* The last four bytes is the CRC in little-endian format */
194	key_len -= 4;
195	/*
196	 * The linux implementation doesn't do pre- and post-increments,
197	 * so we have to do it manually.
198	 */
199	crc = ~crc32(~0, key->key, key_len);
200
201	if (get_unaligned_le32(key->key + key_len) != crc) {
202		pr_err("key crc mismatch (key %08x, crc %08x)\n",
203		       get_unaligned_le32(key->key + key_len), crc);
204		ret = -EKEYREJECTED;
205		goto out_free_secret;
206	}
207	key->len = key_len;
208	key->hash = key_hash;
209	return key;
210out_free_secret:
211	nvme_auth_free_key(key);
212	return ERR_PTR(ret);
213}
214EXPORT_SYMBOL_GPL(nvme_auth_extract_key);
215
216struct nvme_dhchap_key *nvme_auth_alloc_key(u32 len, u8 hash)
217{
218	u32 num_bytes = nvme_auth_key_struct_size(len);
219	struct nvme_dhchap_key *key = kzalloc(num_bytes, GFP_KERNEL);
220
221	if (key) {
222		key->len = len;
223		key->hash = hash;
224	}
225	return key;
226}
227EXPORT_SYMBOL_GPL(nvme_auth_alloc_key);
228
229void nvme_auth_free_key(struct nvme_dhchap_key *key)
230{
231	if (!key)
232		return;
233	kfree_sensitive(key);
234}
235EXPORT_SYMBOL_GPL(nvme_auth_free_key);
236
237struct nvme_dhchap_key *nvme_auth_transform_key(
238		struct nvme_dhchap_key *key, char *nqn)
239{
240	const char *hmac_name;
241	struct crypto_shash *key_tfm;
242	struct shash_desc *shash;
243	struct nvme_dhchap_key *transformed_key;
244	int ret, key_len;
245
246	if (!key) {
247		pr_warn("No key specified\n");
248		return ERR_PTR(-ENOKEY);
249	}
250	if (key->hash == 0) {
251		key_len = nvme_auth_key_struct_size(key->len);
252		transformed_key = kmemdup(key, key_len, GFP_KERNEL);
253		if (!transformed_key)
254			return ERR_PTR(-ENOMEM);
255		return transformed_key;
256	}
257	hmac_name = nvme_auth_hmac_name(key->hash);
258	if (!hmac_name) {
259		pr_warn("Invalid key hash id %d\n", key->hash);
260		return ERR_PTR(-EINVAL);
261	}
262
263	key_tfm = crypto_alloc_shash(hmac_name, 0, 0);
264	if (IS_ERR(key_tfm))
265		return ERR_CAST(key_tfm);
266
267	shash = kmalloc(sizeof(struct shash_desc) +
268			crypto_shash_descsize(key_tfm),
269			GFP_KERNEL);
270	if (!shash) {
271		ret = -ENOMEM;
272		goto out_free_key;
273	}
274
275	key_len = crypto_shash_digestsize(key_tfm);
276	transformed_key = nvme_auth_alloc_key(key_len, key->hash);
277	if (!transformed_key) {
278		ret = -ENOMEM;
279		goto out_free_shash;
280	}
281
282	shash->tfm = key_tfm;
283	ret = crypto_shash_setkey(key_tfm, key->key, key->len);
284	if (ret < 0)
285		goto out_free_transformed_key;
286	ret = crypto_shash_init(shash);
287	if (ret < 0)
288		goto out_free_transformed_key;
289	ret = crypto_shash_update(shash, nqn, strlen(nqn));
290	if (ret < 0)
291		goto out_free_transformed_key;
292	ret = crypto_shash_update(shash, "NVMe-over-Fabrics", 17);
293	if (ret < 0)
294		goto out_free_transformed_key;
295	ret = crypto_shash_final(shash, transformed_key->key);
296	if (ret < 0)
297		goto out_free_transformed_key;
298
299	kfree(shash);
300	crypto_free_shash(key_tfm);
301
302	return transformed_key;
303
304out_free_transformed_key:
305	nvme_auth_free_key(transformed_key);
306out_free_shash:
307	kfree(shash);
308out_free_key:
309	crypto_free_shash(key_tfm);
310
311	return ERR_PTR(ret);
312}
313EXPORT_SYMBOL_GPL(nvme_auth_transform_key);
314
315static int nvme_auth_hash_skey(int hmac_id, u8 *skey, size_t skey_len, u8 *hkey)
316{
317	const char *digest_name;
318	struct crypto_shash *tfm;
319	int ret;
320
321	digest_name = nvme_auth_digest_name(hmac_id);
322	if (!digest_name) {
323		pr_debug("%s: failed to get digest for %d\n", __func__,
324			 hmac_id);
325		return -EINVAL;
326	}
327	tfm = crypto_alloc_shash(digest_name, 0, 0);
328	if (IS_ERR(tfm))
329		return -ENOMEM;
330
331	ret = crypto_shash_tfm_digest(tfm, skey, skey_len, hkey);
332	if (ret < 0)
333		pr_debug("%s: Failed to hash digest len %zu\n", __func__,
334			 skey_len);
335
336	crypto_free_shash(tfm);
337	return ret;
338}
339
340int nvme_auth_augmented_challenge(u8 hmac_id, u8 *skey, size_t skey_len,
341		u8 *challenge, u8 *aug, size_t hlen)
342{
343	struct crypto_shash *tfm;
344	u8 *hashed_key;
345	const char *hmac_name;
346	int ret;
347
348	hashed_key = kmalloc(hlen, GFP_KERNEL);
349	if (!hashed_key)
350		return -ENOMEM;
351
352	ret = nvme_auth_hash_skey(hmac_id, skey,
353				  skey_len, hashed_key);
354	if (ret < 0)
355		goto out_free_key;
356
357	hmac_name = nvme_auth_hmac_name(hmac_id);
358	if (!hmac_name) {
359		pr_warn("%s: invalid hash algorithm %d\n",
360			__func__, hmac_id);
361		ret = -EINVAL;
362		goto out_free_key;
363	}
364
365	tfm = crypto_alloc_shash(hmac_name, 0, 0);
366	if (IS_ERR(tfm)) {
367		ret = PTR_ERR(tfm);
368		goto out_free_key;
369	}
370
371	ret = crypto_shash_setkey(tfm, hashed_key, hlen);
372	if (ret)
373		goto out_free_hash;
374
375	ret = crypto_shash_tfm_digest(tfm, challenge, hlen, aug);
376out_free_hash:
377	crypto_free_shash(tfm);
378out_free_key:
379	kfree_sensitive(hashed_key);
380	return ret;
381}
382EXPORT_SYMBOL_GPL(nvme_auth_augmented_challenge);
383
384int nvme_auth_gen_privkey(struct crypto_kpp *dh_tfm, u8 dh_gid)
385{
386	int ret;
387
388	ret = crypto_kpp_set_secret(dh_tfm, NULL, 0);
389	if (ret)
390		pr_debug("failed to set private key, error %d\n", ret);
391
392	return ret;
393}
394EXPORT_SYMBOL_GPL(nvme_auth_gen_privkey);
395
396int nvme_auth_gen_pubkey(struct crypto_kpp *dh_tfm,
397		u8 *host_key, size_t host_key_len)
398{
399	struct kpp_request *req;
400	struct crypto_wait wait;
401	struct scatterlist dst;
402	int ret;
403
404	req = kpp_request_alloc(dh_tfm, GFP_KERNEL);
405	if (!req)
406		return -ENOMEM;
407
408	crypto_init_wait(&wait);
409	kpp_request_set_input(req, NULL, 0);
410	sg_init_one(&dst, host_key, host_key_len);
411	kpp_request_set_output(req, &dst, host_key_len);
412	kpp_request_set_callback(req, CRYPTO_TFM_REQ_MAY_BACKLOG,
413				 crypto_req_done, &wait);
414
415	ret = crypto_wait_req(crypto_kpp_generate_public_key(req), &wait);
416	kpp_request_free(req);
417	return ret;
418}
419EXPORT_SYMBOL_GPL(nvme_auth_gen_pubkey);
420
421int nvme_auth_gen_shared_secret(struct crypto_kpp *dh_tfm,
422		u8 *ctrl_key, size_t ctrl_key_len,
423		u8 *sess_key, size_t sess_key_len)
424{
425	struct kpp_request *req;
426	struct crypto_wait wait;
427	struct scatterlist src, dst;
428	int ret;
429
430	req = kpp_request_alloc(dh_tfm, GFP_KERNEL);
431	if (!req)
432		return -ENOMEM;
433
434	crypto_init_wait(&wait);
435	sg_init_one(&src, ctrl_key, ctrl_key_len);
436	kpp_request_set_input(req, &src, ctrl_key_len);
437	sg_init_one(&dst, sess_key, sess_key_len);
438	kpp_request_set_output(req, &dst, sess_key_len);
439	kpp_request_set_callback(req, CRYPTO_TFM_REQ_MAY_BACKLOG,
440				 crypto_req_done, &wait);
441
442	ret = crypto_wait_req(crypto_kpp_compute_shared_secret(req), &wait);
443
444	kpp_request_free(req);
445	return ret;
446}
447EXPORT_SYMBOL_GPL(nvme_auth_gen_shared_secret);
448
449int nvme_auth_generate_key(u8 *secret, struct nvme_dhchap_key **ret_key)
450{
451	struct nvme_dhchap_key *key;
452	u8 key_hash;
453
454	if (!secret) {
455		*ret_key = NULL;
456		return 0;
457	}
458
459	if (sscanf(secret, "DHHC-1:%hhd:%*s:", &key_hash) != 1)
460		return -EINVAL;
461
462	/* Pass in the secret without the 'DHHC-1:XX:' prefix */
463	key = nvme_auth_extract_key(secret + 10, key_hash);
464	if (IS_ERR(key)) {
465		*ret_key = NULL;
466		return PTR_ERR(key);
467	}
468
469	*ret_key = key;
470	return 0;
471}
472EXPORT_SYMBOL_GPL(nvme_auth_generate_key);
473
474MODULE_DESCRIPTION("NVMe Authentication framework");
475MODULE_LICENSE("GPL v2");
476