1 /// Implementation of ChaCha20 digester.
2 module tern.digest.chacha20;
3 
4 import tern.digest;
5 import tern.digest.circe;
6 
7 /**
8  * Implementation of ChaCha20 digester.
9  *
10  * ChaCha20 is a symmetric encryption algorithm designed to provide both high performance 
11  * and high security. It operates on 512-bit (64-byte) blocks and accepts a 256-bit (32-byte) 
12  * key and a 96-bit (12-byte) nonce.
13  *
14  * Example:
15  * ```d
16  * import tern.digest.chacha20;
17  *
18  * ubyte[] data = [1, 2, 3, 4, 5];
19  * string key = "my_secret_key"; // Must be exactly 256 bits (32 bytes) in length.
20  * ubyte[12] nonce = [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]; // Must be exactly 96 bits (12 bytes) in length.
21  * uint counter = 0;
22  * ChaCha20.encrypt(data, key, nonce, counter);
23  * ```
24  */
25 public static @digester class ChaCha20
26 {
27 private:
28 static:
29     void quarterRound(ref uint[16] block, uint a, uint b, uint c, uint d)
30     {
31         block[a] += block[b]; block[d] = (block[d] ^ block[a]) >>> 16;
32         block[c] += block[d]; block[b] = (block[b] ^ block[c]) >>> 12;
33         block[a] += block[b]; block[d] = (block[d] ^ block[a]) >>> 8;
34         block[c] += block[d]; block[b] = (block[b] ^ block[c]) >>> 7;
35     }
36 
37     uint[16] innerRound(ref uint[16] block)
38     {
39         foreach (i; 0 .. 10) 
40         {
41             quarterRound(block, 0, 4, 8, 12);
42             quarterRound(block, 1, 5, 9, 13);
43             quarterRound(block, 2, 6, 10, 14);
44             quarterRound(block, 3, 7, 11, 15);
45             quarterRound(block, 0, 5, 10, 15);
46             quarterRound(block, 1, 6, 11, 12);
47             quarterRound(block, 2, 7, 8, 13);
48             quarterRound(block, 3, 4, 9, 14);
49         }
50         return block;
51     }
52 
53 public:
54     /**
55      * Encrypts the given byte array `data`.
56      *
57      * Params:
58      *  data: Reference to the input byte array to be encrypted.
59      *  key: The encryption key. Must be exactly 256 bits (32 bytes) in length.
60      *  nonce: The nonce value. Must be exactly 96 bits (12 bytes) in length.
61      *  counter: The initial counter value. Defaults to 0.
62      */
63     void encrypt(ref ubyte[] data, string key, ubyte[12] nonce = (ubyte[12]).init, uint counter = 0)
64     {
65         assert(key.length == 32, "Key must be 256 bits!");
66         key = cast(string)Circe.hash(cast(ubyte[])key);
67 
68         uint[16] state;
69         state[0..4] = [0x61707865, 0x3320646e, 0x79622d32, 0x6b206574];
70         state[4..12] = *cast(uint[8]*)key.ptr;
71         state[12..13] = counter;
72         state[13..16] = cast(uint[3])nonce;
73 
74         uint[16] keyStream;
75         ubyte offset = 64;
76 
77         foreach (ref octet; data) 
78         {
79             if (offset >= 64) 
80             {
81                 keyStream = state.dup;
82                 innerRound(keyStream);
83                 // Counter
84                 state[12]++;
85                 offset = 0;
86             }
87 
88             octet ^= (cast(ubyte[64])keyStream)[offset];
89             offset++;
90         }
91     }
92 
93     /**
94      * Decrypts the given byte array `data`.
95      *
96      * Params:
97      *  data: Reference to the input byte array to be encrypted.
98      *  key: The encryption key. Must be exactly 256 bits (32 bytes) in length.
99      *  nonce: The nonce value. Must be exactly 96 bits (12 bytes) in length.
100      *  counter: The initial counter value. Defaults to 0.
101      */
102     void decrypt(ref ubyte[] data, string key, ubyte[12] nonce = (ubyte[12]).init, uint counter = 0) => encrypt(data, key, nonce, counter);
103 }