8 #include "arch-detect.hpp"
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
)
24 for(size_t i
= 0; i
< e
; i
++) {
25 std::cerr
<< hex::to(a
[i
]);
29 std::cerr
<< std::endl
;
32 static void show_array(const char* prefix
, const uint8_t* a
, size_t e
)
35 for(size_t i
= 0; i
< e
; i
++) {
36 std::cerr
<< hex::to(a
[i
]);
38 std::cerr
<< std::endl
;
42 inline static void to_words(uint64_t* out
, const void* in
, size_t words
)
45 memcpy(out
, in
, words
<<3);
47 for(unsigned i
= 0; i
< words
; i
++)
49 for(unsigned i
= 0; i
< (words
<<3); i
++)
50 out
[i
>>3]|=((uint64_t)reinterpret_cast<uint8_t*>(in
)[i
] << ((i
&7)<<3));
54 inline static void to_bytes(void* out
, const uint64_t* in
, size_t bytes
)
57 memcpy(out
, in
, bytes
);
59 for(size_t i
= 0; i
< bytes
; i
++)
60 output
[i
] = (out
[i
>>3] >> ((i
&7)<<3));
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);
71 skein256_compress(a
, b
, c
, d
);
72 #ifdef TEST_SKEIN_CODE
73 show_array("Out: ", a
, 4);
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);
84 skein512_compress(a
, b
, c
, d
);
85 #ifdef TEST_SKEIN_CODE
86 show_array("Out: ", a
, 8);
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);
97 skein1024_compress(a
, b
, c
, d
);
98 #ifdef TEST_SKEIN_CODE
99 show_array("Out: ", a
, 16);
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
));
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");
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
};
135 compress(iv
, config
, chain
, tweak
);
136 memcpy(chain
, iv
, fullbuffer
);
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
) {
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)
153 //3) If changing over MESSAGE, flush even if empty.
154 if(last_type
< 48 && newtype
> 48) {
158 flush_buffer(48, true);
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");
171 throw std::runtime_error("Data types in wrong order");
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
;
182 memcpy(buffer
+ bufferfill
, data
, datalen
);
184 bufferfill
+= datalen
;
190 void hash::flush_buffer(uint8_t type
, bool final
)
192 uint64_t _buffer
[16];
193 uint64_t _buffer2
[16];
195 tweak
[0] = data_low
+ bufferfill
;
196 tweak
[1] = data_high
;
197 if(tweak
[0] < data_low
)
199 tweak
[1] += ((uint64_t)type
<< 56);
200 if(!data_low
&& !data_high
)
201 tweak
[1] += (1ULL << 62);
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
)
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};
223 uint64_t tweak
[2] = {8,0xFF00000000000000ULL
};
225 zeroes
[0] = startblock
;
226 for(uint64_t i
= 0; i
< bits
; i
+= (fullbuffer
<<3)) {
227 compress(out
, zeroes
, chain
, tweak
);
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
);
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
);
263 void prng::read(void* buffer
, size_t size
) throw(std::runtime_error
)
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};
271 uint64_t tweak
[2] = {8,0xFF00000000000000ULL
};
272 to_words(chain
, state
, 16);
274 for(uint64_t i
= 0; i
< size
; i
+= 128) {
275 _skein1024_compress(out
, zeroes
, chain
, tweak
);
277 uint64_t fullbytes
= std::min(size
- i
, static_cast<uint64_t>(128));
278 to_bytes(reinterpret_cast<uint8_t*>(buffer
) + i
, out
, fullbytes
);
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()
294 void zeroize(void* ptr
, size_t size
)
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
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;
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);
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);
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};
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
);
341 show_array("", out
, 32);