bcachefs: use library APIs for ChaCha20 and Poly1305
authorEric Biggers <ebiggers@google.com>
Wed, 2 Apr 2025 04:33:33 +0000 (21:33 -0700)
committerKent Overstreet <kent.overstreet@linux.dev>
Sun, 6 Apr 2025 23:33:53 +0000 (19:33 -0400)
Just use the ChaCha20 and Poly1305 libraries instead of the clunky
crypto API.  This is much simpler.  It is also slightly faster, since
the libraries provide more direct access to the same
architecture-optimized ChaCha20 and Poly1305 code.

I've tested that existing encrypted bcachefs filesystems can be continue
to be accessed with this patch applied.

Reviewed-by: Ard Biesheuvel <ardb@kernel.org>
Signed-off-by: Eric Biggers <ebiggers@google.com>
Signed-off-by: Kent Overstreet <kent.overstreet@linux.dev>
fs/bcachefs/Kconfig
fs/bcachefs/bcachefs.h
fs/bcachefs/btree_node_scan.c
fs/bcachefs/checksum.c
fs/bcachefs/checksum.h
fs/bcachefs/io_read.c
fs/bcachefs/super.c

index bf1c94e51dd064b4af05f417f5a8640f09d72c1a..508c0cfdf7c96d6ded492656ebb7171bdaefec9d 100644 (file)
@@ -15,10 +15,9 @@ config BCACHEFS_FS
        select ZLIB_INFLATE
        select ZSTD_COMPRESS
        select ZSTD_DECOMPRESS
-       select CRYPTO
        select CRYPTO_LIB_SHA256
-       select CRYPTO_CHACHA20
-       select CRYPTO_POLY1305
+       select CRYPTO_LIB_CHACHA
+       select CRYPTO_LIB_POLY1305
        select KEYS
        select RAID6_PQ
        select XOR_BLOCKS
index 5d9f208a1bb744a7a53481d99aee2eaf71cff026..5cb0fc384ac020290493ed6f47cd5a4b03c11d5f 100644 (file)
@@ -981,8 +981,8 @@ struct bch_fs {
        mempool_t               compress_workspace[BCH_COMPRESSION_OPT_NR];
        size_t                  zstd_workspace_size;
 
-       struct crypto_sync_skcipher *chacha20;
-       struct crypto_shash     *poly1305;
+       struct bch_key          chacha20_key;
+       bool                    chacha20_key_set;
 
        atomic64_t              key_version;
 
index 8c9fdb7263fea537c0917406683bbe0aa2538053..f520fbcd0455406bb818603a6b485d56c272a3a4 100644 (file)
@@ -183,7 +183,7 @@ static void try_read_btree_node(struct find_btree_nodes *f, struct bch_dev *ca,
                return;
 
        if (bch2_csum_type_is_encryption(BSET_CSUM_TYPE(&bn->keys))) {
-               if (!c->chacha20)
+               if (!c->chacha20_key_set)
                        return;
 
                struct nonce nonce = btree_nonce(&bn->keys, 0);
index 3726689093e3018fa9b8f9cd900d7e16a61612ea..d0a34a097b809ec2475048790778926543e1d7ea 100644 (file)
@@ -7,17 +7,12 @@
 #include "super-io.h"
 
 #include <linux/crc32c.h>
-#include <linux/crypto.h>
 #include <linux/xxhash.h>
 #include <linux/key.h>
 #include <linux/random.h>
 #include <linux/ratelimit.h>
-#include <linux/scatterlist.h>
-#include <crypto/algapi.h>
 #include <crypto/chacha.h>
-#include <crypto/hash.h>
 #include <crypto/poly1305.h>
-#include <crypto/skcipher.h>
 #include <keys/user-type.h>
 
 /*
@@ -96,116 +91,40 @@ static void bch2_checksum_update(struct bch2_checksum_state *state, const void *
        }
 }
 
-static inline int do_encrypt_sg(struct crypto_sync_skcipher *tfm,
-                               struct nonce nonce,
-                               struct scatterlist *sg, size_t len)
+static void bch2_chacha20_init(u32 state[CHACHA_STATE_WORDS],
+                              const struct bch_key *key, struct nonce nonce)
 {
-       SYNC_SKCIPHER_REQUEST_ON_STACK(req, tfm);
+       u32 key_words[CHACHA_KEY_SIZE / sizeof(u32)];
 
-       skcipher_request_set_sync_tfm(req, tfm);
-       skcipher_request_set_callback(req, 0, NULL, NULL);
-       skcipher_request_set_crypt(req, sg, sg, len, nonce.d);
+       BUILD_BUG_ON(sizeof(key_words) != sizeof(*key));
+       memcpy(key_words, key, sizeof(key_words));
+       le32_to_cpu_array(key_words, ARRAY_SIZE(key_words));
 
-       int ret = crypto_skcipher_encrypt(req);
-       if (ret)
-               pr_err("got error %i from crypto_skcipher_encrypt()", ret);
-
-       return ret;
-}
-
-static inline int do_encrypt(struct crypto_sync_skcipher *tfm,
-                             struct nonce nonce,
-                             void *buf, size_t len)
-{
-       if (!is_vmalloc_addr(buf)) {
-               struct scatterlist sg = {};
-
-               sg_mark_end(&sg);
-               sg_set_page(&sg, virt_to_page(buf), len, offset_in_page(buf));
-               return do_encrypt_sg(tfm, nonce, &sg, len);
-       } else {
-               DARRAY_PREALLOCATED(struct scatterlist, 4) sgl;
-               size_t sgl_len = 0;
-               int ret;
-
-               darray_init(&sgl);
-
-               while (len) {
-                       unsigned offset = offset_in_page(buf);
-                       struct scatterlist sg = {
-                               .page_link      = (unsigned long) vmalloc_to_page(buf),
-                               .offset         = offset,
-                               .length         = min(len, PAGE_SIZE - offset),
-                       };
+       BUILD_BUG_ON(sizeof(nonce) != CHACHA_IV_SIZE);
+       chacha_init(state, key_words, (const u8 *)nonce.d);
 
-                       if (darray_push(&sgl, sg)) {
-                               sg_mark_end(&darray_last(sgl));
-                               ret = do_encrypt_sg(tfm, nonce, sgl.data, sgl_len);
-                               if (ret)
-                                       goto err;
-
-                               nonce = nonce_add(nonce, sgl_len);
-                               sgl_len = 0;
-                               sgl.nr = 0;
-                               BUG_ON(darray_push(&sgl, sg));
-                       }
-
-                       buf += sg.length;
-                       len -= sg.length;
-                       sgl_len += sg.length;
-               }
-
-               sg_mark_end(&darray_last(sgl));
-               ret = do_encrypt_sg(tfm, nonce, sgl.data, sgl_len);
-err:
-               darray_exit(&sgl);
-               return ret;
-       }
+       memzero_explicit(key_words, sizeof(key_words));
 }
 
-int bch2_chacha_encrypt_key(struct bch_key *key, struct nonce nonce,
-                           void *buf, size_t len)
+static void bch2_chacha20(const struct bch_key *key, struct nonce nonce,
+                         void *data, size_t len)
 {
-       struct crypto_sync_skcipher *chacha20 =
-               crypto_alloc_sync_skcipher("chacha20", 0, 0);
-       int ret;
-
-       ret = PTR_ERR_OR_ZERO(chacha20);
-       if (ret) {
-               pr_err("error requesting chacha20 cipher: %s", bch2_err_str(ret));
-               return ret;
-       }
-
-       ret = crypto_skcipher_setkey(&chacha20->base,
-                                    (void *) key, sizeof(*key));
-       if (ret) {
-               pr_err("error from crypto_skcipher_setkey(): %s", bch2_err_str(ret));
-               goto err;
-       }
+       u32 state[CHACHA_STATE_WORDS];
 
-       ret = do_encrypt(chacha20, nonce, buf, len);
-err:
-       crypto_free_sync_skcipher(chacha20);
-       return ret;
+       bch2_chacha20_init(state, key, nonce);
+       chacha20_crypt(state, data, data, len);
+       memzero_explicit(state, sizeof(state));
 }
 
-static int gen_poly_key(struct bch_fs *c, struct shash_desc *desc,
-                       struct nonce nonce)
+static void bch2_poly1305_init(struct poly1305_desc_ctx *desc,
+                              struct bch_fs *c, struct nonce nonce)
 {
-       u8 key[POLY1305_KEY_SIZE];
-       int ret;
+       u8 key[POLY1305_KEY_SIZE] = { 0 };
 
        nonce.d[3] ^= BCH_NONCE_POLY;
 
-       memset(key, 0, sizeof(key));
-       ret = do_encrypt(c->chacha20, nonce, key, sizeof(key));
-       if (ret)
-               return ret;
-
-       desc->tfm = c->poly1305;
-       crypto_shash_init(desc);
-       crypto_shash_update(desc, key, sizeof(key));
-       return 0;
+       bch2_chacha20(&c->chacha20_key, nonce, key, sizeof(key));
+       poly1305_init(desc, key);
 }
 
 struct bch_csum bch2_checksum(struct bch_fs *c, unsigned type,
@@ -230,14 +149,13 @@ struct bch_csum bch2_checksum(struct bch_fs *c, unsigned type,
 
        case BCH_CSUM_chacha20_poly1305_80:
        case BCH_CSUM_chacha20_poly1305_128: {
-               SHASH_DESC_ON_STACK(desc, c->poly1305);
+               struct poly1305_desc_ctx dctx;
                u8 digest[POLY1305_DIGEST_SIZE];
                struct bch_csum ret = { 0 };
 
-               gen_poly_key(c, desc, nonce);
-
-               crypto_shash_update(desc, data, len);
-               crypto_shash_final(desc, digest);
+               bch2_poly1305_init(&dctx, c, nonce);
+               poly1305_update(&dctx, data, len);
+               poly1305_final(&dctx, digest);
 
                memcpy(&ret, digest, bch_crc_bytes[type]);
                return ret;
@@ -253,11 +171,12 @@ int bch2_encrypt(struct bch_fs *c, unsigned type,
        if (!bch2_csum_type_is_encryption(type))
                return 0;
 
-       if (bch2_fs_inconsistent_on(!c->chacha20,
+       if (bch2_fs_inconsistent_on(!c->chacha20_key_set,
                                    c, "attempting to encrypt without encryption key"))
                return -BCH_ERR_no_encryption_key;
 
-       return do_encrypt(c->chacha20, nonce, data, len);
+       bch2_chacha20(&c->chacha20_key, nonce, data, len);
+       return 0;
 }
 
 static struct bch_csum __bch2_checksum_bio(struct bch_fs *c, unsigned type,
@@ -296,26 +215,26 @@ static struct bch_csum __bch2_checksum_bio(struct bch_fs *c, unsigned type,
 
        case BCH_CSUM_chacha20_poly1305_80:
        case BCH_CSUM_chacha20_poly1305_128: {
-               SHASH_DESC_ON_STACK(desc, c->poly1305);
+               struct poly1305_desc_ctx dctx;
                u8 digest[POLY1305_DIGEST_SIZE];
                struct bch_csum ret = { 0 };
 
-               gen_poly_key(c, desc, nonce);
+               bch2_poly1305_init(&dctx, c, nonce);
 
 #ifdef CONFIG_HIGHMEM
                __bio_for_each_segment(bv, bio, *iter, *iter) {
                        void *p = kmap_local_page(bv.bv_page) + bv.bv_offset;
 
-                       crypto_shash_update(desc, p, bv.bv_len);
+                       poly1305_update(&dctx, p, bv.bv_len);
                        kunmap_local(p);
                }
 #else
                __bio_for_each_bvec(bv, bio, *iter, *iter)
-                       crypto_shash_update(desc,
+                       poly1305_update(&dctx,
                                page_address(bv.bv_page) + bv.bv_offset,
                                bv.bv_len);
 #endif
-               crypto_shash_final(desc, digest);
+               poly1305_final(&dctx, digest);
 
                memcpy(&ret, digest, bch_crc_bytes[type]);
                return ret;
@@ -338,43 +257,33 @@ int __bch2_encrypt_bio(struct bch_fs *c, unsigned type,
 {
        struct bio_vec bv;
        struct bvec_iter iter;
-       DARRAY_PREALLOCATED(struct scatterlist, 4) sgl;
-       size_t sgl_len = 0;
+       u32 chacha_state[CHACHA_STATE_WORDS];
        int ret = 0;
 
-       if (bch2_fs_inconsistent_on(!c->chacha20,
+       if (bch2_fs_inconsistent_on(!c->chacha20_key_set,
                                    c, "attempting to encrypt without encryption key"))
                return -BCH_ERR_no_encryption_key;
 
-       darray_init(&sgl);
+       bch2_chacha20_init(chacha_state, &c->chacha20_key, nonce);
 
        bio_for_each_segment(bv, bio, iter) {
-               struct scatterlist sg = {
-                       .page_link      = (unsigned long) bv.bv_page,
-                       .offset         = bv.bv_offset,
-                       .length         = bv.bv_len,
-               };
-
-               if (darray_push(&sgl, sg)) {
-                       sg_mark_end(&darray_last(sgl));
-                       ret = do_encrypt_sg(c->chacha20, nonce, sgl.data, sgl_len);
-                       if (ret)
-                               goto err;
-
-                       nonce = nonce_add(nonce, sgl_len);
-                       sgl_len = 0;
-                       sgl.nr = 0;
-
-                       BUG_ON(darray_push(&sgl, sg));
+               void *p;
+
+               /*
+                * chacha_crypt() assumes that the length is a multiple of
+                * CHACHA_BLOCK_SIZE on any non-final call.
+                */
+               if (!IS_ALIGNED(bv.bv_len, CHACHA_BLOCK_SIZE)) {
+                       bch_err_ratelimited(c, "bio not aligned for encryption");
+                       ret = -EIO;
+                       break;
                }
 
-               sgl_len += sg.length;
+               p = bvec_kmap_local(&bv);
+               chacha20_crypt(chacha_state, p, p, bv.bv_len);
+               kunmap_local(p);
        }
-
-       sg_mark_end(&darray_last(sgl));
-       ret = do_encrypt_sg(c->chacha20, nonce, sgl.data, sgl_len);
-err:
-       darray_exit(&sgl);
+       memzero_explicit(chacha_state, sizeof(chacha_state));
        return ret;
 }
 
@@ -650,10 +559,7 @@ int bch2_decrypt_sb_key(struct bch_fs *c,
        }
 
        /* decrypt real key: */
-       ret = bch2_chacha_encrypt_key(&user_key, bch2_sb_key_nonce(c),
-                                     &sb_key, sizeof(sb_key));
-       if (ret)
-               goto err;
+       bch2_chacha20(&user_key, bch2_sb_key_nonce(c), &sb_key, sizeof(sb_key));
 
        if (bch2_key_is_encrypted(&sb_key)) {
                bch_err(c, "incorrect encryption key");
@@ -668,31 +574,6 @@ err:
        return ret;
 }
 
-static int bch2_alloc_ciphers(struct bch_fs *c)
-{
-       if (c->chacha20)
-               return 0;
-
-       struct crypto_sync_skcipher *chacha20 = crypto_alloc_sync_skcipher("chacha20", 0, 0);
-       int ret = PTR_ERR_OR_ZERO(chacha20);
-       if (ret) {
-               bch_err(c, "error requesting chacha20 module: %s", bch2_err_str(ret));
-               return ret;
-       }
-
-       struct crypto_shash *poly1305 = crypto_alloc_shash("poly1305", 0, 0);
-       ret = PTR_ERR_OR_ZERO(poly1305);
-       if (ret) {
-               bch_err(c, "error requesting poly1305 module: %s", bch2_err_str(ret));
-               crypto_free_sync_skcipher(chacha20);
-               return ret;
-       }
-
-       c->chacha20     = chacha20;
-       c->poly1305     = poly1305;
-       return 0;
-}
-
 #if 0
 
 /*
@@ -797,35 +678,21 @@ err:
 
 void bch2_fs_encryption_exit(struct bch_fs *c)
 {
-       if (c->poly1305)
-               crypto_free_shash(c->poly1305);
-       if (c->chacha20)
-               crypto_free_sync_skcipher(c->chacha20);
+       memzero_explicit(&c->chacha20_key, sizeof(c->chacha20_key));
 }
 
 int bch2_fs_encryption_init(struct bch_fs *c)
 {
        struct bch_sb_field_crypt *crypt;
-       struct bch_key key;
-       int ret = 0;
+       int ret;
 
        crypt = bch2_sb_field_get(c->disk_sb.sb, crypt);
        if (!crypt)
-               goto out;
+               return 0;
 
-       ret = bch2_alloc_ciphers(c);
+       ret = bch2_decrypt_sb_key(c, crypt, &c->chacha20_key);
        if (ret)
-               goto out;
-
-       ret = bch2_decrypt_sb_key(c, crypt, &key);
-       if (ret)
-               goto out;
-
-       ret = crypto_skcipher_setkey(&c->chacha20->base,
-                       (void *) &key.key, sizeof(key.key));
-       if (ret)
-               goto out;
-out:
-       memzero_explicit(&key, sizeof(key));
-       return ret;
+               return ret;
+       c->chacha20_key_set = true;
+       return 0;
 }
index 4ac251c8fcd839b94a3406581c057312c1f64ccf..1310782d3ae9300f100b60baa86d7c5648a1e804 100644 (file)
@@ -69,7 +69,6 @@ static inline void bch2_csum_err_msg(struct printbuf *out,
        bch2_csum_to_text(out, type, expected);
 }
 
-int bch2_chacha_encrypt_key(struct bch_key *, struct nonce, void *, size_t);
 int bch2_request_key(struct bch_sb *, struct bch_key *);
 #ifndef __KERNEL__
 int bch2_revoke_key(struct bch_sb *);
@@ -156,7 +155,7 @@ static inline bool bch2_checksum_type_valid(const struct bch_fs *c,
        if (type >= BCH_CSUM_NR)
                return false;
 
-       if (bch2_csum_type_is_encryption(type) && !c->chacha20)
+       if (bch2_csum_type_is_encryption(type) && !c->chacha20_key_set)
                return false;
 
        return true;
index 417bb0c7bbfa6542e0405be1b8f204d10f3c53e1..fd627c8d105364d5167482c0674bddda0747f177 100644 (file)
@@ -977,7 +977,8 @@ retry_pick:
                goto err;
        }
 
-       if (unlikely(bch2_csum_type_is_encryption(pick.crc.csum_type)) && !c->chacha20) {
+       if (unlikely(bch2_csum_type_is_encryption(pick.crc.csum_type)) &&
+           !c->chacha20_key_set) {
                struct printbuf buf = PRINTBUF;
                bch2_read_err_msg_trans(trans, &buf, orig, read_pos);
                prt_printf(&buf, "attempting to read encrypted data without encryption key\n  ");
index 16ebea1214af5f96aca20f808d3f6d2545a4296d..956f51d563b49f4627260507c07ac5e8f698985d 100644 (file)
 #include <linux/percpu.h>
 #include <linux/random.h>
 #include <linux/sysfs.h>
-#include <crypto/hash.h>
 
 MODULE_LICENSE("GPL");
 MODULE_AUTHOR("Kent Overstreet <kent.overstreet@gmail.com>");
 MODULE_DESCRIPTION("bcachefs filesystem");
-MODULE_SOFTDEP("pre: chacha20");
-MODULE_SOFTDEP("pre: poly1305");
 MODULE_SOFTDEP("pre: xxhash");
 
 const char * const bch2_fs_flag_strs[] = {