1
+ // Copyright (c) 2009-2010 Satoshi Nakamoto
2
+ // Copyright (c) 2009-2018 The DigiByte developers
3
+ // Distributed under the MIT/X11 software license, see the accompanying
4
+ // file COPYING or http://www.opensource.org/licenses/mit-license.php.
5
+
6
+ #include " odocrypt.h"
7
+
8
+ #include < algorithm>
9
+
10
+ struct OdoRandom
11
+ {
12
+ // LCG parameters from Knuth
13
+ const static uint64_t BASE_MULTIPLICAND = 6364136223846793005ull ;
14
+ const static uint64_t BASE_ADDEND = 1442695040888963407ull ;
15
+
16
+ OdoRandom (uint32_t seed):
17
+ current (seed),
18
+ multiplicand (1 ),
19
+ addend (0 )
20
+ {}
21
+
22
+ // For a standard LCG, every seed produces the same sequence, but from a different
23
+ // starting point. This generator gives the 1st, 3rd, 6th, 10th, etc output from
24
+ // a standard LCG. This ensures that every seed produces a unique sequence.
25
+ inline uint32_t NextInt ()
26
+ {
27
+ addend += multiplicand * BASE_ADDEND;
28
+ multiplicand *= BASE_MULTIPLICAND;
29
+ current = current * multiplicand + addend;
30
+ return current >> 32 ;
31
+ }
32
+
33
+ inline uint64_t NextLong ()
34
+ {
35
+ uint64_t hi = NextInt ();
36
+ return (hi << 32 ) | NextInt ();
37
+ }
38
+
39
+ inline int Next (int N)
40
+ {
41
+ return ((uint64_t )NextInt () * N) >> 32 ;
42
+ }
43
+
44
+ template <class T , size_t sz>
45
+ void Permutation (T (&arr)[sz])
46
+ {
47
+ for (size_t i = 0 ; i < sz; i++)
48
+ arr[i] = i;
49
+ for (int i = 1 ; i < sz; i++)
50
+ std::swap (arr[i], arr[Next (i+1 )]);
51
+ }
52
+
53
+ uint64_t current;
54
+ uint64_t multiplicand;
55
+ uint64_t addend;
56
+ };
57
+
58
+ OdoCrypt::OdoCrypt (uint32_t key)
59
+ {
60
+ OdoRandom r (key);
61
+
62
+ // Randomize each s-box
63
+ for (int i = 0 ; i < SMALL_SBOX_COUNT; i++)
64
+ {
65
+ r.Permutation (Sbox1[i]);
66
+ }
67
+ for (int i = 0 ; i < LARGE_SBOX_COUNT; i++)
68
+ {
69
+ r.Permutation (Sbox2[i]);
70
+ }
71
+
72
+ // Randomize each p-box
73
+ for (int i = 0 ; i < 2 ; i++)
74
+ {
75
+ Pbox& perm = Permutation[i];
76
+ for (int j = 0 ; j < PBOX_SUBROUNDS; j++)
77
+ for (int k = 0 ; k < STATE_SIZE/2 ; k++)
78
+ perm.mask [j][k] = r.NextLong ();
79
+ for (int j = 0 ; j < PBOX_SUBROUNDS-1 ; j++)
80
+ for (int k = 0 ; k < STATE_SIZE/2 ; k++)
81
+ perm.rotation [j][k] = r.Next (63 ) + 1 ;
82
+ }
83
+
84
+ // Randomize rotations
85
+ // Rotations must be distinct, non-zero, and have odd sum
86
+ {
87
+ int bits[WORD_BITS-1 ];
88
+ r.Permutation (bits);
89
+ int sum = 0 ;
90
+ for (int j = 0 ; j < ROTATION_COUNT-1 ; j++)
91
+ {
92
+ Rotations[j] = bits[j] + 1 ;
93
+ sum += Rotations[j];
94
+ }
95
+ for (int j = ROTATION_COUNT-1 ; ; j++)
96
+ {
97
+ if ((bits[j] + 1 + sum) % 2 )
98
+ {
99
+ Rotations[ROTATION_COUNT-1 ] = bits[j] + 1 ;
100
+ break ;
101
+ }
102
+ }
103
+ }
104
+
105
+ // Randomize each round key
106
+ for (int i = 0 ; i < ROUNDS; i++)
107
+ RoundKey[i] = r.Next (1 << STATE_SIZE);
108
+ }
109
+
110
+ void OdoCrypt::Encrypt (char cipher[DIGEST_SIZE], const char plain[DIGEST_SIZE]) const
111
+ {
112
+ uint64_t state[STATE_SIZE];
113
+ Unpack (state, plain);
114
+ PreMix (state);
115
+ for (int round = 0 ; round < ROUNDS; round ++)
116
+ {
117
+ ApplyPbox (state, Permutation[0 ]);
118
+ ApplySboxes (state, Sbox1, Sbox2);
119
+ ApplyPbox (state, Permutation[1 ]);
120
+ ApplyRotations (state, Rotations);
121
+ ApplyRoundKey (state, RoundKey[round ]);
122
+ }
123
+ Pack (state, cipher);
124
+ }
125
+
126
+ template <class T , size_t sz1, size_t sz2>
127
+ void InvertMapping (T (&res)[sz1][sz2], const T (&mapping)[sz1][sz2])
128
+ {
129
+ for (size_t i = 0 ; i < sz1; i++)
130
+ for (size_t j = 0 ; j < sz2; j++)
131
+ res[i][mapping[i][j]] = j;
132
+ }
133
+
134
+ void OdoCrypt::Decrypt (char plain[DIGEST_SIZE], const char cipher[DIGEST_SIZE]) const
135
+ {
136
+ uint8_t invSbox1[SMALL_SBOX_COUNT][1 << SMALL_SBOX_WIDTH];
137
+ uint16_t invSbox2[LARGE_SBOX_COUNT][1 << LARGE_SBOX_WIDTH];
138
+
139
+ InvertMapping (invSbox1, Sbox1);
140
+ InvertMapping (invSbox2, Sbox2);
141
+
142
+ uint64_t state[STATE_SIZE];
143
+ Unpack (state, cipher);
144
+ for (int round = ROUNDS-1 ; round >= 0 ; round --)
145
+ {
146
+ ApplyRoundKey (state, RoundKey[round ]);
147
+ // LCM(STATE_SIZE, WORD_BITS)-1 is enough iterations, but this will do.
148
+ for (int i = 0 ; i < STATE_SIZE*WORD_BITS-1 ; i++)
149
+ ApplyRotations (state, Rotations);
150
+ ApplyInvPbox (state, Permutation[1 ]);
151
+ ApplySboxes (state, invSbox1, invSbox2);
152
+ ApplyInvPbox (state, Permutation[0 ]);
153
+ }
154
+ PreMix (state);
155
+ Pack (state, plain);
156
+ }
157
+
158
+ void OdoCrypt::Unpack (uint64_t state[STATE_SIZE], const char bytes[DIGEST_SIZE])
159
+ {
160
+ std::fill (state, state+STATE_SIZE, 0 );
161
+ for (int i = 0 ; i < STATE_SIZE; i++)
162
+ {
163
+ for (int j = 0 ; j < 8 ; j++)
164
+ {
165
+ state[i] |= (uint64_t )(uint8_t )bytes[8 *i + j] << (8 *j);
166
+ }
167
+ }
168
+ }
169
+
170
+ void OdoCrypt::Pack (const uint64_t state[STATE_SIZE], char bytes[DIGEST_SIZE])
171
+ {
172
+ std::fill (bytes, bytes+DIGEST_SIZE, 0 );
173
+ for (int i = 0 ; i < STATE_SIZE; i++)
174
+ {
175
+ for (int j = 0 ; j < 8 ; j++)
176
+ {
177
+ bytes[8 *i + j] = (state[i] >> (8 *j)) & 0xff ;
178
+ }
179
+ }
180
+ }
181
+
182
+ void OdoCrypt::PreMix (uint64_t state[STATE_SIZE])
183
+ {
184
+ uint64_t total = 0 ;
185
+ for (int i = 0 ; i < STATE_SIZE; i++)
186
+ total ^= state[i];
187
+ total ^= total >> 32 ;
188
+ for (int i = 0 ; i < STATE_SIZE; i++)
189
+ state[i] ^= total;
190
+ }
191
+
192
+ void OdoCrypt::ApplySboxes (
193
+ uint64_t state[STATE_SIZE],
194
+ const uint8_t sbox1[SMALL_SBOX_COUNT][1 << SMALL_SBOX_WIDTH],
195
+ const uint16_t sbox2[LARGE_SBOX_COUNT][1 << LARGE_SBOX_WIDTH])
196
+ {
197
+ const static uint64_t MASK1 = (1 << SMALL_SBOX_WIDTH) - 1 ;
198
+ const static uint64_t MASK2 = (1 << LARGE_SBOX_WIDTH) - 1 ;
199
+
200
+ int smallSboxIndex = 0 ;
201
+ for (int i = 0 ; i < STATE_SIZE; i++)
202
+ {
203
+ uint64_t next = 0 ;
204
+ int pos = 0 ;
205
+ int largeSboxIndex = i;
206
+ for (int j = 0 ; j < SMALL_SBOX_COUNT / STATE_SIZE; j++)
207
+ {
208
+ next |= (uint64_t )sbox1[smallSboxIndex][(state[i] >> pos) & MASK1] << pos;
209
+ pos += SMALL_SBOX_WIDTH;
210
+ next |= (uint64_t )sbox2[largeSboxIndex][(state[i] >> pos) & MASK2] << pos;
211
+ pos += LARGE_SBOX_WIDTH;
212
+ smallSboxIndex++;
213
+ }
214
+ state[i] = next;
215
+ }
216
+ }
217
+
218
+ void OdoCrypt::ApplyMaskedSwaps (uint64_t state[STATE_SIZE], const uint64_t mask[STATE_SIZE/2 ])
219
+ {
220
+ for (int i = 0 ; i < STATE_SIZE/2 ; i++)
221
+ {
222
+ uint64_t & a = state[2 *i];
223
+ uint64_t & b = state[2 *i+1 ];
224
+ // For each bit set in the mask, swap the corresponding bits in `a` and `b`
225
+ uint64_t swp = mask[i] & (a ^ b);
226
+ a ^= swp;
227
+ b ^= swp;
228
+ }
229
+ }
230
+
231
+ void OdoCrypt::ApplyWordShuffle (uint64_t state[STATE_SIZE], int m)
232
+ {
233
+ uint64_t next[STATE_SIZE];
234
+ for (int i = 0 ; i < STATE_SIZE; i++)
235
+ {
236
+ next[m*i % STATE_SIZE] = state[i];
237
+ }
238
+ std::copy (next, next+STATE_SIZE, state);
239
+ }
240
+
241
+ inline uint64_t Rot (uint64_t x, int r)
242
+ {
243
+ return r == 0 ? x : (x << r) ^ (x >> (64 -r));
244
+ }
245
+
246
+ void OdoCrypt::ApplyPboxRotations (uint64_t state[STATE_SIZE], const int rotation[STATE_SIZE/2 ])
247
+ {
248
+ for (int i = 0 ; i < STATE_SIZE/2 ; i++)
249
+ {
250
+ // Only rotate the even words. Rotating the odd words wouldn't actually
251
+ // be useful - a transformation that rotates all the words can be
252
+ // transformed into one that only rotates the even words, then rotates
253
+ // the odd words once after the final iteration.
254
+ state[2 *i] = Rot (state[2 *i], rotation[i]);
255
+ }
256
+ }
257
+
258
+ void OdoCrypt::ApplyPbox (uint64_t state[STATE_SIZE], const Pbox& perm)
259
+ {
260
+ for (int i = 0 ; i < PBOX_SUBROUNDS-1 ; i++)
261
+ {
262
+ // Conditionally move bits between adjacent pairs of words
263
+ ApplyMaskedSwaps (state, perm.mask [i]);
264
+ // Move the words around
265
+ ApplyWordShuffle (state, PBOX_M);
266
+ // Rotate the bits within words
267
+ ApplyPboxRotations (state, perm.rotation [i]);
268
+ }
269
+ ApplyMaskedSwaps (state, perm.mask [PBOX_SUBROUNDS-1 ]);
270
+ }
271
+
272
+ void OdoCrypt::ApplyInvPbox (uint64_t state[STATE_SIZE], const Pbox& perm)
273
+ {
274
+ ApplyMaskedSwaps (state, perm.mask [PBOX_SUBROUNDS-1 ]);
275
+ for (int i = PBOX_SUBROUNDS-2 ; i >= 0 ; i--)
276
+ {
277
+ int invRotation[STATE_SIZE/2 ];
278
+ for (int j = 0 ; j < STATE_SIZE/2 ; j++)
279
+ invRotation[j] = WORD_BITS - perm.rotation [i][j];
280
+ ApplyPboxRotations (state, invRotation);
281
+ ApplyWordShuffle (state, INV_PBOX_M);
282
+ ApplyMaskedSwaps (state, perm.mask [i]);
283
+ }
284
+ }
285
+
286
+ void OdoCrypt::ApplyRotations (uint64_t state[STATE_SIZE], const int rotations[ROTATION_COUNT])
287
+ {
288
+ uint64_t next[STATE_SIZE];
289
+ std::rotate_copy (state, state+1 , state+STATE_SIZE, next);
290
+ for (int i = 0 ; i < STATE_SIZE; i++)
291
+ for (int j = 0 ; j < ROTATION_COUNT; j++)
292
+ {
293
+ next[i] ^= Rot (state[i], rotations[j]);
294
+ }
295
+ std::copy (next, next+STATE_SIZE, state);
296
+ }
297
+
298
+ void OdoCrypt::ApplyRoundKey (uint64_t state[STATE_SIZE], int roundKey)
299
+ {
300
+ for (int i = 0 ; i < STATE_SIZE; i++)
301
+ state[i] ^= (roundKey >> i) & 1 ;
302
+ }
0 commit comments