Lua: Don't lua_error() out of context with pending dtors
[lsnes.git] / src / library / skein.cpp
blob3dad0c7fda1991b9ec6d70ca632c08a2d597c6ad
1 #include <cstdint>
2 #include <cstring>
3 #include "skein.hpp"
4 #include <iostream>
5 #include <stdexcept>
6 #include <iomanip>
7 #include <algorithm>
8 #include "arch-detect.hpp"
9 #ifdef TEST_SKEIN_CODE
10 #include "hex.hpp"
11 #endif
13 namespace skein
15 //Jerry Solinas was not here.
16 #include "skein512c.inc"
18 static uint8_t bitmasks[] = {0, 128, 192, 224, 240, 248, 252, 254, 255};
20 #ifdef TEST_SKEIN_CODE
21 static void show_array(const char* prefix, const uint64_t* a, size_t e)
23 std::cerr << prefix;
24 for(size_t i = 0; i < e; i++) {
25 std::cerr << hex::to(a[i]);
26 if(i < e - 1)
27 std::cerr << ", ";
29 std::cerr << std::endl;
32 static void show_array(const char* prefix, const uint8_t* a, size_t e)
34 std::cerr << prefix;
35 for(size_t i = 0; i < e; i++) {
36 std::cerr << hex::to(a[i]);
38 std::cerr << std::endl;
40 #endif
42 inline static void to_words(uint64_t* out, const void* in, size_t words)
44 #ifdef ARCH_IS_I386
45 memcpy(out, in, words<<3);
46 #else
47 for(unsigned i = 0; i < words; i++)
48 out[i]=0;
49 for(unsigned i = 0; i < (words<<3); i++)
50 out[i>>3]|=((uint64_t)reinterpret_cast<uint8_t*>(in)[i] << ((i&7)<<3));
51 #endif
54 inline static void to_bytes(void* out, const uint64_t* in, size_t bytes)
56 #ifdef ARCH_IS_I386
57 memcpy(out, in, bytes);
58 #else
59 for(size_t i = 0; i < bytes; i++)
60 output[i] = (out[i>>3] >> ((i&7)<<3));
61 #endif
64 inline static void _skein256_compress(uint64_t* a, const uint64_t* b, const uint64_t* c, const uint64_t* d)
66 #ifdef TEST_SKEIN_CODE
67 show_array("Key: ", c, 4);
68 show_array("Data: ", b, 4);
69 show_array("Tweak: ", d, 2);
70 #endif
71 skein256_compress(a, b, c, d);
72 #ifdef TEST_SKEIN_CODE
73 show_array("Out: ", a, 4);
74 #endif
77 inline static void _skein512_compress(uint64_t* a, const uint64_t* b, const uint64_t* c, const uint64_t* d)
79 #ifdef TEST_SKEIN_CODE
80 show_array("Key: ", c, 8);
81 show_array("Data: ", b, 8);
82 show_array("Tweak: ", d, 2);
83 #endif
84 skein512_compress(a, b, c, d);
85 #ifdef TEST_SKEIN_CODE
86 show_array("Out: ", a, 8);
87 #endif
90 inline static void _skein1024_compress(uint64_t* a, const uint64_t* b, const uint64_t* c, const uint64_t* d)
92 #ifdef TEST_SKEIN_CODE
93 show_array("Key: ", c, 16);
94 show_array("Data: ", b, 16);
95 show_array("Tweak: ", d, 2);
96 #endif
97 skein1024_compress(a, b, c, d);
98 #ifdef TEST_SKEIN_CODE
99 show_array("Out: ", a, 16);
100 #endif
103 hash::hash(hash::variant v, uint64_t _outbits) throw(std::runtime_error)
105 memset(chain, 0, sizeof(chain));
106 memset(buffer, 0, sizeof(buffer));
107 switch(v) {
108 case PIPE_256: compress = _skein256_compress; fullbuffer = 32; break;
109 case PIPE_512: compress = _skein512_compress; fullbuffer = 64; break;
110 case PIPE_1024: compress = _skein1024_compress; fullbuffer = 128; break;
111 default: throw std::runtime_error("Invalid Skein variant");
113 bufferfill = 0;
114 data_low = 0;
115 data_high = 0;
116 outbits = _outbits;
117 last_type = -1;
120 hash::~hash() throw()
122 zeroize(chain, sizeof(chain));
123 zeroize(buffer, sizeof(buffer));
124 zeroize(&bufferfill, sizeof(bufferfill));
125 zeroize(&data_low, sizeof(data_low));
126 zeroize(&data_high, sizeof(data_high));
127 zeroize(&last_type, sizeof(last_type));
130 void hash::configure()
132 uint64_t config[16] = {0x133414853ULL,outbits};
133 uint64_t tweak[2] = {32,0xC400000000000000ULL};
134 uint64_t iv[16];
135 compress(iv, config, chain, tweak);
136 memcpy(chain, iv, fullbuffer);
137 last_type = 4;
138 zeroize(iv, sizeof(iv));
139 zeroize(tweak, sizeof(tweak));
140 zeroize(config, sizeof(config));
143 void hash::typechange(uint8_t newtype)
145 if(last_type != newtype) {
146 //Type change.
147 //1) If changing from any other last type except NONE or CONFIG, flush.
148 if(last_type >= 0 && last_type != 4)
149 flush_buffer(last_type, true);
150 //2) If changing over CONFIG, configure the hash.
151 if(last_type < 4 && newtype > 4)
152 configure();
153 //3) If changing over MESSAGE, flush even if empty.
154 if(last_type < 48 && newtype > 48) {
155 last_type = newtype;
156 data_low = 0;
157 data_high = 0;
158 flush_buffer(48, true);
160 last_type = newtype;
161 data_low = 0;
162 data_high = 0;
166 void hash::write(const uint8_t* data, size_t datalen, hash::datatype type) throw(std::runtime_error)
168 if(type < 0 || type == 4 || type > 62)
169 throw std::runtime_error("Invalid data type to write");
170 if(type < last_type)
171 throw std::runtime_error("Data types in wrong order");
172 while(datalen > 0) {
173 typechange(type);
174 if(bufferfill == fullbuffer)
175 flush_buffer(type, false);
176 if(datalen >= fullbuffer - bufferfill) {
177 memcpy(buffer + bufferfill, data, fullbuffer - bufferfill);
178 data += (fullbuffer - bufferfill);
179 datalen -= (fullbuffer - bufferfill);
180 bufferfill = fullbuffer;
181 } else {
182 memcpy(buffer + bufferfill, data, datalen);
183 data += datalen;
184 bufferfill += datalen;
185 datalen = 0;
190 void hash::flush_buffer(uint8_t type, bool final)
192 uint64_t _buffer[16];
193 uint64_t _buffer2[16];
194 uint64_t tweak[2];
195 tweak[0] = data_low + bufferfill;
196 tweak[1] = data_high;
197 if(tweak[0] < data_low)
198 tweak[1]++;
199 tweak[1] += ((uint64_t)type << 56);
200 if(!data_low && !data_high)
201 tweak[1] += (1ULL << 62);
202 if(final)
203 tweak[1] += (1ULL << 63);
204 to_words(_buffer, buffer, fullbuffer >> 3);
205 compress(_buffer2, _buffer, chain, tweak);
206 memcpy(chain, _buffer2, fullbuffer);
207 data_low += bufferfill;
208 if(data_low < bufferfill)
209 data_high++;
210 bufferfill = 0;
211 memset(buffer, 0, fullbuffer);
212 zeroize(_buffer, sizeof(_buffer));
213 zeroize(_buffer2, sizeof(_buffer2));
214 zeroize(tweak, sizeof(tweak));
217 void hash::read_partial(uint8_t* output, uint64_t startblock, uint64_t bits) throw()
219 typechange(63); //Switch to output.
220 //The final one is special.
221 uint64_t zeroes[16] = {0};
222 uint64_t out[16];
223 uint64_t tweak[2] = {8,0xFF00000000000000ULL};
224 uint64_t offset = 0;
225 zeroes[0] = startblock;
226 for(uint64_t i = 0; i < bits; i += (fullbuffer<<3)) {
227 compress(out, zeroes, chain, tweak);
228 zeroes[0]++;
229 uint64_t fullbytes = std::min((bits - i) >> 3, static_cast<uint64_t>(fullbuffer));
230 to_bytes(output + offset, out, fullbytes);
231 if(fullbytes < fullbuffer && i + 8 * fullbytes < bits) {
232 output[offset + fullbytes] = (out[fullbytes>>3] >> ((fullbytes&7)<<3));
233 output[offset + fullbytes] &= bitmasks[bits&7];
235 offset += fullbuffer;
237 zeroize(out, sizeof(out));
238 zeroize(zeroes, sizeof(zeroes));
239 zeroize(tweak, sizeof(tweak));
242 void hash::read(uint8_t* output) throw()
244 read_partial(output, 0, outbits);
247 prng::prng() throw()
249 _is_seeded = false;
250 memset(state, 0, 128);
253 void prng::write(const void* buffer, size_t size) throw()
255 hash h(hash::PIPE_1024, 1024);
256 h.write(state, 128, hash::T_NONCE);
257 h.write(reinterpret_cast<const uint8_t*>(buffer), size, hash::T_MESSAGE);
258 h.read(state);
259 if(size > 0)
260 _is_seeded = true;
263 void prng::read(void* buffer, size_t size) throw(std::runtime_error)
265 if(!_is_seeded)
266 throw std::runtime_error("PRNG is not initialized");
267 //We can't use skein itself here, but the underlying compression function.
268 uint64_t chain[16] = {0};
269 uint64_t zeroes[16] = {0};
270 uint64_t out[16];
271 uint64_t tweak[2] = {8,0xFF00000000000000ULL};
272 to_words(chain, state, 16);
273 zeroes[0] = 1;
274 for(uint64_t i = 0; i < size; i += 128) {
275 _skein1024_compress(out, zeroes, chain, tweak);
276 zeroes[0]++;
277 uint64_t fullbytes = std::min(size - i, static_cast<uint64_t>(128));
278 to_bytes(reinterpret_cast<uint8_t*>(buffer) + i, out, fullbytes);
280 zeroes[0] = 0;
281 _skein1024_compress(out, zeroes, chain, tweak);
282 to_bytes(state, out, 128);
283 zeroize(chain, sizeof(chain));
284 zeroize(zeroes, sizeof(zeroes));
285 zeroize(out, sizeof(out));
286 zeroize(tweak, sizeof(tweak));
289 bool prng::is_seeded() const throw()
291 return _is_seeded;
294 void zeroize(void* ptr, size_t size)
296 if(!size) return;
297 //Whee... Do it like OpenSSL/GnuTLS.
298 volatile char* vptr = (volatile char*)ptr;
299 volatile size_t vidx = 0;
300 do { memset(ptr, 0, size); } while(vptr[vidx]);
304 #ifdef TEST_SKEIN_CODE
305 #define SKEIN_DEBUG
306 #include <skein.h>
307 #include <skein_debug.h>
311 int main(int argc, char** argv)
314 //skein_DebugFlag = SKEIN_DEBUG_STATE | SKEIN_DEBUG_TWEAK | SKEIN_DEBUG_INPUT_64;
315 uint8_t out[128];
316 skein::hash ctx(skein::hash::PIPE_512, 256);
317 ctx.write((uint8_t*)argv[1], strlen(argv[1]), skein::hash::T_KEY);
318 ctx.write((uint8_t*)argv[2], strlen(argv[2]), skein::hash::T_MESSAGE);
319 ctx.read(out);
320 show_array("New: ", out, 32);
321 Skein_512_Ctxt_t ctx2;
322 Skein_512_InitExt(&ctx2, 256, SKEIN_CFG_TREE_INFO_SEQUENTIAL, (uint8_t*)argv[1], strlen(argv[1]));
323 Skein_512_Update(&ctx2, (uint8_t*)argv[2], strlen(argv[2]));
324 Skein_512_Final(&ctx2, out);
325 show_array("Ref: ", out, 32);
326 return 0;
329 int main()
333 uint8_t buf[129] = {0xFF,0xFE,0xFD,0xFC,0xFB,0xFA,0xF9,0xF8,0xF7};
334 uint8_t key[135] = {0x05,0x04,0x46,0x22,0x26,0x35,0x63,0x26,0xFF};
335 uint8_t out[128];
336 skein::hash ctx(skein::hash::PIPE_256, 256);
337 ctx.write(key, sizeof(key), skein::hash::T_KEY);
338 ctx.write(key, sizeof(key), skein::hash::T_NONCE);
339 ctx.write(buf, 2);
340 ctx.read(out);
341 show_array("", out, 32);
344 #endif