]> git.karo-electronics.de Git - karo-tx-linux.git/blob - crypto/chacha20_generic.c
arm64: configs: enable QCOM_WCNSS PIL and CRTL
[karo-tx-linux.git] / crypto / chacha20_generic.c
1 /*
2  * ChaCha20 256-bit cipher algorithm, RFC7539
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>
13 #include <linux/crypto.h>
14 #include <linux/kernel.h>
15 #include <linux/module.h>
16 #include <crypto/chacha20.h>
17
18 static inline u32 rotl32(u32 v, u8 n)
19 {
20         return (v << n) | (v >> (sizeof(v) * 8 - n));
21 }
22
23 static inline u32 le32_to_cpuvp(const void *p)
24 {
25         return le32_to_cpup(p);
26 }
27
28 static void chacha20_block(u32 *state, void *stream)
29 {
30         u32 x[16], *out = stream;
31         int i;
32
33         for (i = 0; i < ARRAY_SIZE(x); i++)
34                 x[i] = state[i];
35
36         for (i = 0; i < 20; i += 2) {
37                 x[0]  += x[4];    x[12] = rotl32(x[12] ^ x[0],  16);
38                 x[1]  += x[5];    x[13] = rotl32(x[13] ^ x[1],  16);
39                 x[2]  += x[6];    x[14] = rotl32(x[14] ^ x[2],  16);
40                 x[3]  += x[7];    x[15] = rotl32(x[15] ^ x[3],  16);
41
42                 x[8]  += x[12];   x[4]  = rotl32(x[4]  ^ x[8],  12);
43                 x[9]  += x[13];   x[5]  = rotl32(x[5]  ^ x[9],  12);
44                 x[10] += x[14];   x[6]  = rotl32(x[6]  ^ x[10], 12);
45                 x[11] += x[15];   x[7]  = rotl32(x[7]  ^ x[11], 12);
46
47                 x[0]  += x[4];    x[12] = rotl32(x[12] ^ x[0],   8);
48                 x[1]  += x[5];    x[13] = rotl32(x[13] ^ x[1],   8);
49                 x[2]  += x[6];    x[14] = rotl32(x[14] ^ x[2],   8);
50                 x[3]  += x[7];    x[15] = rotl32(x[15] ^ x[3],   8);
51
52                 x[8]  += x[12];   x[4]  = rotl32(x[4]  ^ x[8],   7);
53                 x[9]  += x[13];   x[5]  = rotl32(x[5]  ^ x[9],   7);
54                 x[10] += x[14];   x[6]  = rotl32(x[6]  ^ x[10],  7);
55                 x[11] += x[15];   x[7]  = rotl32(x[7]  ^ x[11],  7);
56
57                 x[0]  += x[5];    x[15] = rotl32(x[15] ^ x[0],  16);
58                 x[1]  += x[6];    x[12] = rotl32(x[12] ^ x[1],  16);
59                 x[2]  += x[7];    x[13] = rotl32(x[13] ^ x[2],  16);
60                 x[3]  += x[4];    x[14] = rotl32(x[14] ^ x[3],  16);
61
62                 x[10] += x[15];   x[5]  = rotl32(x[5]  ^ x[10], 12);
63                 x[11] += x[12];   x[6]  = rotl32(x[6]  ^ x[11], 12);
64                 x[8]  += x[13];   x[7]  = rotl32(x[7]  ^ x[8],  12);
65                 x[9]  += x[14];   x[4]  = rotl32(x[4]  ^ x[9],  12);
66
67                 x[0]  += x[5];    x[15] = rotl32(x[15] ^ x[0],   8);
68                 x[1]  += x[6];    x[12] = rotl32(x[12] ^ x[1],   8);
69                 x[2]  += x[7];    x[13] = rotl32(x[13] ^ x[2],   8);
70                 x[3]  += x[4];    x[14] = rotl32(x[14] ^ x[3],   8);
71
72                 x[10] += x[15];   x[5]  = rotl32(x[5]  ^ x[10],  7);
73                 x[11] += x[12];   x[6]  = rotl32(x[6]  ^ x[11],  7);
74                 x[8]  += x[13];   x[7]  = rotl32(x[7]  ^ x[8],   7);
75                 x[9]  += x[14];   x[4]  = rotl32(x[4]  ^ x[9],   7);
76         }
77
78         for (i = 0; i < ARRAY_SIZE(x); i++)
79                 out[i] = cpu_to_le32(x[i] + state[i]);
80
81         state[12]++;
82 }
83
84 static void chacha20_docrypt(u32 *state, u8 *dst, const u8 *src,
85                              unsigned int bytes)
86 {
87         u8 stream[CHACHA20_BLOCK_SIZE];
88
89         if (dst != src)
90                 memcpy(dst, src, bytes);
91
92         while (bytes >= CHACHA20_BLOCK_SIZE) {
93                 chacha20_block(state, stream);
94                 crypto_xor(dst, stream, CHACHA20_BLOCK_SIZE);
95                 bytes -= CHACHA20_BLOCK_SIZE;
96                 dst += CHACHA20_BLOCK_SIZE;
97         }
98         if (bytes) {
99                 chacha20_block(state, stream);
100                 crypto_xor(dst, stream, bytes);
101         }
102 }
103
104 void crypto_chacha20_init(u32 *state, struct chacha20_ctx *ctx, u8 *iv)
105 {
106         static const char constant[16] = "expand 32-byte k";
107
108         state[0]  = le32_to_cpuvp(constant +  0);
109         state[1]  = le32_to_cpuvp(constant +  4);
110         state[2]  = le32_to_cpuvp(constant +  8);
111         state[3]  = le32_to_cpuvp(constant + 12);
112         state[4]  = ctx->key[0];
113         state[5]  = ctx->key[1];
114         state[6]  = ctx->key[2];
115         state[7]  = ctx->key[3];
116         state[8]  = ctx->key[4];
117         state[9]  = ctx->key[5];
118         state[10] = ctx->key[6];
119         state[11] = ctx->key[7];
120         state[12] = le32_to_cpuvp(iv +  0);
121         state[13] = le32_to_cpuvp(iv +  4);
122         state[14] = le32_to_cpuvp(iv +  8);
123         state[15] = le32_to_cpuvp(iv + 12);
124 }
125 EXPORT_SYMBOL_GPL(crypto_chacha20_init);
126
127 int crypto_chacha20_setkey(struct crypto_tfm *tfm, const u8 *key,
128                            unsigned int keysize)
129 {
130         struct chacha20_ctx *ctx = crypto_tfm_ctx(tfm);
131         int i;
132
133         if (keysize != CHACHA20_KEY_SIZE)
134                 return -EINVAL;
135
136         for (i = 0; i < ARRAY_SIZE(ctx->key); i++)
137                 ctx->key[i] = le32_to_cpuvp(key + i * sizeof(u32));
138
139         return 0;
140 }
141 EXPORT_SYMBOL_GPL(crypto_chacha20_setkey);
142
143 int crypto_chacha20_crypt(struct blkcipher_desc *desc, struct scatterlist *dst,
144                           struct scatterlist *src, unsigned int nbytes)
145 {
146         struct blkcipher_walk walk;
147         u32 state[16];
148         int err;
149
150         blkcipher_walk_init(&walk, dst, src, nbytes);
151         err = blkcipher_walk_virt_block(desc, &walk, CHACHA20_BLOCK_SIZE);
152
153         crypto_chacha20_init(state, crypto_blkcipher_ctx(desc->tfm), walk.iv);
154
155         while (walk.nbytes >= CHACHA20_BLOCK_SIZE) {
156                 chacha20_docrypt(state, walk.dst.virt.addr, walk.src.virt.addr,
157                                  rounddown(walk.nbytes, CHACHA20_BLOCK_SIZE));
158                 err = blkcipher_walk_done(desc, &walk,
159                                           walk.nbytes % CHACHA20_BLOCK_SIZE);
160         }
161
162         if (walk.nbytes) {
163                 chacha20_docrypt(state, walk.dst.virt.addr, walk.src.virt.addr,
164                                  walk.nbytes);
165                 err = blkcipher_walk_done(desc, &walk, 0);
166         }
167
168         return err;
169 }
170 EXPORT_SYMBOL_GPL(crypto_chacha20_crypt);
171
172 static struct crypto_alg alg = {
173         .cra_name               = "chacha20",
174         .cra_driver_name        = "chacha20-generic",
175         .cra_priority           = 100,
176         .cra_flags              = CRYPTO_ALG_TYPE_BLKCIPHER,
177         .cra_blocksize          = 1,
178         .cra_type               = &crypto_blkcipher_type,
179         .cra_ctxsize            = sizeof(struct chacha20_ctx),
180         .cra_alignmask          = sizeof(u32) - 1,
181         .cra_module             = THIS_MODULE,
182         .cra_u                  = {
183                 .blkcipher = {
184                         .min_keysize    = CHACHA20_KEY_SIZE,
185                         .max_keysize    = CHACHA20_KEY_SIZE,
186                         .ivsize         = CHACHA20_IV_SIZE,
187                         .geniv          = "seqiv",
188                         .setkey         = crypto_chacha20_setkey,
189                         .encrypt        = crypto_chacha20_crypt,
190                         .decrypt        = crypto_chacha20_crypt,
191                 },
192         },
193 };
194
195 static int __init chacha20_generic_mod_init(void)
196 {
197         return crypto_register_alg(&alg);
198 }
199
200 static void __exit chacha20_generic_mod_fini(void)
201 {
202         crypto_unregister_alg(&alg);
203 }
204
205 module_init(chacha20_generic_mod_init);
206 module_exit(chacha20_generic_mod_fini);
207
208 MODULE_LICENSE("GPL");
209 MODULE_AUTHOR("Martin Willi <martin@strongswan.org>");
210 MODULE_DESCRIPTION("chacha20 cipher algorithm");
211 MODULE_ALIAS_CRYPTO("chacha20");
212 MODULE_ALIAS_CRYPTO("chacha20-generic");