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(lock: &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(len: allocated_len, hash: 0);
175 if (!key)
176 return ERR_PTR(error: -ENOMEM);
177
178 key_len = base64_decode(src: secret, len: allocated_len, dst: 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(p: 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(error: 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(size: 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(objp: 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(error: -ENOKEY);
249 }
250 if (key->hash == 0) {
251 key_len = nvme_auth_key_struct_size(key->len);
252 transformed_key = kmemdup(p: key, size: key_len, GFP_KERNEL);
253 if (!transformed_key)
254 return ERR_PTR(error: -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(error: -EINVAL);
261 }
262
263 key_tfm = crypto_alloc_shash(alg_name: hmac_name, type: 0, mask: 0);
264 if (IS_ERR(ptr: key_tfm))
265 return ERR_CAST(ptr: key_tfm);
266
267 shash = kmalloc(size: sizeof(struct shash_desc) +
268 crypto_shash_descsize(tfm: key_tfm),
269 GFP_KERNEL);
270 if (!shash) {
271 ret = -ENOMEM;
272 goto out_free_key;
273 }
274
275 key_len = crypto_shash_digestsize(tfm: 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(tfm: key_tfm, key: key->key, keylen: key->len);
284 if (ret < 0)
285 goto out_free_transformed_key;
286 ret = crypto_shash_init(desc: shash);
287 if (ret < 0)
288 goto out_free_transformed_key;
289 ret = crypto_shash_update(desc: shash, data: nqn, strlen(nqn));
290 if (ret < 0)
291 goto out_free_transformed_key;
292 ret = crypto_shash_update(desc: shash, data: "NVMe-over-Fabrics", len: 17);
293 if (ret < 0)
294 goto out_free_transformed_key;
295 ret = crypto_shash_final(desc: shash, out: transformed_key->key);
296 if (ret < 0)
297 goto out_free_transformed_key;
298
299 kfree(objp: shash);
300 crypto_free_shash(tfm: key_tfm);
301
302 return transformed_key;
303
304out_free_transformed_key:
305 nvme_auth_free_key(transformed_key);
306out_free_shash:
307 kfree(objp: shash);
308out_free_key:
309 crypto_free_shash(tfm: key_tfm);
310
311 return ERR_PTR(error: 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(alg_name: digest_name, type: 0, mask: 0);
328 if (IS_ERR(ptr: tfm))
329 return -ENOMEM;
330
331 ret = crypto_shash_tfm_digest(tfm, data: skey, len: skey_len, out: 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(size: hlen, GFP_KERNEL);
349 if (!hashed_key)
350 return -ENOMEM;
351
352 ret = nvme_auth_hash_skey(hmac_id, skey,
353 skey_len, hkey: 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(alg_name: hmac_name, type: 0, mask: 0);
366 if (IS_ERR(ptr: tfm)) {
367 ret = PTR_ERR(ptr: tfm);
368 goto out_free_key;
369 }
370
371 ret = crypto_shash_setkey(tfm, key: hashed_key, keylen: hlen);
372 if (ret)
373 goto out_free_hash;
374
375 ret = crypto_shash_tfm_digest(tfm, data: challenge, len: hlen, out: aug);
376out_free_hash:
377 crypto_free_shash(tfm);
378out_free_key:
379 kfree_sensitive(objp: 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(tfm: dh_tfm, NULL, len: 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(tfm: dh_tfm, GFP_KERNEL);
405 if (!req)
406 return -ENOMEM;
407
408 crypto_init_wait(wait: &wait);
409 kpp_request_set_input(req, NULL, input_len: 0);
410 sg_init_one(&dst, host_key, host_key_len);
411 kpp_request_set_output(req, output: &dst, output_len: host_key_len);
412 kpp_request_set_callback(req, CRYPTO_TFM_REQ_MAY_BACKLOG,
413 cmpl: crypto_req_done, data: &wait);
414
415 ret = crypto_wait_req(err: crypto_kpp_generate_public_key(req), wait: &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(tfm: dh_tfm, GFP_KERNEL);
431 if (!req)
432 return -ENOMEM;
433
434 crypto_init_wait(wait: &wait);
435 sg_init_one(&src, ctrl_key, ctrl_key_len);
436 kpp_request_set_input(req, input: &src, input_len: ctrl_key_len);
437 sg_init_one(&dst, sess_key, sess_key_len);
438 kpp_request_set_output(req, output: &dst, output_len: sess_key_len);
439 kpp_request_set_callback(req, CRYPTO_TFM_REQ_MAY_BACKLOG,
440 cmpl: crypto_req_done, data: &wait);
441
442 ret = crypto_wait_req(err: crypto_kpp_compute_shared_secret(req), wait: &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(ptr: key)) {
465 *ret_key = NULL;
466 return PTR_ERR(ptr: 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

source code of linux/drivers/nvme/common/auth.c