Skip to content

Commit da7a0ab

Browse files
ebiggersherbertx
authored andcommitted
crypto: speck - add support for the Speck block cipher
Add a generic implementation of Speck, including the Speck128 and Speck64 variants. Speck is a lightweight block cipher that can be much faster than AES on processors that don't have AES instructions. We are planning to offer Speck-XTS (probably Speck128/256-XTS) as an option for dm-crypt and fscrypt on Android, for low-end mobile devices with older CPUs such as ARMv7 which don't have the Cryptography Extensions. Currently, such devices are unencrypted because AES is not fast enough, even when the NEON bit-sliced implementation of AES is used. Other AES alternatives such as Twofish, Threefish, Camellia, CAST6, and Serpent aren't fast enough either; it seems that only a modern ARX cipher can provide sufficient performance on these devices. This is a replacement for our original proposal (https://patchwork.kernel.org/patch/10101451/) which was to offer ChaCha20 for these devices. However, the use of a stream cipher for disk/file encryption with no space to store nonces would have been much more insecure than we thought initially, given that it would be used on top of flash storage as well as potentially on top of F2FS, neither of which is guaranteed to overwrite data in-place. Speck has been somewhat controversial due to its origin. Nevertheless, it has a straightforward design (it's an ARX cipher), and it appears to be the leading software-optimized lightweight block cipher currently, with the most cryptanalysis. It's also easy to implement without side channels, unlike AES. Moreover, we only intend Speck to be used when the status quo is no encryption, due to AES not being fast enough. We've also considered a novel length-preserving encryption mode based on ChaCha20 and Poly1305. While theoretically attractive, such a mode would be a brand new crypto construction and would be more complicated and difficult to implement efficiently in comparison to Speck-XTS. There is confusion about the byte and word orders of Speck, since the original paper doesn't specify them. But we have implemented it using the orders the authors recommended in a correspondence with them. The test vectors are taken from the original paper but were mapped to byte arrays using the recommended byte and word orders. Signed-off-by: Eric Biggers <[email protected]> Signed-off-by: Herbert Xu <[email protected]>
1 parent e845520 commit da7a0ab

File tree

5 files changed

+460
-0
lines changed

5 files changed

+460
-0
lines changed

crypto/Kconfig

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1508,6 +1508,20 @@ config CRYPTO_SERPENT_AVX2_X86_64
15081508
See also:
15091509
<http://www.cl.cam.ac.uk/~rja14/serpent.html>
15101510

1511+
config CRYPTO_SPECK
1512+
tristate "Speck cipher algorithm"
1513+
select CRYPTO_ALGAPI
1514+
help
1515+
Speck is a lightweight block cipher that is tuned for optimal
1516+
performance in software (rather than hardware).
1517+
1518+
Speck may not be as secure as AES, and should only be used on systems
1519+
where AES is not fast enough.
1520+
1521+
See also: <https://eprint.iacr.org/2013/404.pdf>
1522+
1523+
If unsure, say N.
1524+
15111525
config CRYPTO_TEA
15121526
tristate "TEA, XTEA and XETA cipher algorithms"
15131527
select CRYPTO_ALGAPI

crypto/Makefile

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -110,6 +110,7 @@ obj-$(CONFIG_CRYPTO_TEA) += tea.o
110110
obj-$(CONFIG_CRYPTO_KHAZAD) += khazad.o
111111
obj-$(CONFIG_CRYPTO_ANUBIS) += anubis.o
112112
obj-$(CONFIG_CRYPTO_SEED) += seed.o
113+
obj-$(CONFIG_CRYPTO_SPECK) += speck.o
113114
obj-$(CONFIG_CRYPTO_SALSA20) += salsa20_generic.o
114115
obj-$(CONFIG_CRYPTO_CHACHA20) += chacha20_generic.o
115116
obj-$(CONFIG_CRYPTO_POLY1305) += poly1305_generic.o

crypto/speck.c

Lines changed: 299 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,299 @@
1+
// SPDX-License-Identifier: GPL-2.0
2+
/*
3+
* Speck: a lightweight block cipher
4+
*
5+
* Copyright (c) 2018 Google, Inc
6+
*
7+
* Speck has 10 variants, including 5 block sizes. For now we only implement
8+
* the variants Speck128/128, Speck128/192, Speck128/256, Speck64/96, and
9+
* Speck64/128. Speck${B}/${K} denotes the variant with a block size of B bits
10+
* and a key size of K bits. The Speck128 variants are believed to be the most
11+
* secure variants, and they use the same block size and key sizes as AES. The
12+
* Speck64 variants are less secure, but on 32-bit processors are usually
13+
* faster. The remaining variants (Speck32, Speck48, and Speck96) are even less
14+
* secure and/or not as well suited for implementation on either 32-bit or
15+
* 64-bit processors, so are omitted.
16+
*
17+
* Reference: "The Simon and Speck Families of Lightweight Block Ciphers"
18+
* https://eprint.iacr.org/2013/404.pdf
19+
*
20+
* In a correspondence, the Speck designers have also clarified that the words
21+
* should be interpreted in little-endian format, and the words should be
22+
* ordered such that the first word of each block is 'y' rather than 'x', and
23+
* the first key word (rather than the last) becomes the first round key.
24+
*/
25+
26+
#include <asm/unaligned.h>
27+
#include <linux/bitops.h>
28+
#include <linux/crypto.h>
29+
#include <linux/init.h>
30+
#include <linux/module.h>
31+
32+
/* Speck128 */
33+
34+
#define SPECK128_BLOCK_SIZE 16
35+
36+
#define SPECK128_128_KEY_SIZE 16
37+
#define SPECK128_128_NROUNDS 32
38+
39+
#define SPECK128_192_KEY_SIZE 24
40+
#define SPECK128_192_NROUNDS 33
41+
42+
#define SPECK128_256_KEY_SIZE 32
43+
#define SPECK128_256_NROUNDS 34
44+
45+
struct speck128_tfm_ctx {
46+
u64 round_keys[SPECK128_256_NROUNDS];
47+
int nrounds;
48+
};
49+
50+
static __always_inline void speck128_round(u64 *x, u64 *y, u64 k)
51+
{
52+
*x = ror64(*x, 8);
53+
*x += *y;
54+
*x ^= k;
55+
*y = rol64(*y, 3);
56+
*y ^= *x;
57+
}
58+
59+
static __always_inline void speck128_unround(u64 *x, u64 *y, u64 k)
60+
{
61+
*y ^= *x;
62+
*y = ror64(*y, 3);
63+
*x ^= k;
64+
*x -= *y;
65+
*x = rol64(*x, 8);
66+
}
67+
68+
static void speck128_encrypt(struct crypto_tfm *tfm, u8 *out, const u8 *in)
69+
{
70+
const struct speck128_tfm_ctx *ctx = crypto_tfm_ctx(tfm);
71+
u64 y = get_unaligned_le64(in);
72+
u64 x = get_unaligned_le64(in + 8);
73+
int i;
74+
75+
for (i = 0; i < ctx->nrounds; i++)
76+
speck128_round(&x, &y, ctx->round_keys[i]);
77+
78+
put_unaligned_le64(y, out);
79+
put_unaligned_le64(x, out + 8);
80+
}
81+
82+
static void speck128_decrypt(struct crypto_tfm *tfm, u8 *out, const u8 *in)
83+
{
84+
const struct speck128_tfm_ctx *ctx = crypto_tfm_ctx(tfm);
85+
u64 y = get_unaligned_le64(in);
86+
u64 x = get_unaligned_le64(in + 8);
87+
int i;
88+
89+
for (i = ctx->nrounds - 1; i >= 0; i--)
90+
speck128_unround(&x, &y, ctx->round_keys[i]);
91+
92+
put_unaligned_le64(y, out);
93+
put_unaligned_le64(x, out + 8);
94+
}
95+
96+
static int speck128_setkey(struct crypto_tfm *tfm, const u8 *key,
97+
unsigned int keylen)
98+
{
99+
struct speck128_tfm_ctx *ctx = crypto_tfm_ctx(tfm);
100+
u64 l[3];
101+
u64 k;
102+
int i;
103+
104+
switch (keylen) {
105+
case SPECK128_128_KEY_SIZE:
106+
k = get_unaligned_le64(key);
107+
l[0] = get_unaligned_le64(key + 8);
108+
ctx->nrounds = SPECK128_128_NROUNDS;
109+
for (i = 0; i < ctx->nrounds; i++) {
110+
ctx->round_keys[i] = k;
111+
speck128_round(&l[0], &k, i);
112+
}
113+
break;
114+
case SPECK128_192_KEY_SIZE:
115+
k = get_unaligned_le64(key);
116+
l[0] = get_unaligned_le64(key + 8);
117+
l[1] = get_unaligned_le64(key + 16);
118+
ctx->nrounds = SPECK128_192_NROUNDS;
119+
for (i = 0; i < ctx->nrounds; i++) {
120+
ctx->round_keys[i] = k;
121+
speck128_round(&l[i % 2], &k, i);
122+
}
123+
break;
124+
case SPECK128_256_KEY_SIZE:
125+
k = get_unaligned_le64(key);
126+
l[0] = get_unaligned_le64(key + 8);
127+
l[1] = get_unaligned_le64(key + 16);
128+
l[2] = get_unaligned_le64(key + 24);
129+
ctx->nrounds = SPECK128_256_NROUNDS;
130+
for (i = 0; i < ctx->nrounds; i++) {
131+
ctx->round_keys[i] = k;
132+
speck128_round(&l[i % 3], &k, i);
133+
}
134+
break;
135+
default:
136+
return -EINVAL;
137+
}
138+
139+
return 0;
140+
}
141+
142+
/* Speck64 */
143+
144+
#define SPECK64_BLOCK_SIZE 8
145+
146+
#define SPECK64_96_KEY_SIZE 12
147+
#define SPECK64_96_NROUNDS 26
148+
149+
#define SPECK64_128_KEY_SIZE 16
150+
#define SPECK64_128_NROUNDS 27
151+
152+
struct speck64_tfm_ctx {
153+
u32 round_keys[SPECK64_128_NROUNDS];
154+
int nrounds;
155+
};
156+
157+
static __always_inline void speck64_round(u32 *x, u32 *y, u32 k)
158+
{
159+
*x = ror32(*x, 8);
160+
*x += *y;
161+
*x ^= k;
162+
*y = rol32(*y, 3);
163+
*y ^= *x;
164+
}
165+
166+
static __always_inline void speck64_unround(u32 *x, u32 *y, u32 k)
167+
{
168+
*y ^= *x;
169+
*y = ror32(*y, 3);
170+
*x ^= k;
171+
*x -= *y;
172+
*x = rol32(*x, 8);
173+
}
174+
175+
static void speck64_encrypt(struct crypto_tfm *tfm, u8 *out, const u8 *in)
176+
{
177+
const struct speck64_tfm_ctx *ctx = crypto_tfm_ctx(tfm);
178+
u32 y = get_unaligned_le32(in);
179+
u32 x = get_unaligned_le32(in + 4);
180+
int i;
181+
182+
for (i = 0; i < ctx->nrounds; i++)
183+
speck64_round(&x, &y, ctx->round_keys[i]);
184+
185+
put_unaligned_le32(y, out);
186+
put_unaligned_le32(x, out + 4);
187+
}
188+
189+
static void speck64_decrypt(struct crypto_tfm *tfm, u8 *out, const u8 *in)
190+
{
191+
const struct speck64_tfm_ctx *ctx = crypto_tfm_ctx(tfm);
192+
u32 y = get_unaligned_le32(in);
193+
u32 x = get_unaligned_le32(in + 4);
194+
int i;
195+
196+
for (i = ctx->nrounds - 1; i >= 0; i--)
197+
speck64_unround(&x, &y, ctx->round_keys[i]);
198+
199+
put_unaligned_le32(y, out);
200+
put_unaligned_le32(x, out + 4);
201+
}
202+
203+
static int speck64_setkey(struct crypto_tfm *tfm, const u8 *key,
204+
unsigned int keylen)
205+
{
206+
struct speck64_tfm_ctx *ctx = crypto_tfm_ctx(tfm);
207+
u32 l[3];
208+
u32 k;
209+
int i;
210+
211+
switch (keylen) {
212+
case SPECK64_96_KEY_SIZE:
213+
k = get_unaligned_le32(key);
214+
l[0] = get_unaligned_le32(key + 4);
215+
l[1] = get_unaligned_le32(key + 8);
216+
ctx->nrounds = SPECK64_96_NROUNDS;
217+
for (i = 0; i < ctx->nrounds; i++) {
218+
ctx->round_keys[i] = k;
219+
speck64_round(&l[i % 2], &k, i);
220+
}
221+
break;
222+
case SPECK64_128_KEY_SIZE:
223+
k = get_unaligned_le32(key);
224+
l[0] = get_unaligned_le32(key + 4);
225+
l[1] = get_unaligned_le32(key + 8);
226+
l[2] = get_unaligned_le32(key + 12);
227+
ctx->nrounds = SPECK64_128_NROUNDS;
228+
for (i = 0; i < ctx->nrounds; i++) {
229+
ctx->round_keys[i] = k;
230+
speck64_round(&l[i % 3], &k, i);
231+
}
232+
break;
233+
default:
234+
return -EINVAL;
235+
}
236+
237+
return 0;
238+
}
239+
240+
/* Algorithm definitions */
241+
242+
static struct crypto_alg speck_algs[] = {
243+
{
244+
.cra_name = "speck128",
245+
.cra_driver_name = "speck128-generic",
246+
.cra_priority = 100,
247+
.cra_flags = CRYPTO_ALG_TYPE_CIPHER,
248+
.cra_blocksize = SPECK128_BLOCK_SIZE,
249+
.cra_ctxsize = sizeof(struct speck128_tfm_ctx),
250+
.cra_module = THIS_MODULE,
251+
.cra_u = {
252+
.cipher = {
253+
.cia_min_keysize = SPECK128_128_KEY_SIZE,
254+
.cia_max_keysize = SPECK128_256_KEY_SIZE,
255+
.cia_setkey = speck128_setkey,
256+
.cia_encrypt = speck128_encrypt,
257+
.cia_decrypt = speck128_decrypt
258+
}
259+
}
260+
}, {
261+
.cra_name = "speck64",
262+
.cra_driver_name = "speck64-generic",
263+
.cra_priority = 100,
264+
.cra_flags = CRYPTO_ALG_TYPE_CIPHER,
265+
.cra_blocksize = SPECK64_BLOCK_SIZE,
266+
.cra_ctxsize = sizeof(struct speck64_tfm_ctx),
267+
.cra_module = THIS_MODULE,
268+
.cra_u = {
269+
.cipher = {
270+
.cia_min_keysize = SPECK64_96_KEY_SIZE,
271+
.cia_max_keysize = SPECK64_128_KEY_SIZE,
272+
.cia_setkey = speck64_setkey,
273+
.cia_encrypt = speck64_encrypt,
274+
.cia_decrypt = speck64_decrypt
275+
}
276+
}
277+
}
278+
};
279+
280+
static int __init speck_module_init(void)
281+
{
282+
return crypto_register_algs(speck_algs, ARRAY_SIZE(speck_algs));
283+
}
284+
285+
static void __exit speck_module_exit(void)
286+
{
287+
crypto_unregister_algs(speck_algs, ARRAY_SIZE(speck_algs));
288+
}
289+
290+
module_init(speck_module_init);
291+
module_exit(speck_module_exit);
292+
293+
MODULE_DESCRIPTION("Speck block cipher (generic)");
294+
MODULE_LICENSE("GPL");
295+
MODULE_AUTHOR("Eric Biggers <[email protected]>");
296+
MODULE_ALIAS_CRYPTO("speck128");
297+
MODULE_ALIAS_CRYPTO("speck128-generic");
298+
MODULE_ALIAS_CRYPTO("speck64");
299+
MODULE_ALIAS_CRYPTO("speck64-generic");

crypto/testmgr.c

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3000,6 +3000,24 @@ static const struct alg_test_desc alg_test_descs[] = {
30003000
.dec = __VECS(serpent_dec_tv_template)
30013001
}
30023002
}
3003+
}, {
3004+
.alg = "ecb(speck128)",
3005+
.test = alg_test_skcipher,
3006+
.suite = {
3007+
.cipher = {
3008+
.enc = __VECS(speck128_enc_tv_template),
3009+
.dec = __VECS(speck128_dec_tv_template)
3010+
}
3011+
}
3012+
}, {
3013+
.alg = "ecb(speck64)",
3014+
.test = alg_test_skcipher,
3015+
.suite = {
3016+
.cipher = {
3017+
.enc = __VECS(speck64_enc_tv_template),
3018+
.dec = __VECS(speck64_dec_tv_template)
3019+
}
3020+
}
30033021
}, {
30043022
.alg = "ecb(tea)",
30053023
.test = alg_test_skcipher,

0 commit comments

Comments
 (0)