linux/crypto/speck.c
<<
>>
Prefs
   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 <crypto/speck.h>
  28#include <linux/bitops.h>
  29#include <linux/crypto.h>
  30#include <linux/init.h>
  31#include <linux/module.h>
  32
  33/* Speck128 */
  34
  35static __always_inline void speck128_round(u64 *x, u64 *y, u64 k)
  36{
  37        *x = ror64(*x, 8);
  38        *x += *y;
  39        *x ^= k;
  40        *y = rol64(*y, 3);
  41        *y ^= *x;
  42}
  43
  44static __always_inline void speck128_unround(u64 *x, u64 *y, u64 k)
  45{
  46        *y ^= *x;
  47        *y = ror64(*y, 3);
  48        *x ^= k;
  49        *x -= *y;
  50        *x = rol64(*x, 8);
  51}
  52
  53void crypto_speck128_encrypt(const struct speck128_tfm_ctx *ctx,
  54                             u8 *out, const u8 *in)
  55{
  56        u64 y = get_unaligned_le64(in);
  57        u64 x = get_unaligned_le64(in + 8);
  58        int i;
  59
  60        for (i = 0; i < ctx->nrounds; i++)
  61                speck128_round(&x, &y, ctx->round_keys[i]);
  62
  63        put_unaligned_le64(y, out);
  64        put_unaligned_le64(x, out + 8);
  65}
  66EXPORT_SYMBOL_GPL(crypto_speck128_encrypt);
  67
  68static void speck128_encrypt(struct crypto_tfm *tfm, u8 *out, const u8 *in)
  69{
  70        crypto_speck128_encrypt(crypto_tfm_ctx(tfm), out, in);
  71}
  72
  73void crypto_speck128_decrypt(const struct speck128_tfm_ctx *ctx,
  74                             u8 *out, const u8 *in)
  75{
  76        u64 y = get_unaligned_le64(in);
  77        u64 x = get_unaligned_le64(in + 8);
  78        int i;
  79
  80        for (i = ctx->nrounds - 1; i >= 0; i--)
  81                speck128_unround(&x, &y, ctx->round_keys[i]);
  82
  83        put_unaligned_le64(y, out);
  84        put_unaligned_le64(x, out + 8);
  85}
  86EXPORT_SYMBOL_GPL(crypto_speck128_decrypt);
  87
  88static void speck128_decrypt(struct crypto_tfm *tfm, u8 *out, const u8 *in)
  89{
  90        crypto_speck128_decrypt(crypto_tfm_ctx(tfm), out, in);
  91}
  92
  93int crypto_speck128_setkey(struct speck128_tfm_ctx *ctx, const u8 *key,
  94                           unsigned int keylen)
  95{
  96        u64 l[3];
  97        u64 k;
  98        int i;
  99
 100        switch (keylen) {
 101        case SPECK128_128_KEY_SIZE:
 102                k = get_unaligned_le64(key);
 103                l[0] = get_unaligned_le64(key + 8);
 104                ctx->nrounds = SPECK128_128_NROUNDS;
 105                for (i = 0; i < ctx->nrounds; i++) {
 106                        ctx->round_keys[i] = k;
 107                        speck128_round(&l[0], &k, i);
 108                }
 109                break;
 110        case SPECK128_192_KEY_SIZE:
 111                k = get_unaligned_le64(key);
 112                l[0] = get_unaligned_le64(key + 8);
 113                l[1] = get_unaligned_le64(key + 16);
 114                ctx->nrounds = SPECK128_192_NROUNDS;
 115                for (i = 0; i < ctx->nrounds; i++) {
 116                        ctx->round_keys[i] = k;
 117                        speck128_round(&l[i % 2], &k, i);
 118                }
 119                break;
 120        case SPECK128_256_KEY_SIZE:
 121                k = get_unaligned_le64(key);
 122                l[0] = get_unaligned_le64(key + 8);
 123                l[1] = get_unaligned_le64(key + 16);
 124                l[2] = get_unaligned_le64(key + 24);
 125                ctx->nrounds = SPECK128_256_NROUNDS;
 126                for (i = 0; i < ctx->nrounds; i++) {
 127                        ctx->round_keys[i] = k;
 128                        speck128_round(&l[i % 3], &k, i);
 129                }
 130                break;
 131        default:
 132                return -EINVAL;
 133        }
 134
 135        return 0;
 136}
 137EXPORT_SYMBOL_GPL(crypto_speck128_setkey);
 138
 139static int speck128_setkey(struct crypto_tfm *tfm, const u8 *key,
 140                           unsigned int keylen)
 141{
 142        return crypto_speck128_setkey(crypto_tfm_ctx(tfm), key, keylen);
 143}
 144
 145/* Speck64 */
 146
 147static __always_inline void speck64_round(u32 *x, u32 *y, u32 k)
 148{
 149        *x = ror32(*x, 8);
 150        *x += *y;
 151        *x ^= k;
 152        *y = rol32(*y, 3);
 153        *y ^= *x;
 154}
 155
 156static __always_inline void speck64_unround(u32 *x, u32 *y, u32 k)
 157{
 158        *y ^= *x;
 159        *y = ror32(*y, 3);
 160        *x ^= k;
 161        *x -= *y;
 162        *x = rol32(*x, 8);
 163}
 164
 165void crypto_speck64_encrypt(const struct speck64_tfm_ctx *ctx,
 166                            u8 *out, const u8 *in)
 167{
 168        u32 y = get_unaligned_le32(in);
 169        u32 x = get_unaligned_le32(in + 4);
 170        int i;
 171
 172        for (i = 0; i < ctx->nrounds; i++)
 173                speck64_round(&x, &y, ctx->round_keys[i]);
 174
 175        put_unaligned_le32(y, out);
 176        put_unaligned_le32(x, out + 4);
 177}
 178EXPORT_SYMBOL_GPL(crypto_speck64_encrypt);
 179
 180static void speck64_encrypt(struct crypto_tfm *tfm, u8 *out, const u8 *in)
 181{
 182        crypto_speck64_encrypt(crypto_tfm_ctx(tfm), out, in);
 183}
 184
 185void crypto_speck64_decrypt(const struct speck64_tfm_ctx *ctx,
 186                            u8 *out, const u8 *in)
 187{
 188        u32 y = get_unaligned_le32(in);
 189        u32 x = get_unaligned_le32(in + 4);
 190        int i;
 191
 192        for (i = ctx->nrounds - 1; i >= 0; i--)
 193                speck64_unround(&x, &y, ctx->round_keys[i]);
 194
 195        put_unaligned_le32(y, out);
 196        put_unaligned_le32(x, out + 4);
 197}
 198EXPORT_SYMBOL_GPL(crypto_speck64_decrypt);
 199
 200static void speck64_decrypt(struct crypto_tfm *tfm, u8 *out, const u8 *in)
 201{
 202        crypto_speck64_decrypt(crypto_tfm_ctx(tfm), out, in);
 203}
 204
 205int crypto_speck64_setkey(struct speck64_tfm_ctx *ctx, const u8 *key,
 206                          unsigned int keylen)
 207{
 208        u32 l[3];
 209        u32 k;
 210        int i;
 211
 212        switch (keylen) {
 213        case SPECK64_96_KEY_SIZE:
 214                k = get_unaligned_le32(key);
 215                l[0] = get_unaligned_le32(key + 4);
 216                l[1] = get_unaligned_le32(key + 8);
 217                ctx->nrounds = SPECK64_96_NROUNDS;
 218                for (i = 0; i < ctx->nrounds; i++) {
 219                        ctx->round_keys[i] = k;
 220                        speck64_round(&l[i % 2], &k, i);
 221                }
 222                break;
 223        case SPECK64_128_KEY_SIZE:
 224                k = get_unaligned_le32(key);
 225                l[0] = get_unaligned_le32(key + 4);
 226                l[1] = get_unaligned_le32(key + 8);
 227                l[2] = get_unaligned_le32(key + 12);
 228                ctx->nrounds = SPECK64_128_NROUNDS;
 229                for (i = 0; i < ctx->nrounds; i++) {
 230                        ctx->round_keys[i] = k;
 231                        speck64_round(&l[i % 3], &k, i);
 232                }
 233                break;
 234        default:
 235                return -EINVAL;
 236        }
 237
 238        return 0;
 239}
 240EXPORT_SYMBOL_GPL(crypto_speck64_setkey);
 241
 242static int speck64_setkey(struct crypto_tfm *tfm, const u8 *key,
 243                          unsigned int keylen)
 244{
 245        return crypto_speck64_setkey(crypto_tfm_ctx(tfm), key, keylen);
 246}
 247
 248/* Algorithm definitions */
 249
 250static struct crypto_alg speck_algs[] = {
 251        {
 252                .cra_name               = "speck128",
 253                .cra_driver_name        = "speck128-generic",
 254                .cra_priority           = 100,
 255                .cra_flags              = CRYPTO_ALG_TYPE_CIPHER,
 256                .cra_blocksize          = SPECK128_BLOCK_SIZE,
 257                .cra_ctxsize            = sizeof(struct speck128_tfm_ctx),
 258                .cra_module             = THIS_MODULE,
 259                .cra_u                  = {
 260                        .cipher = {
 261                                .cia_min_keysize        = SPECK128_128_KEY_SIZE,
 262                                .cia_max_keysize        = SPECK128_256_KEY_SIZE,
 263                                .cia_setkey             = speck128_setkey,
 264                                .cia_encrypt            = speck128_encrypt,
 265                                .cia_decrypt            = speck128_decrypt
 266                        }
 267                }
 268        }, {
 269                .cra_name               = "speck64",
 270                .cra_driver_name        = "speck64-generic",
 271                .cra_priority           = 100,
 272                .cra_flags              = CRYPTO_ALG_TYPE_CIPHER,
 273                .cra_blocksize          = SPECK64_BLOCK_SIZE,
 274                .cra_ctxsize            = sizeof(struct speck64_tfm_ctx),
 275                .cra_module             = THIS_MODULE,
 276                .cra_u                  = {
 277                        .cipher = {
 278                                .cia_min_keysize        = SPECK64_96_KEY_SIZE,
 279                                .cia_max_keysize        = SPECK64_128_KEY_SIZE,
 280                                .cia_setkey             = speck64_setkey,
 281                                .cia_encrypt            = speck64_encrypt,
 282                                .cia_decrypt            = speck64_decrypt
 283                        }
 284                }
 285        }
 286};
 287
 288static int __init speck_module_init(void)
 289{
 290        return crypto_register_algs(speck_algs, ARRAY_SIZE(speck_algs));
 291}
 292
 293static void __exit speck_module_exit(void)
 294{
 295        crypto_unregister_algs(speck_algs, ARRAY_SIZE(speck_algs));
 296}
 297
 298module_init(speck_module_init);
 299module_exit(speck_module_exit);
 300
 301MODULE_DESCRIPTION("Speck block cipher (generic)");
 302MODULE_LICENSE("GPL");
 303MODULE_AUTHOR("Eric Biggers <ebiggers@google.com>");
 304MODULE_ALIAS_CRYPTO("speck128");
 305MODULE_ALIAS_CRYPTO("speck128-generic");
 306MODULE_ALIAS_CRYPTO("speck64");
 307MODULE_ALIAS_CRYPTO("speck64-generic");
 308