1 /// Implementation of Berus digester.
2 module tern.digest.berus;
3 
4 import tern.serialization;
5 import tern.digest;
6 import std.conv;
7 
8 /**
9  * Implementation of Berus digester.
10  *
11  * Berus is a modified Argon2 designed for lightweight and low-resource
12  * environments. It operates by dividing the input data into blocks, applying a series
13  * of operations, including XOR and addition, and then compressing the result.
14  *
15  * Example:
16  * ```d
17  * import tern.digest.berus;
18  * 
19  * ubyte[] data = [1, 2, 3, 4, 5];
20  * ubyte[] salt = [6, 7, 8, 9, 10];
21  * auto hashValue = Berus.hash(data, salt);
22  * ```
23  */
24 public static @digester class Berus
25 {
26 private:
27 static:
28 pure:
29     enum BLOCK_SIZE = 32;
30     enum OPS_LIMIT = 14;
31 
32 public:
33     /**
34      * Computes the Berus digest of the given data.
35      * 
36      * Params:
37      *  data - The data to be hashed.
38      *  salt - The salt to be used in the hashing process.
39      * 
40      * Returns:
41      *  The hashed result as an array of ubytes.
42      */
43     ubyte[] hash(ubyte[] data, ubyte[] salt) 
44     {
45         ulong[BLOCK_SIZE] block;
46 
47         foreach (i, ref b; block)
48             b += data[i % data.length];
49 
50         foreach (i, b; data)
51             block[i % BLOCK_SIZE] ^= b;
52 
53         foreach (i, b; salt)
54             block[(i + BLOCK_SIZE / 2) % BLOCK_SIZE] ^= b;
55 
56         void compress()
57         {
58             ulong F(ulong x, ulong y) 
59             {
60                 return (x + y) * (x ^ y);
61             }
62 
63             foreach (i; 0..BLOCK_SIZE / 4) 
64             {
65                 block[i * 4] = F(block[i * 4], block[i * 4 + 1]);
66                 block[i * 4 + 1] = F(block[i * 4 + 1], block[i * 4 + 2]);
67                 block[i * 4 + 2] = F(block[i * 4 + 2], block[i * 4 + 3]);
68                 block[i * 4 + 3] = F(block[i * 4 + 3], block[i * 4]);
69             }
70 
71             foreach (i; 0..BLOCK_SIZE) 
72                 block[i] ^= block[(i + 1) % BLOCK_SIZE];
73 
74             foreach_reverse (i; 1..BLOCK_SIZE) 
75                 block[i] += block[i - 1];
76         }
77 
78         for (uint i = 0; i < OPS_LIMIT; i++)
79             compress();
80 
81         return block[0..8].serialize!true();
82     }
83 }