aboutsummaryrefslogtreecommitdiffstats
path: root/src/lib/hash/skein/skein_512.cpp
diff options
context:
space:
mode:
authorlloyd <[email protected]>2014-01-10 03:41:59 +0000
committerlloyd <[email protected]>2014-01-10 03:41:59 +0000
commit6894dca64c04936d07048c0e8cbf7e25858548c3 (patch)
tree5d572bfde9fe667dab14e3f04b5285a85d8acd95 /src/lib/hash/skein/skein_512.cpp
parent9efa3be92442afb3d0b69890a36c7f122df18eda (diff)
Move lib into src
Diffstat (limited to 'src/lib/hash/skein/skein_512.cpp')
-rw-r--r--src/lib/hash/skein/skein_512.cpp274
1 files changed, 274 insertions, 0 deletions
diff --git a/src/lib/hash/skein/skein_512.cpp b/src/lib/hash/skein/skein_512.cpp
new file mode 100644
index 000000000..9aafb1616
--- /dev/null
+++ b/src/lib/hash/skein/skein_512.cpp
@@ -0,0 +1,274 @@
+/*
+* The Skein-512 hash function
+* (C) 2009-2010 Jack Lloyd
+*
+* Distributed under the terms of the Botan license
+*/
+
+#include <botan/skein_512.h>
+#include <botan/loadstor.h>
+#include <botan/parsing.h>
+#include <botan/exceptn.h>
+#include <botan/rotate.h>
+#include <algorithm>
+
+namespace Botan {
+
+namespace {
+
+enum type_code {
+ SKEIN_KEY = 0,
+ SKEIN_CONFIG = 4,
+ SKEIN_PERSONALIZATION = 8,
+ SKEIN_PUBLIC_KEY = 12,
+ SKEIN_KEY_IDENTIFIER = 16,
+ SKEIN_NONCE = 20,
+ SKEIN_MSG = 48,
+ SKEIN_OUTPUT = 63
+};
+
+void ubi_512(secure_vector<u64bit>& H,
+ secure_vector<u64bit>& T,
+ const byte msg[], size_t msg_len)
+ {
+ do
+ {
+ const size_t to_proc = std::min<size_t>(msg_len, 64);
+ T[0] += to_proc;
+
+ u64bit M[8] = { 0 };
+
+ load_le(M, msg, to_proc / 8);
+
+ if(to_proc % 8)
+ {
+ for(size_t j = 0; j != to_proc % 8; ++j)
+ M[to_proc/8] |= static_cast<u64bit>(msg[8*(to_proc/8)+j]) << (8*j);
+ }
+
+ H[8] = H[0] ^ H[1] ^ H[2] ^ H[3] ^
+ H[4] ^ H[5] ^ H[6] ^ H[7] ^ 0x1BD11BDAA9FC1A22;
+
+ T[2] = T[0] ^ T[1];
+
+ u64bit X0 = M[0] + H[0];
+ u64bit X1 = M[1] + H[1];
+ u64bit X2 = M[2] + H[2];
+ u64bit X3 = M[3] + H[3];
+ u64bit X4 = M[4] + H[4];
+ u64bit X5 = M[5] + H[5] + T[0];
+ u64bit X6 = M[6] + H[6] + T[1];
+ u64bit X7 = M[7] + H[7];
+
+#define THREEFISH_ROUND(X1,X2,X3,X4,X5,X6,X7,X8,ROT1,ROT2,ROT3,ROT4) \
+ do { \
+ X1 += X2; X2 = rotate_left(X2, ROT1) ^ X1; \
+ X3 += X4; X4 = rotate_left(X4, ROT2) ^ X3; \
+ X5 += X6; X6 = rotate_left(X6, ROT3) ^ X5; \
+ X7 += X8; X8 = rotate_left(X8, ROT4) ^ X7; \
+ } while(0);
+
+#define THREEFISH_INJECT_KEY(r) \
+ do { \
+ X0 += H[(r ) % 9]; \
+ X1 += H[(r+1) % 9]; \
+ X2 += H[(r+2) % 9]; \
+ X3 += H[(r+3) % 9]; \
+ X4 += H[(r+4) % 9]; \
+ X5 += H[(r+5) % 9] + T[(r ) % 3]; \
+ X6 += H[(r+6) % 9] + T[(r+1) % 3]; \
+ X7 += H[(r+7) % 9] + (r); \
+ } while(0);
+
+#define THREEFISH_8_ROUNDS(R1,R2) \
+ do { \
+ THREEFISH_ROUND(X0,X1,X2,X3,X4,X5,X6,X7, 46,36,19,37); \
+ THREEFISH_ROUND(X2,X1,X4,X7,X6,X5,X0,X3, 33,27,14,42); \
+ THREEFISH_ROUND(X4,X1,X6,X3,X0,X5,X2,X7, 17,49,36,39); \
+ THREEFISH_ROUND(X6,X1,X0,X7,X2,X5,X4,X3, 44, 9,54,56); \
+ \
+ THREEFISH_INJECT_KEY(R1); \
+ \
+ THREEFISH_ROUND(X0,X1,X2,X3,X4,X5,X6,X7, 39,30,34,24); \
+ THREEFISH_ROUND(X2,X1,X4,X7,X6,X5,X0,X3, 13,50,10,17); \
+ THREEFISH_ROUND(X4,X1,X6,X3,X0,X5,X2,X7, 25,29,39,43); \
+ THREEFISH_ROUND(X6,X1,X0,X7,X2,X5,X4,X3, 8,35,56,22); \
+ \
+ THREEFISH_INJECT_KEY(R2); \
+ } while(0);
+
+ THREEFISH_8_ROUNDS(1,2);
+ THREEFISH_8_ROUNDS(3,4);
+ THREEFISH_8_ROUNDS(5,6);
+ THREEFISH_8_ROUNDS(7,8);
+ THREEFISH_8_ROUNDS(9,10);
+ THREEFISH_8_ROUNDS(11,12);
+ THREEFISH_8_ROUNDS(13,14);
+ THREEFISH_8_ROUNDS(15,16);
+ THREEFISH_8_ROUNDS(17,18);
+
+ // message feed forward
+ H[0] = X0 ^ M[0];
+ H[1] = X1 ^ M[1];
+ H[2] = X2 ^ M[2];
+ H[3] = X3 ^ M[3];
+ H[4] = X4 ^ M[4];
+ H[5] = X5 ^ M[5];
+ H[6] = X6 ^ M[6];
+ H[7] = X7 ^ M[7];
+
+ // clear first flag if set
+ T[1] &= ~(static_cast<u64bit>(1) << 62);
+
+ msg_len -= to_proc;
+ msg += to_proc;
+ } while(msg_len);
+ }
+
+void reset_tweak(secure_vector<u64bit>& T,
+ type_code type, bool final)
+ {
+ T[0] = 0;
+
+ T[1] = (static_cast<u64bit>(type) << 56) |
+ (static_cast<u64bit>(1) << 62) |
+ (static_cast<u64bit>(final) << 63);
+ }
+
+void initial_block(secure_vector<u64bit>& H,
+ secure_vector<u64bit>& T,
+ size_t output_bits,
+ const std::string& personalization)
+ {
+ zeroise(H);
+
+ // ASCII("SHA3") followed by version (0x0001) code
+ byte config_str[32] = { 0x53, 0x48, 0x41, 0x33, 0x01, 0x00, 0 };
+ store_le(u32bit(output_bits), config_str + 8);
+
+ reset_tweak(T, SKEIN_CONFIG, true);
+ ubi_512(H, T, config_str, sizeof(config_str));
+
+ if(personalization != "")
+ {
+ /*
+ This is a limitation of this implementation, and not of the
+ algorithm specification. Could be fixed relatively easily, but
+ doesn't seem worth the trouble.
+ */
+ if(personalization.length() > 64)
+ throw Invalid_Argument("Skein personalization must be <= 64 bytes");
+
+ const byte* bits = reinterpret_cast<const byte*>(personalization.data());
+
+ reset_tweak(T, SKEIN_PERSONALIZATION, true);
+ ubi_512(H, T, bits, personalization.length());
+ }
+
+ reset_tweak(T, SKEIN_MSG, false);
+ }
+
+}
+
+Skein_512::Skein_512(size_t arg_output_bits,
+ const std::string& arg_personalization) :
+ personalization(arg_personalization),
+ output_bits(arg_output_bits),
+ H(9), T(3), buffer(64), buf_pos(0)
+ {
+ if(output_bits == 0 || output_bits % 8 != 0 || output_bits > 64*1024)
+ throw Invalid_Argument("Bad output bits size for Skein-512");
+
+ initial_block(H, T, output_bits, personalization);
+ }
+
+std::string Skein_512::name() const
+ {
+ if(personalization != "")
+ return "Skein-512(" + std::to_string(output_bits) + "," +
+ personalization + ")";
+ return "Skein-512(" + std::to_string(output_bits) + ")";
+ }
+
+HashFunction* Skein_512::clone() const
+ {
+ return new Skein_512(output_bits, personalization);
+ }
+
+void Skein_512::clear()
+ {
+ zeroise(H);
+ zeroise(T);
+ zeroise(buffer);
+ buf_pos = 0;
+ }
+
+void Skein_512::add_data(const byte input[], size_t length)
+ {
+ if(length == 0)
+ return;
+
+ if(buf_pos)
+ {
+ buffer_insert(buffer, buf_pos, input, length);
+ if(buf_pos + length > 64)
+ {
+ ubi_512(H, T, &buffer[0], buffer.size());
+
+ input += (64 - buf_pos);
+ length -= (64 - buf_pos);
+ buf_pos = 0;
+ }
+ }
+
+ const size_t full_blocks = (length - 1) / 64;
+
+ if(full_blocks)
+ ubi_512(H, T, input, 64*full_blocks);
+
+ length -= full_blocks * 64;
+
+ buffer_insert(buffer, buf_pos, input + full_blocks * 64, length);
+ buf_pos += length;
+ }
+
+void Skein_512::final_result(byte out[])
+ {
+ T[1] |= (static_cast<u64bit>(1) << 63); // final block flag
+
+ for(size_t i = buf_pos; i != buffer.size(); ++i)
+ buffer[i] = 0;
+
+ ubi_512(H, T, &buffer[0], buf_pos);
+
+ byte counter[8] = { 0 };
+
+ size_t out_bytes = output_bits / 8;
+
+ secure_vector<u64bit> H_out(9);
+
+ while(out_bytes)
+ {
+ const size_t to_proc = std::min<size_t>(out_bytes, 64);
+
+ copy_mem(&H_out[0], &H[0], 8);
+
+ reset_tweak(T, SKEIN_OUTPUT, true);
+ ubi_512(H_out, T, counter, sizeof(counter));
+
+ for(size_t i = 0; i != to_proc; ++i)
+ out[i] = get_byte(7-i%8, H_out[i/8]);
+
+ out_bytes -= to_proc;
+ out += to_proc;
+
+ for(size_t i = 0; i != sizeof(counter); ++i)
+ if(++counter[i])
+ break;
+ }
+
+ buf_pos = 0;
+ initial_block(H, T, output_bits, personalization);
+ }
+
+}