Skip to content

Commit 521fd61

Browse files
zx2c4dhowells
authored andcommitted
security/keys: rewrite big_key crypto to use library interface
A while back, I noticed that the crypto and crypto API usage in big_keys were entirely broken in multiple ways, so I rewrote it. Now, I'm rewriting it again, but this time using the simpler ChaCha20Poly1305 library function. This makes the file considerably more simple; the diffstat alone should justify this commit. It also should be faster, since it no longer requires a mutex around the "aead api object" (nor allocations), allowing us to encrypt multiple items in parallel. We also benefit from being able to pass any type of pointer, so we can get rid of the ridiculously complex custom page allocator that big_key really doesn't need. [DH: Change the select CRYPTO_LIB_CHACHA20POLY1305 to a depends on as select doesn't propagate and the build can end up with an =y dependending on some =m pieces. The depends on CRYPTO also had to be removed otherwise the configurator complains about a recursive dependency.] Cc: Andy Lutomirski <[email protected]> Cc: Greg KH <[email protected]> Cc: Linus Torvalds <[email protected]> Cc: [email protected] Reviewed-by: Eric Biggers <[email protected]> Signed-off-by: Jason A. Donenfeld <[email protected]> Signed-off-by: David Howells <[email protected]>
1 parent 2ce113f commit 521fd61

File tree

2 files changed

+35
-209
lines changed

2 files changed

+35
-209
lines changed

security/keys/Kconfig

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -60,9 +60,7 @@ config BIG_KEYS
6060
bool "Large payload keys"
6161
depends on KEYS
6262
depends on TMPFS
63-
select CRYPTO
64-
select CRYPTO_AES
65-
select CRYPTO_GCM
63+
depends on CRYPTO_LIB_CHACHA20POLY1305 = y
6664
help
6765
This option provides support for holding large keys within the kernel
6866
(for example Kerberos ticket caches). The data may be stored out to

security/keys/big_key.c

Lines changed: 34 additions & 206 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
// SPDX-License-Identifier: GPL-2.0-or-later
22
/* Large capacity key type
33
*
4-
* Copyright (C) 2017 Jason A. Donenfeld <[email protected]>. All Rights Reserved.
4+
* Copyright (C) 2017-2020 Jason A. Donenfeld <[email protected]>. All Rights Reserved.
55
* Copyright (C) 2013 Red Hat, Inc. All Rights Reserved.
66
* Written by David Howells ([email protected])
77
*/
@@ -12,20 +12,10 @@
1212
#include <linux/file.h>
1313
#include <linux/shmem_fs.h>
1414
#include <linux/err.h>
15-
#include <linux/scatterlist.h>
1615
#include <linux/random.h>
17-
#include <linux/vmalloc.h>
1816
#include <keys/user-type.h>
1917
#include <keys/big_key-type.h>
20-
#include <crypto/aead.h>
21-
#include <crypto/gcm.h>
22-
23-
struct big_key_buf {
24-
unsigned int nr_pages;
25-
void *virt;
26-
struct scatterlist *sg;
27-
struct page *pages[];
28-
};
18+
#include <crypto/chacha20poly1305.h>
2919

3020
/*
3121
* Layout of key payload words.
@@ -37,31 +27,13 @@ enum {
3727
big_key_len,
3828
};
3929

40-
/*
41-
* Crypto operation with big_key data
42-
*/
43-
enum big_key_op {
44-
BIG_KEY_ENC,
45-
BIG_KEY_DEC,
46-
};
47-
4830
/*
4931
* If the data is under this limit, there's no point creating a shm file to
5032
* hold it as the permanently resident metadata for the shmem fs will be at
5133
* least as large as the data.
5234
*/
5335
#define BIG_KEY_FILE_THRESHOLD (sizeof(struct inode) + sizeof(struct dentry))
5436

55-
/*
56-
* Key size for big_key data encryption
57-
*/
58-
#define ENC_KEY_SIZE 32
59-
60-
/*
61-
* Authentication tag length
62-
*/
63-
#define ENC_AUTHTAG_SIZE 16
64-
6537
/*
6638
* big_key defined keys take an arbitrary string as the description and an
6739
* arbitrary blob of data as the payload
@@ -75,136 +47,20 @@ struct key_type key_type_big_key = {
7547
.destroy = big_key_destroy,
7648
.describe = big_key_describe,
7749
.read = big_key_read,
78-
/* no ->update(); don't add it without changing big_key_crypt() nonce */
50+
/* no ->update(); don't add it without changing chacha20poly1305's nonce */
7951
};
8052

81-
/*
82-
* Crypto names for big_key data authenticated encryption
83-
*/
84-
static const char big_key_alg_name[] = "gcm(aes)";
85-
#define BIG_KEY_IV_SIZE GCM_AES_IV_SIZE
86-
87-
/*
88-
* Crypto algorithms for big_key data authenticated encryption
89-
*/
90-
static struct crypto_aead *big_key_aead;
91-
92-
/*
93-
* Since changing the key affects the entire object, we need a mutex.
94-
*/
95-
static DEFINE_MUTEX(big_key_aead_lock);
96-
97-
/*
98-
* Encrypt/decrypt big_key data
99-
*/
100-
static int big_key_crypt(enum big_key_op op, struct big_key_buf *buf, size_t datalen, u8 *key)
101-
{
102-
int ret;
103-
struct aead_request *aead_req;
104-
/* We always use a zero nonce. The reason we can get away with this is
105-
* because we're using a different randomly generated key for every
106-
* different encryption. Notably, too, key_type_big_key doesn't define
107-
* an .update function, so there's no chance we'll wind up reusing the
108-
* key to encrypt updated data. Simply put: one key, one encryption.
109-
*/
110-
u8 zero_nonce[BIG_KEY_IV_SIZE];
111-
112-
aead_req = aead_request_alloc(big_key_aead, GFP_KERNEL);
113-
if (!aead_req)
114-
return -ENOMEM;
115-
116-
memset(zero_nonce, 0, sizeof(zero_nonce));
117-
aead_request_set_crypt(aead_req, buf->sg, buf->sg, datalen, zero_nonce);
118-
aead_request_set_callback(aead_req, CRYPTO_TFM_REQ_MAY_SLEEP, NULL, NULL);
119-
aead_request_set_ad(aead_req, 0);
120-
121-
mutex_lock(&big_key_aead_lock);
122-
if (crypto_aead_setkey(big_key_aead, key, ENC_KEY_SIZE)) {
123-
ret = -EAGAIN;
124-
goto error;
125-
}
126-
if (op == BIG_KEY_ENC)
127-
ret = crypto_aead_encrypt(aead_req);
128-
else
129-
ret = crypto_aead_decrypt(aead_req);
130-
error:
131-
mutex_unlock(&big_key_aead_lock);
132-
aead_request_free(aead_req);
133-
return ret;
134-
}
135-
136-
/*
137-
* Free up the buffer.
138-
*/
139-
static void big_key_free_buffer(struct big_key_buf *buf)
140-
{
141-
unsigned int i;
142-
143-
if (buf->virt) {
144-
memset(buf->virt, 0, buf->nr_pages * PAGE_SIZE);
145-
vunmap(buf->virt);
146-
}
147-
148-
for (i = 0; i < buf->nr_pages; i++)
149-
if (buf->pages[i])
150-
__free_page(buf->pages[i]);
151-
152-
kfree(buf);
153-
}
154-
155-
/*
156-
* Allocate a buffer consisting of a set of pages with a virtual mapping
157-
* applied over them.
158-
*/
159-
static void *big_key_alloc_buffer(size_t len)
160-
{
161-
struct big_key_buf *buf;
162-
unsigned int npg = (len + PAGE_SIZE - 1) >> PAGE_SHIFT;
163-
unsigned int i, l;
164-
165-
buf = kzalloc(sizeof(struct big_key_buf) +
166-
sizeof(struct page) * npg +
167-
sizeof(struct scatterlist) * npg,
168-
GFP_KERNEL);
169-
if (!buf)
170-
return NULL;
171-
172-
buf->nr_pages = npg;
173-
buf->sg = (void *)(buf->pages + npg);
174-
sg_init_table(buf->sg, npg);
175-
176-
for (i = 0; i < buf->nr_pages; i++) {
177-
buf->pages[i] = alloc_page(GFP_KERNEL);
178-
if (!buf->pages[i])
179-
goto nomem;
180-
181-
l = min_t(size_t, len, PAGE_SIZE);
182-
sg_set_page(&buf->sg[i], buf->pages[i], l, 0);
183-
len -= l;
184-
}
185-
186-
buf->virt = vmap(buf->pages, buf->nr_pages, VM_MAP, PAGE_KERNEL);
187-
if (!buf->virt)
188-
goto nomem;
189-
190-
return buf;
191-
192-
nomem:
193-
big_key_free_buffer(buf);
194-
return NULL;
195-
}
196-
19753
/*
19854
* Preparse a big key
19955
*/
20056
int big_key_preparse(struct key_preparsed_payload *prep)
20157
{
202-
struct big_key_buf *buf;
20358
struct path *path = (struct path *)&prep->payload.data[big_key_path];
20459
struct file *file;
205-
u8 *enckey;
60+
u8 *buf, *enckey;
20661
ssize_t written;
207-
size_t datalen = prep->datalen, enclen = datalen + ENC_AUTHTAG_SIZE;
62+
size_t datalen = prep->datalen;
63+
size_t enclen = datalen + CHACHA20POLY1305_AUTHTAG_SIZE;
20864
int ret;
20965

21066
if (datalen <= 0 || datalen > 1024 * 1024 || !prep->data)
@@ -220,28 +76,28 @@ int big_key_preparse(struct key_preparsed_payload *prep)
22076
* to be swapped out if needed.
22177
*
22278
* File content is stored encrypted with randomly generated key.
79+
* Since the key is random for each file, we can set the nonce
80+
* to zero, provided we never define a ->update() call.
22381
*/
22482
loff_t pos = 0;
22583

226-
buf = big_key_alloc_buffer(enclen);
84+
buf = kvmalloc(enclen, GFP_KERNEL);
22785
if (!buf)
22886
return -ENOMEM;
229-
memcpy(buf->virt, prep->data, datalen);
23087

23188
/* generate random key */
232-
enckey = kmalloc(ENC_KEY_SIZE, GFP_KERNEL);
89+
enckey = kmalloc(CHACHA20POLY1305_KEY_SIZE, GFP_KERNEL);
23390
if (!enckey) {
23491
ret = -ENOMEM;
23592
goto error;
23693
}
237-
ret = get_random_bytes_wait(enckey, ENC_KEY_SIZE);
94+
ret = get_random_bytes_wait(enckey, CHACHA20POLY1305_KEY_SIZE);
23895
if (unlikely(ret))
23996
goto err_enckey;
24097

241-
/* encrypt aligned data */
242-
ret = big_key_crypt(BIG_KEY_ENC, buf, datalen, enckey);
243-
if (ret)
244-
goto err_enckey;
98+
/* encrypt data */
99+
chacha20poly1305_encrypt(buf, prep->data, datalen, NULL, 0,
100+
0, enckey);
245101

246102
/* save aligned data to file */
247103
file = shmem_kernel_file_setup("", enclen, 0);
@@ -250,11 +106,11 @@ int big_key_preparse(struct key_preparsed_payload *prep)
250106
goto err_enckey;
251107
}
252108

253-
written = kernel_write(file, buf->virt, enclen, &pos);
109+
written = kernel_write(file, buf, enclen, &pos);
254110
if (written != enclen) {
255111
ret = written;
256112
if (written >= 0)
257-
ret = -ENOMEM;
113+
ret = -EIO;
258114
goto err_fput;
259115
}
260116

@@ -265,7 +121,8 @@ int big_key_preparse(struct key_preparsed_payload *prep)
265121
*path = file->f_path;
266122
path_get(path);
267123
fput(file);
268-
big_key_free_buffer(buf);
124+
memzero_explicit(buf, enclen);
125+
kvfree(buf);
269126
} else {
270127
/* Just store the data in a buffer */
271128
void *data = kmalloc(datalen, GFP_KERNEL);
@@ -283,7 +140,8 @@ int big_key_preparse(struct key_preparsed_payload *prep)
283140
err_enckey:
284141
kzfree(enckey);
285142
error:
286-
big_key_free_buffer(buf);
143+
memzero_explicit(buf, enclen);
144+
kvfree(buf);
287145
return ret;
288146
}
289147

@@ -361,14 +219,13 @@ long big_key_read(const struct key *key, char *buffer, size_t buflen)
361219
return datalen;
362220

363221
if (datalen > BIG_KEY_FILE_THRESHOLD) {
364-
struct big_key_buf *buf;
365222
struct path *path = (struct path *)&key->payload.data[big_key_path];
366223
struct file *file;
367-
u8 *enckey = (u8 *)key->payload.data[big_key_data];
368-
size_t enclen = datalen + ENC_AUTHTAG_SIZE;
224+
u8 *buf, *enckey = (u8 *)key->payload.data[big_key_data];
225+
size_t enclen = datalen + CHACHA20POLY1305_AUTHTAG_SIZE;
369226
loff_t pos = 0;
370227

371-
buf = big_key_alloc_buffer(enclen);
228+
buf = kvmalloc(enclen, GFP_KERNEL);
372229
if (!buf)
373230
return -ENOMEM;
374231

@@ -379,25 +236,28 @@ long big_key_read(const struct key *key, char *buffer, size_t buflen)
379236
}
380237

381238
/* read file to kernel and decrypt */
382-
ret = kernel_read(file, buf->virt, enclen, &pos);
383-
if (ret >= 0 && ret != enclen) {
384-
ret = -EIO;
239+
ret = kernel_read(file, buf, enclen, &pos);
240+
if (ret != enclen) {
241+
if (ret >= 0)
242+
ret = -EIO;
385243
goto err_fput;
386244
}
387245

388-
ret = big_key_crypt(BIG_KEY_DEC, buf, enclen, enckey);
389-
if (ret)
246+
ret = chacha20poly1305_decrypt(buf, buf, enclen, NULL, 0, 0,
247+
enckey) ? 0 : -EBADMSG;
248+
if (unlikely(ret))
390249
goto err_fput;
391250

392251
ret = datalen;
393252

394253
/* copy out decrypted data */
395-
memcpy(buffer, buf->virt, datalen);
254+
memcpy(buffer, buf, datalen);
396255

397256
err_fput:
398257
fput(file);
399258
error:
400-
big_key_free_buffer(buf);
259+
memzero_explicit(buf, enclen);
260+
kvfree(buf);
401261
} else {
402262
ret = datalen;
403263
memcpy(buffer, key->payload.data[big_key_data], datalen);
@@ -411,39 +271,7 @@ long big_key_read(const struct key *key, char *buffer, size_t buflen)
411271
*/
412272
static int __init big_key_init(void)
413273
{
414-
int ret;
415-
416-
/* init block cipher */
417-
big_key_aead = crypto_alloc_aead(big_key_alg_name, 0, CRYPTO_ALG_ASYNC);
418-
if (IS_ERR(big_key_aead)) {
419-
ret = PTR_ERR(big_key_aead);
420-
pr_err("Can't alloc crypto: %d\n", ret);
421-
return ret;
422-
}
423-
424-
if (unlikely(crypto_aead_ivsize(big_key_aead) != BIG_KEY_IV_SIZE)) {
425-
WARN(1, "big key algorithm changed?");
426-
ret = -EINVAL;
427-
goto free_aead;
428-
}
429-
430-
ret = crypto_aead_setauthsize(big_key_aead, ENC_AUTHTAG_SIZE);
431-
if (ret < 0) {
432-
pr_err("Can't set crypto auth tag len: %d\n", ret);
433-
goto free_aead;
434-
}
435-
436-
ret = register_key_type(&key_type_big_key);
437-
if (ret < 0) {
438-
pr_err("Can't register type: %d\n", ret);
439-
goto free_aead;
440-
}
441-
442-
return 0;
443-
444-
free_aead:
445-
crypto_free_aead(big_key_aead);
446-
return ret;
274+
return register_key_type(&key_type_big_key);
447275
}
448276

449277
late_initcall(big_key_init);

0 commit comments

Comments
 (0)