2020年3月29日日曜日

ChaCha

はじめに

アルゴリズムやコードを使用するにあたり, 著作権や特許権の調査には手間がかかります.
ストリーム暗号ChaChaは, 特許的に訴えられる可能性がかなり低いアルゴリズムと思われます.
著作権的にもRFCやWikipediaからコードを実装すれば訴えられる可能性は低いです.
RFCやWikipediaから実装すると, コードが似てしまうのはどうしようもないと思います.

ストリーム暗号は, 入力された文をバイト単位などで逐次, 暗号化・複合する暗号アルゴリズムを指します. RC4またはARCFOURが有名です.
ストリーム暗号は, 事前に文の長さを知る必要がなく, 文の長さに制限もありません. ブロック暗号は, 文を固定長のブロックに分割して暗号化・複合する暗号アルゴリズムを指します. AESが有名です.
固定長のブロックごとに分割するため, 文が足りない場合に何かしらの規則でデータを埋める必要があります.

ChaChaの実装コードをここに書き留めておきますが, "これをコピーしました"と言っても, 簡単に使えないのだろうなと, 虚しい気持ちになります.
初期ベクトル, nonceを, 用途によって設定できるインターフェイスを用意すべきでしょう.
鍵の長さについても, 任意の長さの鍵を256bitに合わせるインターフェイスを用意すべきでしょう.

実装

実装
  1. #ifndef INC_CHACHA_H_
  2. /**
  3. @file ChaCha.h
  4. @author taqu
  5.  
  6. USAGE:
  7. Put '#define CHACHA_IMPLEMENTATION' before including this file to create the implementation.
  8. */
  9. #ifdef __cplusplus
  10. #include <cstdint>
  11. #else
  12. #include <stdint.h>
  13. #endif
  14.  
  15. #ifdef __cplusplus
  16. namespace cppchacha
  17. {
  18. #endif
  19.  
  20. void chacha20_encrypt256(const uint32_t key[8], const uint32_t nonce[2], uint32_t size, uint8_t* message);
  21.  
  22. #ifdef __cplusplus
  23. }
  24. #endif
  25. #endif //INC_CHACHA_H_
  26.  
  27. #ifdef CHACHA_IMPLEMENTATION
  28.  
  29. #define ROTL(a,b) (((a)<<(b)) | ((a)>>(32-(b))))
  30. #define QR(a,b,c,d) (\
  31. a += b, d ^= a, d = ROTL(d,16),\
  32. c += d, b ^= c, b = ROTL(b,12),\
  33. a += b, d ^= a, d = ROTL(d, 8),\
  34. c += d, b ^= c, b = ROTL(b, 7))
  35.  
  36. #ifdef __cplusplus
  37. namespace cppchacha
  38. {
  39. #endif
  40.  
  41. #ifdef __cplusplus
  42. namespace
  43. {
  44. #define CHACHA_STATIC
  45. #define CHACHA_CAST(type, ptr) reinterpret_cast<type>(ptr)
  46.  
  47. #else //__cplusplus
  48. #define CHACHA_STATIC static
  49. #define CHACHA_CAST(type, ptr) (type)(ptr)
  50.  
  51. #endif //__cplusplus
  52.  
  53. //'expa', 'nd 3', '2-by', 'te k'
  54. static const uint32_t iv_32[4] = {0x65787061U, 0x6e642033U, 0x322d6279U, 0x7465206bU};
  55.  
  56. CHACHA_STATIC void chacha20_block(uint32_t out[16], const uint32_t in[16])
  57. {
  58. uint32_t x[16];
  59. for(int32_t i=0; i<16; ++i){
  60. x[i] = in[i];
  61. }
  62. for(int32_t i=0; i<10; ++i){
  63. //Odd round
  64. QR(x[0], x[4], x[8], x[12]); //Column 0
  65. QR(x[1], x[5], x[9], x[13]); //Column 1
  66. QR(x[2], x[6], x[10], x[14]); //Column 2
  67. QR(x[3], x[7], x[11], x[15]); //Column 3
  68. //Even round
  69. QR(x[0], x[5], x[10], x[15]); //Diagonal 0
  70. QR(x[1], x[6], x[11], x[12]); //Diagonal 1
  71. QR(x[2], x[7], x[8], x[13]); //Diagonal 2
  72. QR(x[3], x[4], x[9], x[14]); //Diagonal 3
  73. }
  74. for(int32_t i=0; i<16; ++i){
  75. out[i] = x[i] + in[i];
  76. }
  77. }
  78.  
  79. void chacha20_encrypt(uint32_t input[16], uint32_t size, uint8_t* message)
  80. {
  81. uint32_t output[16];
  82. uint32_t* p = CHACHA_CAST(uint32_t*, message);
  83. uint64_t* counter = CHACHA_CAST(uint64_t*, &input[12]);
  84. for(;;){
  85. chacha20_block(output, input);
  86. *counter += 1;
  87. if(size<=64){
  88. uint8_t* pb = CHACHA_CAST(uint8_t*, p);
  89. uint8_t* po = CHACHA_CAST(uint8_t*, output);
  90. for(uint32_t i=0; i<size; ++i){
  91. pb[i] = pb[i] ^ po[i];
  92. }
  93. break;
  94. }else{
  95. for(uint32_t i = 0; i < 16; ++i){
  96. p[i] ^= output[i];
  97. }
  98. size -= 64;
  99. p += 16;
  100. }
  101. }
  102. }
  103.  
  104. #ifdef __cplusplus
  105. }
  106. #endif
  107.  
  108. void chacha20_encrypt256(const uint32_t key[8], const uint32_t nonce[2], uint32_t size, uint8_t* message)
  109. {
  110. uint32_t input[16];
  111.  
  112. for(int32_t i=0; i<4; ++i){
  113. input[i] = iv_32[i];
  114. }
  115.  
  116. for(int32_t i=0; i<8; ++i){
  117. input[4+i] = key[i];
  118. }
  119.  
  120. input[12] = 0;
  121. input[13] = 0;
  122. input[14] = nonce[0];
  123. input[15] = nonce[1];
  124. chacha20_encrypt(input, size, message);
  125. }
  126.  
  127. #ifdef __cplusplus
  128. }
  129. #endif
  130.  
  131. #endif //CHACHA_IMPLEMENTATION

使い方

使い方
#define CHACHA_IMPLEMENTATION
#include <string.h>
#include <assert.h>
#include <random>
#include <chrono>
#include <iostream>
#include "ChaCha.h"

int main(int, char**)
{
    using namespace cppchacha;
    const uint8_t key[8*4] = {
        0x23U, 0xADU, 0x52U, 0xB1U, 0x5FU, 0xA7U, 0xEBU, 0xDCU,
        0x46U, 0x72U, 0xD7U, 0x22U, 0x89U, 0x25U, 0x3DU, 0x95U,
        0xDCU, 0x9AU, 0x43U, 0x24U, 0xFCU, 0x36U, 0x9FU, 0x59U,
        0x3FU, 0xDCU, 0xC7U, 0x73U, 0x3AU, 0xD7U, 0x76U, 0x17};
    const uint8_t nonce[2*4] = {
        0x5AU, 0x5FU, 0x6CU, 0x13U, 0xC1U, 0xF1U, 0x26U, 0x53U};

    std::random_device seed_gen;
 std::mt19937 engine(seed_gen());

    int32_t length = 1025 * 1025;
    char* message0 = reinterpret_cast<char*>(malloc(length*sizeof(char)));
    char* message1 = reinterpret_cast<char*>(malloc(length*sizeof(char)));
    for(int32_t i=0; i<length; ++i){
        message0[i] = static_cast<char>(engine());
    }

    memcpy(message1, message0, length);

    std::chrono::high_resolution_clock::time_point begin;
    std::chrono::microseconds elapsed_time;

    begin = std::chrono::high_resolution_clock::now();
    chacha20_encrypt256(reinterpret_cast<const uint32_t*>(key), reinterpret_cast<const uint32_t*>(nonce), length, reinterpret_cast<uint8_t*>(message1));
    chacha20_encrypt256(reinterpret_cast<const uint32_t*>(key), reinterpret_cast<const uint32_t*>(nonce), length, reinterpret_cast<uint8_t*>(message1));
    elapsed_time = std::chrono::duration_cast<std::chrono::microseconds>(std::chrono::high_resolution_clock::now() - begin);
    std::cout << elapsed_time.count() << std::endl;

    for(int32_t i=0; i<length; ++i){
        assert(message0[i] == message1[i]);
    }
    free(message1);
    free(message0);
    return 0;
}