blob: 44466881cdc96bd1c025acc04139356c76e50018 [file] [log] [blame]
Martin Willic9320b62015-07-16 19:14:01 +02001/*
2 * ChaCha20 256-bit cipher algorithm, RFC7539, SIMD glue code
3 *
4 * Copyright (C) 2015 Martin Willi
5 *
6 * This program is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation; either version 2 of the License, or
9 * (at your option) any later version.
10 */
11
12#include <crypto/algapi.h>
Eric Biggersb8181f32018-11-16 17:26:21 -080013#include <crypto/chacha.h>
Martin Willic9320b62015-07-16 19:14:01 +020014#include <linux/crypto.h>
15#include <linux/kernel.h>
16#include <linux/module.h>
17#include <asm/fpu/api.h>
18#include <asm/simd.h>
19
20#define CHACHA20_STATE_ALIGN 16
21
22asmlinkage void chacha20_block_xor_ssse3(u32 *state, u8 *dst, const u8 *src);
Martin Willi274f9382015-07-16 19:14:02 +020023asmlinkage void chacha20_4block_xor_ssse3(u32 *state, u8 *dst, const u8 *src);
Martin Willi3d1e93c2015-07-16 19:14:03 +020024#ifdef CONFIG_AS_AVX2
25asmlinkage void chacha20_8block_xor_avx2(u32 *state, u8 *dst, const u8 *src);
26static bool chacha20_use_avx2;
27#endif
Martin Willic9320b62015-07-16 19:14:01 +020028
29static void chacha20_dosimd(u32 *state, u8 *dst, const u8 *src,
30 unsigned int bytes)
31{
Eric Biggersb8181f32018-11-16 17:26:21 -080032 u8 buf[CHACHA_BLOCK_SIZE];
Martin Willic9320b62015-07-16 19:14:01 +020033
Martin Willi3d1e93c2015-07-16 19:14:03 +020034#ifdef CONFIG_AS_AVX2
35 if (chacha20_use_avx2) {
Eric Biggersb8181f32018-11-16 17:26:21 -080036 while (bytes >= CHACHA_BLOCK_SIZE * 8) {
Martin Willi3d1e93c2015-07-16 19:14:03 +020037 chacha20_8block_xor_avx2(state, dst, src);
Eric Biggersb8181f32018-11-16 17:26:21 -080038 bytes -= CHACHA_BLOCK_SIZE * 8;
39 src += CHACHA_BLOCK_SIZE * 8;
40 dst += CHACHA_BLOCK_SIZE * 8;
Martin Willi3d1e93c2015-07-16 19:14:03 +020041 state[12] += 8;
42 }
43 }
44#endif
Eric Biggersb8181f32018-11-16 17:26:21 -080045 while (bytes >= CHACHA_BLOCK_SIZE * 4) {
Martin Willi274f9382015-07-16 19:14:02 +020046 chacha20_4block_xor_ssse3(state, dst, src);
Eric Biggersb8181f32018-11-16 17:26:21 -080047 bytes -= CHACHA_BLOCK_SIZE * 4;
48 src += CHACHA_BLOCK_SIZE * 4;
49 dst += CHACHA_BLOCK_SIZE * 4;
Martin Willi274f9382015-07-16 19:14:02 +020050 state[12] += 4;
51 }
Eric Biggersb8181f32018-11-16 17:26:21 -080052 while (bytes >= CHACHA_BLOCK_SIZE) {
Martin Willic9320b62015-07-16 19:14:01 +020053 chacha20_block_xor_ssse3(state, dst, src);
Eric Biggersb8181f32018-11-16 17:26:21 -080054 bytes -= CHACHA_BLOCK_SIZE;
55 src += CHACHA_BLOCK_SIZE;
56 dst += CHACHA_BLOCK_SIZE;
Martin Willic9320b62015-07-16 19:14:01 +020057 state[12]++;
58 }
59 if (bytes) {
60 memcpy(buf, src, bytes);
61 chacha20_block_xor_ssse3(state, buf, buf);
62 memcpy(dst, buf, bytes);
63 }
64}
65
66static int chacha20_simd(struct blkcipher_desc *desc, struct scatterlist *dst,
67 struct scatterlist *src, unsigned int nbytes)
68{
69 u32 *state, state_buf[16 + (CHACHA20_STATE_ALIGN / sizeof(u32)) - 1];
70 struct blkcipher_walk walk;
71 int err;
72
Eric Biggersb8181f32018-11-16 17:26:21 -080073 if (nbytes <= CHACHA_BLOCK_SIZE || !may_use_simd())
74 return crypto_chacha_crypt(desc, dst, src, nbytes);
Martin Willic9320b62015-07-16 19:14:01 +020075
76 state = (u32 *)roundup((uintptr_t)state_buf, CHACHA20_STATE_ALIGN);
77
78 blkcipher_walk_init(&walk, dst, src, nbytes);
Eric Biggersb8181f32018-11-16 17:26:21 -080079 err = blkcipher_walk_virt_block(desc, &walk, CHACHA_BLOCK_SIZE);
Eric Biggersce8ca302019-01-07 15:15:59 -080080 desc->flags &= ~CRYPTO_TFM_REQ_MAY_SLEEP;
Martin Willic9320b62015-07-16 19:14:01 +020081
Eric Biggersb8181f32018-11-16 17:26:21 -080082 crypto_chacha_init(state, crypto_blkcipher_ctx(desc->tfm), walk.iv);
Martin Willic9320b62015-07-16 19:14:01 +020083
84 kernel_fpu_begin();
85
Eric Biggersb8181f32018-11-16 17:26:21 -080086 while (walk.nbytes >= CHACHA_BLOCK_SIZE) {
Martin Willic9320b62015-07-16 19:14:01 +020087 chacha20_dosimd(state, walk.dst.virt.addr, walk.src.virt.addr,
Eric Biggersb8181f32018-11-16 17:26:21 -080088 rounddown(walk.nbytes, CHACHA_BLOCK_SIZE));
Martin Willic9320b62015-07-16 19:14:01 +020089 err = blkcipher_walk_done(desc, &walk,
Eric Biggersb8181f32018-11-16 17:26:21 -080090 walk.nbytes % CHACHA_BLOCK_SIZE);
Martin Willic9320b62015-07-16 19:14:01 +020091 }
92
93 if (walk.nbytes) {
94 chacha20_dosimd(state, walk.dst.virt.addr, walk.src.virt.addr,
95 walk.nbytes);
96 err = blkcipher_walk_done(desc, &walk, 0);
97 }
98
99 kernel_fpu_end();
100
101 return err;
102}
103
104static struct crypto_alg alg = {
105 .cra_name = "chacha20",
106 .cra_driver_name = "chacha20-simd",
107 .cra_priority = 300,
108 .cra_flags = CRYPTO_ALG_TYPE_BLKCIPHER,
109 .cra_blocksize = 1,
110 .cra_type = &crypto_blkcipher_type,
Eric Biggersb8181f32018-11-16 17:26:21 -0800111 .cra_ctxsize = sizeof(struct chacha_ctx),
Martin Willic9320b62015-07-16 19:14:01 +0200112 .cra_alignmask = sizeof(u32) - 1,
113 .cra_module = THIS_MODULE,
114 .cra_u = {
115 .blkcipher = {
Eric Biggersb8181f32018-11-16 17:26:21 -0800116 .min_keysize = CHACHA_KEY_SIZE,
117 .max_keysize = CHACHA_KEY_SIZE,
118 .ivsize = CHACHA_IV_SIZE,
Martin Willic9320b62015-07-16 19:14:01 +0200119 .geniv = "seqiv",
120 .setkey = crypto_chacha20_setkey,
121 .encrypt = chacha20_simd,
122 .decrypt = chacha20_simd,
123 },
124 },
125};
126
127static int __init chacha20_simd_mod_init(void)
128{
Borislav Petkov362f9242015-12-07 10:39:41 +0100129 if (!boot_cpu_has(X86_FEATURE_SSSE3))
Martin Willic9320b62015-07-16 19:14:01 +0200130 return -ENODEV;
131
Martin Willi3d1e93c2015-07-16 19:14:03 +0200132#ifdef CONFIG_AS_AVX2
Borislav Petkovda154e82016-04-04 22:24:56 +0200133 chacha20_use_avx2 = boot_cpu_has(X86_FEATURE_AVX) &&
134 boot_cpu_has(X86_FEATURE_AVX2) &&
Dave Hansend91cab72015-09-02 16:31:26 -0700135 cpu_has_xfeatures(XFEATURE_MASK_SSE | XFEATURE_MASK_YMM, NULL);
Martin Willi3d1e93c2015-07-16 19:14:03 +0200136#endif
Martin Willic9320b62015-07-16 19:14:01 +0200137 return crypto_register_alg(&alg);
138}
139
140static void __exit chacha20_simd_mod_fini(void)
141{
142 crypto_unregister_alg(&alg);
143}
144
145module_init(chacha20_simd_mod_init);
146module_exit(chacha20_simd_mod_fini);
147
148MODULE_LICENSE("GPL");
149MODULE_AUTHOR("Martin Willi <martin@strongswan.org>");
150MODULE_DESCRIPTION("chacha20 cipher algorithm, SIMD accelerated");
151MODULE_ALIAS_CRYPTO("chacha20");
152MODULE_ALIAS_CRYPTO("chacha20-simd");