Back to home page

OSCL-LXR

 
 

    


0001 /*
0002  * ARM NEON and scalar accelerated ChaCha and XChaCha stream ciphers,
0003  * including ChaCha20 (RFC7539)
0004  *
0005  * Copyright (C) 2016 - 2017 Linaro, Ltd. <ard.biesheuvel@linaro.org>
0006  *
0007  * This program is free software; you can redistribute it and/or modify
0008  * it under the terms of the GNU General Public License version 2 as
0009  * published by the Free Software Foundation.
0010  *
0011  * Based on:
0012  * ChaCha20 256-bit cipher algorithm, RFC7539, SIMD glue code
0013  *
0014  * Copyright (C) 2015 Martin Willi
0015  *
0016  * This program is free software; you can redistribute it and/or modify
0017  * it under the terms of the GNU General Public License as published by
0018  * the Free Software Foundation; either version 2 of the License, or
0019  * (at your option) any later version.
0020  */
0021 
0022 #include <crypto/algapi.h>
0023 #include <crypto/internal/chacha.h>
0024 #include <crypto/internal/simd.h>
0025 #include <crypto/internal/skcipher.h>
0026 #include <linux/jump_label.h>
0027 #include <linux/kernel.h>
0028 #include <linux/module.h>
0029 
0030 #include <asm/hwcap.h>
0031 #include <asm/neon.h>
0032 #include <asm/simd.h>
0033 
0034 asmlinkage void chacha_block_xor_neon(u32 *state, u8 *dst, const u8 *src,
0035                       int nrounds);
0036 asmlinkage void chacha_4block_xor_neon(u32 *state, u8 *dst, const u8 *src,
0037                        int nrounds, int bytes);
0038 asmlinkage void hchacha_block_neon(const u32 *state, u32 *out, int nrounds);
0039 
0040 static __ro_after_init DEFINE_STATIC_KEY_FALSE(have_neon);
0041 
0042 static void chacha_doneon(u32 *state, u8 *dst, const u8 *src,
0043               int bytes, int nrounds)
0044 {
0045     while (bytes > 0) {
0046         int l = min(bytes, CHACHA_BLOCK_SIZE * 5);
0047 
0048         if (l <= CHACHA_BLOCK_SIZE) {
0049             u8 buf[CHACHA_BLOCK_SIZE];
0050 
0051             memcpy(buf, src, l);
0052             chacha_block_xor_neon(state, buf, buf, nrounds);
0053             memcpy(dst, buf, l);
0054             state[12] += 1;
0055             break;
0056         }
0057         chacha_4block_xor_neon(state, dst, src, nrounds, l);
0058         bytes -= l;
0059         src += l;
0060         dst += l;
0061         state[12] += DIV_ROUND_UP(l, CHACHA_BLOCK_SIZE);
0062     }
0063 }
0064 
0065 void hchacha_block_arch(const u32 *state, u32 *stream, int nrounds)
0066 {
0067     if (!static_branch_likely(&have_neon) || !crypto_simd_usable()) {
0068         hchacha_block_generic(state, stream, nrounds);
0069     } else {
0070         kernel_neon_begin();
0071         hchacha_block_neon(state, stream, nrounds);
0072         kernel_neon_end();
0073     }
0074 }
0075 EXPORT_SYMBOL(hchacha_block_arch);
0076 
0077 void chacha_init_arch(u32 *state, const u32 *key, const u8 *iv)
0078 {
0079     chacha_init_generic(state, key, iv);
0080 }
0081 EXPORT_SYMBOL(chacha_init_arch);
0082 
0083 void chacha_crypt_arch(u32 *state, u8 *dst, const u8 *src, unsigned int bytes,
0084                int nrounds)
0085 {
0086     if (!static_branch_likely(&have_neon) || bytes <= CHACHA_BLOCK_SIZE ||
0087         !crypto_simd_usable())
0088         return chacha_crypt_generic(state, dst, src, bytes, nrounds);
0089 
0090     do {
0091         unsigned int todo = min_t(unsigned int, bytes, SZ_4K);
0092 
0093         kernel_neon_begin();
0094         chacha_doneon(state, dst, src, todo, nrounds);
0095         kernel_neon_end();
0096 
0097         bytes -= todo;
0098         src += todo;
0099         dst += todo;
0100     } while (bytes);
0101 }
0102 EXPORT_SYMBOL(chacha_crypt_arch);
0103 
0104 static int chacha_neon_stream_xor(struct skcipher_request *req,
0105                   const struct chacha_ctx *ctx, const u8 *iv)
0106 {
0107     struct skcipher_walk walk;
0108     u32 state[16];
0109     int err;
0110 
0111     err = skcipher_walk_virt(&walk, req, false);
0112 
0113     chacha_init_generic(state, ctx->key, iv);
0114 
0115     while (walk.nbytes > 0) {
0116         unsigned int nbytes = walk.nbytes;
0117 
0118         if (nbytes < walk.total)
0119             nbytes = rounddown(nbytes, walk.stride);
0120 
0121         if (!static_branch_likely(&have_neon) ||
0122             !crypto_simd_usable()) {
0123             chacha_crypt_generic(state, walk.dst.virt.addr,
0124                          walk.src.virt.addr, nbytes,
0125                          ctx->nrounds);
0126         } else {
0127             kernel_neon_begin();
0128             chacha_doneon(state, walk.dst.virt.addr,
0129                       walk.src.virt.addr, nbytes, ctx->nrounds);
0130             kernel_neon_end();
0131         }
0132         err = skcipher_walk_done(&walk, walk.nbytes - nbytes);
0133     }
0134 
0135     return err;
0136 }
0137 
0138 static int chacha_neon(struct skcipher_request *req)
0139 {
0140     struct crypto_skcipher *tfm = crypto_skcipher_reqtfm(req);
0141     struct chacha_ctx *ctx = crypto_skcipher_ctx(tfm);
0142 
0143     return chacha_neon_stream_xor(req, ctx, req->iv);
0144 }
0145 
0146 static int xchacha_neon(struct skcipher_request *req)
0147 {
0148     struct crypto_skcipher *tfm = crypto_skcipher_reqtfm(req);
0149     struct chacha_ctx *ctx = crypto_skcipher_ctx(tfm);
0150     struct chacha_ctx subctx;
0151     u32 state[16];
0152     u8 real_iv[16];
0153 
0154     chacha_init_generic(state, ctx->key, req->iv);
0155     hchacha_block_arch(state, subctx.key, ctx->nrounds);
0156     subctx.nrounds = ctx->nrounds;
0157 
0158     memcpy(&real_iv[0], req->iv + 24, 8);
0159     memcpy(&real_iv[8], req->iv + 16, 8);
0160     return chacha_neon_stream_xor(req, &subctx, real_iv);
0161 }
0162 
0163 static struct skcipher_alg algs[] = {
0164     {
0165         .base.cra_name      = "chacha20",
0166         .base.cra_driver_name   = "chacha20-neon",
0167         .base.cra_priority  = 300,
0168         .base.cra_blocksize = 1,
0169         .base.cra_ctxsize   = sizeof(struct chacha_ctx),
0170         .base.cra_module    = THIS_MODULE,
0171 
0172         .min_keysize        = CHACHA_KEY_SIZE,
0173         .max_keysize        = CHACHA_KEY_SIZE,
0174         .ivsize         = CHACHA_IV_SIZE,
0175         .chunksize      = CHACHA_BLOCK_SIZE,
0176         .walksize       = 5 * CHACHA_BLOCK_SIZE,
0177         .setkey         = chacha20_setkey,
0178         .encrypt        = chacha_neon,
0179         .decrypt        = chacha_neon,
0180     }, {
0181         .base.cra_name      = "xchacha20",
0182         .base.cra_driver_name   = "xchacha20-neon",
0183         .base.cra_priority  = 300,
0184         .base.cra_blocksize = 1,
0185         .base.cra_ctxsize   = sizeof(struct chacha_ctx),
0186         .base.cra_module    = THIS_MODULE,
0187 
0188         .min_keysize        = CHACHA_KEY_SIZE,
0189         .max_keysize        = CHACHA_KEY_SIZE,
0190         .ivsize         = XCHACHA_IV_SIZE,
0191         .chunksize      = CHACHA_BLOCK_SIZE,
0192         .walksize       = 5 * CHACHA_BLOCK_SIZE,
0193         .setkey         = chacha20_setkey,
0194         .encrypt        = xchacha_neon,
0195         .decrypt        = xchacha_neon,
0196     }, {
0197         .base.cra_name      = "xchacha12",
0198         .base.cra_driver_name   = "xchacha12-neon",
0199         .base.cra_priority  = 300,
0200         .base.cra_blocksize = 1,
0201         .base.cra_ctxsize   = sizeof(struct chacha_ctx),
0202         .base.cra_module    = THIS_MODULE,
0203 
0204         .min_keysize        = CHACHA_KEY_SIZE,
0205         .max_keysize        = CHACHA_KEY_SIZE,
0206         .ivsize         = XCHACHA_IV_SIZE,
0207         .chunksize      = CHACHA_BLOCK_SIZE,
0208         .walksize       = 5 * CHACHA_BLOCK_SIZE,
0209         .setkey         = chacha12_setkey,
0210         .encrypt        = xchacha_neon,
0211         .decrypt        = xchacha_neon,
0212     }
0213 };
0214 
0215 static int __init chacha_simd_mod_init(void)
0216 {
0217     if (!cpu_have_named_feature(ASIMD))
0218         return 0;
0219 
0220     static_branch_enable(&have_neon);
0221 
0222     return IS_REACHABLE(CONFIG_CRYPTO_SKCIPHER) ?
0223         crypto_register_skciphers(algs, ARRAY_SIZE(algs)) : 0;
0224 }
0225 
0226 static void __exit chacha_simd_mod_fini(void)
0227 {
0228     if (IS_REACHABLE(CONFIG_CRYPTO_SKCIPHER) && cpu_have_named_feature(ASIMD))
0229         crypto_unregister_skciphers(algs, ARRAY_SIZE(algs));
0230 }
0231 
0232 module_init(chacha_simd_mod_init);
0233 module_exit(chacha_simd_mod_fini);
0234 
0235 MODULE_DESCRIPTION("ChaCha and XChaCha stream ciphers (NEON accelerated)");
0236 MODULE_AUTHOR("Ard Biesheuvel <ard.biesheuvel@linaro.org>");
0237 MODULE_LICENSE("GPL v2");
0238 MODULE_ALIAS_CRYPTO("chacha20");
0239 MODULE_ALIAS_CRYPTO("chacha20-neon");
0240 MODULE_ALIAS_CRYPTO("xchacha20");
0241 MODULE_ALIAS_CRYPTO("xchacha20-neon");
0242 MODULE_ALIAS_CRYPTO("xchacha12");
0243 MODULE_ALIAS_CRYPTO("xchacha12-neon");