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 <linux/unaligned.h>
12 #include <crypto/hash.h>
13 #include <crypto/dh.h>
14 #include <linux/nvme.h>
15 #include <linux/nvme-auth.h>
16
17 static u32 nvme_dhchap_seqnum;
18 static DEFINE_MUTEX(nvme_dhchap_mutex);
19
nvme_auth_get_seqnum(void)20 u32 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 }
36 EXPORT_SYMBOL_GPL(nvme_auth_get_seqnum);
37
38 static 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
nvme_auth_dhgroup_name(u8 dhgroup_id)56 const 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 }
62 EXPORT_SYMBOL_GPL(nvme_auth_dhgroup_name);
63
nvme_auth_dhgroup_kpp(u8 dhgroup_id)64 const 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 }
70 EXPORT_SYMBOL_GPL(nvme_auth_dhgroup_kpp);
71
nvme_auth_dhgroup_id(const char * dhgroup_name)72 u8 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 }
87 EXPORT_SYMBOL_GPL(nvme_auth_dhgroup_id);
88
89 static 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
nvme_auth_hmac_name(u8 hmac_id)111 const 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 }
117 EXPORT_SYMBOL_GPL(nvme_auth_hmac_name);
118
nvme_auth_digest_name(u8 hmac_id)119 const 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 }
125 EXPORT_SYMBOL_GPL(nvme_auth_digest_name);
126
nvme_auth_hmac_id(const char * hmac_name)127 u8 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 }
143 EXPORT_SYMBOL_GPL(nvme_auth_hmac_id);
144
nvme_auth_hmac_hash_len(u8 hmac_id)145 size_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 }
151 EXPORT_SYMBOL_GPL(nvme_auth_hmac_hash_len);
152
nvme_auth_key_struct_size(u32 key_len)153 u32 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 }
159 EXPORT_SYMBOL_GPL(nvme_auth_key_struct_size);
160
nvme_auth_extract_key(unsigned char * secret,u8 key_hash)161 struct 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;
210 out_free_secret:
211 nvme_auth_free_key(key);
212 return ERR_PTR(ret);
213 }
214 EXPORT_SYMBOL_GPL(nvme_auth_extract_key);
215
nvme_auth_alloc_key(u32 len,u8 hash)216 struct 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 }
227 EXPORT_SYMBOL_GPL(nvme_auth_alloc_key);
228
nvme_auth_free_key(struct nvme_dhchap_key * key)229 void nvme_auth_free_key(struct nvme_dhchap_key *key)
230 {
231 if (!key)
232 return;
233 kfree_sensitive(key);
234 }
235 EXPORT_SYMBOL_GPL(nvme_auth_free_key);
236
nvme_auth_transform_key(struct nvme_dhchap_key * key,char * nqn)237 struct 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
304 out_free_transformed_key:
305 nvme_auth_free_key(transformed_key);
306 out_free_shash:
307 kfree(shash);
308 out_free_key:
309 crypto_free_shash(key_tfm);
310
311 return ERR_PTR(ret);
312 }
313 EXPORT_SYMBOL_GPL(nvme_auth_transform_key);
314
nvme_auth_hash_skey(int hmac_id,u8 * skey,size_t skey_len,u8 * hkey)315 static 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
nvme_auth_augmented_challenge(u8 hmac_id,u8 * skey,size_t skey_len,u8 * challenge,u8 * aug,size_t hlen)340 int 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);
376 out_free_hash:
377 crypto_free_shash(tfm);
378 out_free_key:
379 kfree_sensitive(hashed_key);
380 return ret;
381 }
382 EXPORT_SYMBOL_GPL(nvme_auth_augmented_challenge);
383
nvme_auth_gen_privkey(struct crypto_kpp * dh_tfm,u8 dh_gid)384 int 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 }
394 EXPORT_SYMBOL_GPL(nvme_auth_gen_privkey);
395
nvme_auth_gen_pubkey(struct crypto_kpp * dh_tfm,u8 * host_key,size_t host_key_len)396 int 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 }
419 EXPORT_SYMBOL_GPL(nvme_auth_gen_pubkey);
420
nvme_auth_gen_shared_secret(struct crypto_kpp * dh_tfm,u8 * ctrl_key,size_t ctrl_key_len,u8 * sess_key,size_t sess_key_len)421 int 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 }
447 EXPORT_SYMBOL_GPL(nvme_auth_gen_shared_secret);
448
nvme_auth_generate_key(u8 * secret,struct nvme_dhchap_key ** ret_key)449 int 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 }
472 EXPORT_SYMBOL_GPL(nvme_auth_generate_key);
473
474 MODULE_DESCRIPTION("NVMe Authentication framework");
475 MODULE_LICENSE("GPL v2");
476