Back to home page

LXR

 
 

    


0001 /*
0002  * ChaCha20 256-bit cipher algorithm, RFC7539
0003  *
0004  * Copyright (C) 2015 Martin Willi
0005  *
0006  * This program is free software; you can redistribute it and/or modify
0007  * it under the terms of the GNU General Public License as published by
0008  * the Free Software Foundation; either version 2 of the License, or
0009  * (at your option) any later version.
0010  */
0011 
0012 #include <crypto/algapi.h>
0013 #include <linux/crypto.h>
0014 #include <linux/kernel.h>
0015 #include <linux/module.h>
0016 #include <crypto/chacha20.h>
0017 
0018 static inline u32 le32_to_cpuvp(const void *p)
0019 {
0020     return le32_to_cpup(p);
0021 }
0022 
0023 static void chacha20_docrypt(u32 *state, u8 *dst, const u8 *src,
0024                  unsigned int bytes)
0025 {
0026     u8 stream[CHACHA20_BLOCK_SIZE];
0027 
0028     if (dst != src)
0029         memcpy(dst, src, bytes);
0030 
0031     while (bytes >= CHACHA20_BLOCK_SIZE) {
0032         chacha20_block(state, stream);
0033         crypto_xor(dst, stream, CHACHA20_BLOCK_SIZE);
0034         bytes -= CHACHA20_BLOCK_SIZE;
0035         dst += CHACHA20_BLOCK_SIZE;
0036     }
0037     if (bytes) {
0038         chacha20_block(state, stream);
0039         crypto_xor(dst, stream, bytes);
0040     }
0041 }
0042 
0043 void crypto_chacha20_init(u32 *state, struct chacha20_ctx *ctx, u8 *iv)
0044 {
0045     static const char constant[16] = "expand 32-byte k";
0046 
0047     state[0]  = le32_to_cpuvp(constant +  0);
0048     state[1]  = le32_to_cpuvp(constant +  4);
0049     state[2]  = le32_to_cpuvp(constant +  8);
0050     state[3]  = le32_to_cpuvp(constant + 12);
0051     state[4]  = ctx->key[0];
0052     state[5]  = ctx->key[1];
0053     state[6]  = ctx->key[2];
0054     state[7]  = ctx->key[3];
0055     state[8]  = ctx->key[4];
0056     state[9]  = ctx->key[5];
0057     state[10] = ctx->key[6];
0058     state[11] = ctx->key[7];
0059     state[12] = le32_to_cpuvp(iv +  0);
0060     state[13] = le32_to_cpuvp(iv +  4);
0061     state[14] = le32_to_cpuvp(iv +  8);
0062     state[15] = le32_to_cpuvp(iv + 12);
0063 }
0064 EXPORT_SYMBOL_GPL(crypto_chacha20_init);
0065 
0066 int crypto_chacha20_setkey(struct crypto_tfm *tfm, const u8 *key,
0067                unsigned int keysize)
0068 {
0069     struct chacha20_ctx *ctx = crypto_tfm_ctx(tfm);
0070     int i;
0071 
0072     if (keysize != CHACHA20_KEY_SIZE)
0073         return -EINVAL;
0074 
0075     for (i = 0; i < ARRAY_SIZE(ctx->key); i++)
0076         ctx->key[i] = le32_to_cpuvp(key + i * sizeof(u32));
0077 
0078     return 0;
0079 }
0080 EXPORT_SYMBOL_GPL(crypto_chacha20_setkey);
0081 
0082 int crypto_chacha20_crypt(struct blkcipher_desc *desc, struct scatterlist *dst,
0083               struct scatterlist *src, unsigned int nbytes)
0084 {
0085     struct blkcipher_walk walk;
0086     u32 state[16];
0087     int err;
0088 
0089     blkcipher_walk_init(&walk, dst, src, nbytes);
0090     err = blkcipher_walk_virt_block(desc, &walk, CHACHA20_BLOCK_SIZE);
0091 
0092     crypto_chacha20_init(state, crypto_blkcipher_ctx(desc->tfm), walk.iv);
0093 
0094     while (walk.nbytes >= CHACHA20_BLOCK_SIZE) {
0095         chacha20_docrypt(state, walk.dst.virt.addr, walk.src.virt.addr,
0096                  rounddown(walk.nbytes, CHACHA20_BLOCK_SIZE));
0097         err = blkcipher_walk_done(desc, &walk,
0098                       walk.nbytes % CHACHA20_BLOCK_SIZE);
0099     }
0100 
0101     if (walk.nbytes) {
0102         chacha20_docrypt(state, walk.dst.virt.addr, walk.src.virt.addr,
0103                  walk.nbytes);
0104         err = blkcipher_walk_done(desc, &walk, 0);
0105     }
0106 
0107     return err;
0108 }
0109 EXPORT_SYMBOL_GPL(crypto_chacha20_crypt);
0110 
0111 static struct crypto_alg alg = {
0112     .cra_name       = "chacha20",
0113     .cra_driver_name    = "chacha20-generic",
0114     .cra_priority       = 100,
0115     .cra_flags      = CRYPTO_ALG_TYPE_BLKCIPHER,
0116     .cra_blocksize      = 1,
0117     .cra_type       = &crypto_blkcipher_type,
0118     .cra_ctxsize        = sizeof(struct chacha20_ctx),
0119     .cra_alignmask      = sizeof(u32) - 1,
0120     .cra_module     = THIS_MODULE,
0121     .cra_u          = {
0122         .blkcipher = {
0123             .min_keysize    = CHACHA20_KEY_SIZE,
0124             .max_keysize    = CHACHA20_KEY_SIZE,
0125             .ivsize     = CHACHA20_IV_SIZE,
0126             .geniv      = "seqiv",
0127             .setkey     = crypto_chacha20_setkey,
0128             .encrypt    = crypto_chacha20_crypt,
0129             .decrypt    = crypto_chacha20_crypt,
0130         },
0131     },
0132 };
0133 
0134 static int __init chacha20_generic_mod_init(void)
0135 {
0136     return crypto_register_alg(&alg);
0137 }
0138 
0139 static void __exit chacha20_generic_mod_fini(void)
0140 {
0141     crypto_unregister_alg(&alg);
0142 }
0143 
0144 module_init(chacha20_generic_mod_init);
0145 module_exit(chacha20_generic_mod_fini);
0146 
0147 MODULE_LICENSE("GPL");
0148 MODULE_AUTHOR("Martin Willi <martin@strongswan.org>");
0149 MODULE_DESCRIPTION("chacha20 cipher algorithm");
0150 MODULE_ALIAS_CRYPTO("chacha20");
0151 MODULE_ALIAS_CRYPTO("chacha20-generic");