linux/crypto/chacha20_generic.c
<<
>>
Prefs
   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
  18static inline u32 le32_to_cpuvp(const void *p)
  19{
  20        return le32_to_cpup(p);
  21}
  22
  23static void chacha20_docrypt(u32 *state, u8 *dst, const u8 *src,
  24                             unsigned int bytes)
  25{
  26        u8 stream[CHACHA20_BLOCK_SIZE];
  27
  28        if (dst != src)
  29                memcpy(dst, src, bytes);
  30
  31        while (bytes >= CHACHA20_BLOCK_SIZE) {
  32                chacha20_block(state, stream);
  33                crypto_xor(dst, stream, CHACHA20_BLOCK_SIZE);
  34                bytes -= CHACHA20_BLOCK_SIZE;
  35                dst += CHACHA20_BLOCK_SIZE;
  36        }
  37        if (bytes) {
  38                chacha20_block(state, stream);
  39                crypto_xor(dst, stream, bytes);
  40        }
  41}
  42
  43void crypto_chacha20_init(u32 *state, struct chacha20_ctx *ctx, u8 *iv)
  44{
  45        static const char constant[16] = "expand 32-byte k";
  46
  47        state[0]  = le32_to_cpuvp(constant +  0);
  48        state[1]  = le32_to_cpuvp(constant +  4);
  49        state[2]  = le32_to_cpuvp(constant +  8);
  50        state[3]  = le32_to_cpuvp(constant + 12);
  51        state[4]  = ctx->key[0];
  52        state[5]  = ctx->key[1];
  53        state[6]  = ctx->key[2];
  54        state[7]  = ctx->key[3];
  55        state[8]  = ctx->key[4];
  56        state[9]  = ctx->key[5];
  57        state[10] = ctx->key[6];
  58        state[11] = ctx->key[7];
  59        state[12] = le32_to_cpuvp(iv +  0);
  60        state[13] = le32_to_cpuvp(iv +  4);
  61        state[14] = le32_to_cpuvp(iv +  8);
  62        state[15] = le32_to_cpuvp(iv + 12);
  63}
  64EXPORT_SYMBOL_GPL(crypto_chacha20_init);
  65
  66int crypto_chacha20_setkey(struct crypto_tfm *tfm, const u8 *key,
  67                           unsigned int keysize)
  68{
  69        struct chacha20_ctx *ctx = crypto_tfm_ctx(tfm);
  70        int i;
  71
  72        if (keysize != CHACHA20_KEY_SIZE)
  73                return -EINVAL;
  74
  75        for (i = 0; i < ARRAY_SIZE(ctx->key); i++)
  76                ctx->key[i] = le32_to_cpuvp(key + i * sizeof(u32));
  77
  78        return 0;
  79}
  80EXPORT_SYMBOL_GPL(crypto_chacha20_setkey);
  81
  82int crypto_chacha20_crypt(struct blkcipher_desc *desc, struct scatterlist *dst,
  83                          struct scatterlist *src, unsigned int nbytes)
  84{
  85        struct blkcipher_walk walk;
  86        u32 state[16];
  87        int err;
  88
  89        blkcipher_walk_init(&walk, dst, src, nbytes);
  90        err = blkcipher_walk_virt_block(desc, &walk, CHACHA20_BLOCK_SIZE);
  91
  92        crypto_chacha20_init(state, crypto_blkcipher_ctx(desc->tfm), walk.iv);
  93
  94        while (walk.nbytes >= CHACHA20_BLOCK_SIZE) {
  95                chacha20_docrypt(state, walk.dst.virt.addr, walk.src.virt.addr,
  96                                 rounddown(walk.nbytes, CHACHA20_BLOCK_SIZE));
  97                err = blkcipher_walk_done(desc, &walk,
  98                                          walk.nbytes % CHACHA20_BLOCK_SIZE);
  99        }
 100
 101        if (walk.nbytes) {
 102                chacha20_docrypt(state, walk.dst.virt.addr, walk.src.virt.addr,
 103                                 walk.nbytes);
 104                err = blkcipher_walk_done(desc, &walk, 0);
 105        }
 106
 107        return err;
 108}
 109EXPORT_SYMBOL_GPL(crypto_chacha20_crypt);
 110
 111static struct crypto_alg alg = {
 112        .cra_name               = "chacha20",
 113        .cra_driver_name        = "chacha20-generic",
 114        .cra_priority           = 100,
 115        .cra_flags              = CRYPTO_ALG_TYPE_BLKCIPHER,
 116        .cra_blocksize          = 1,
 117        .cra_type               = &crypto_blkcipher_type,
 118        .cra_ctxsize            = sizeof(struct chacha20_ctx),
 119        .cra_alignmask          = sizeof(u32) - 1,
 120        .cra_module             = THIS_MODULE,
 121        .cra_u                  = {
 122                .blkcipher = {
 123                        .min_keysize    = CHACHA20_KEY_SIZE,
 124                        .max_keysize    = CHACHA20_KEY_SIZE,
 125                        .ivsize         = CHACHA20_IV_SIZE,
 126                        .geniv          = "seqiv",
 127                        .setkey         = crypto_chacha20_setkey,
 128                        .encrypt        = crypto_chacha20_crypt,
 129                        .decrypt        = crypto_chacha20_crypt,
 130                },
 131        },
 132};
 133
 134static int __init chacha20_generic_mod_init(void)
 135{
 136        return crypto_register_alg(&alg);
 137}
 138
 139static void __exit chacha20_generic_mod_fini(void)
 140{
 141        crypto_unregister_alg(&alg);
 142}
 143
 144module_init(chacha20_generic_mod_init);
 145module_exit(chacha20_generic_mod_fini);
 146
 147MODULE_LICENSE("GPL");
 148MODULE_AUTHOR("Martin Willi <martin@strongswan.org>");
 149MODULE_DESCRIPTION("chacha20 cipher algorithm");
 150MODULE_ALIAS_CRYPTO("chacha20");
 151MODULE_ALIAS_CRYPTO("chacha20-generic");
 152