chacha20_generic.c 3.9 KB

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