summaryrefslogtreecommitdiff
path: root/skein512.c
diff options
context:
space:
mode:
Diffstat (limited to 'skein512.c')
-rw-r--r--skein512.c147
1 files changed, 147 insertions, 0 deletions
diff --git a/skein512.c b/skein512.c
new file mode 100644
index 00000000..ea78859f
--- /dev/null
+++ b/skein512.c
@@ -0,0 +1,147 @@
+/* skein512.c
+
+ Copyright (C) 2016, 2017, 2018 Niels Möller
+
+ This file is part of GNU Nettle.
+
+ GNU Nettle is free software: you can redistribute it and/or
+ modify it under the terms of either:
+
+ * the GNU Lesser General Public License as published by the Free
+ Software Foundation; either version 3 of the License, or (at your
+ option) any later version.
+
+ or
+
+ * the GNU General Public License as published by the Free
+ Software Foundation; either version 2 of the License, or (at your
+ option) any later version.
+
+ or both in parallel, as here.
+
+ GNU Nettle is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received copies of the GNU General Public License and
+ the GNU Lesser General Public License along with this program. If
+ not, see http://www.gnu.org/licenses/.
+*/
+
+#if HAVE_CONFIG_H
+# include "config.h"
+#endif
+
+#include <assert.h>
+#include <string.h>
+
+#include "skein.h"
+
+#include "nettle-write.h"
+
+void
+_skein512_expand(uint64_t keys[_SKEIN512_NKEYS])
+{
+ uint64_t sum;
+ unsigned i;
+
+ for (i = 0, sum = _SKEIN_C240; i < _SKEIN512_LENGTH; i++)
+ sum ^= keys[i];
+ keys[_SKEIN512_LENGTH] = sum;
+
+ /* Repeat keys, for simpler indexing. */
+ for (i = _SKEIN512_LENGTH + 1; i < _SKEIN512_NKEYS; i++)
+ keys[i] = keys[i-9];
+}
+
+void
+skein512_init(struct skein512_ctx *ctx)
+{
+ static const uint64_t G0[_SKEIN512_LENGTH] = {
+ 0x4903ADFF749C51CEull, 0x0D95DE399746DF03ull,
+ 0x8FD1934127C79BCEull, 0x9A255629FF352CB1ull,
+ 0x5DB62599DF6CA7B0ull, 0xEABE394CA9D5C3F4ull,
+ 0x991112C71A75B523ull, 0xAE18A40B660FCC33ull,
+ };
+ memcpy (ctx->state, G0, sizeof(G0));
+ ctx->count = 0;
+ ctx->index = 0;
+}
+
+static void
+skein512_process_block(struct skein512_ctx *ctx,
+ unsigned tag, unsigned length,
+ const uint8_t *data)
+{
+ /* Expand key */
+ uint64_t tweak[_SKEIN_NTWEAK];
+
+ tag |= ((ctx->count == 0) << 6);
+
+ tweak[0] = (ctx->count << 6) + length;
+ tweak[1] = (ctx->count >> 58) | ((uint64_t) tag << 56);
+ _skein512_expand(ctx->state);
+
+ _skein512_block(ctx->state, ctx->state, tweak, data);
+
+ ctx->count++;
+
+ /* Wraparound not handled (limited message size). */
+ assert (ctx->count > 0);
+}
+
+void
+skein512_update(struct skein512_ctx *ctx,
+ size_t length,
+ const uint8_t *data)
+{
+ if (ctx->index > 0)
+ {
+ unsigned left = SKEIN512_BLOCK_SIZE - ctx->index;
+ if (length <= left)
+ {
+ memcpy (ctx->block + ctx->index, data, length);
+ ctx->index += length;
+ return;
+ }
+ memcpy (ctx->block + ctx->index, data, left);
+ data += left;
+ length -= left;
+
+ assert (length > 0);
+
+ skein512_process_block(ctx, 0x30, SKEIN512_BLOCK_SIZE, ctx->block);
+ }
+ while (length > SKEIN512_BLOCK_SIZE)
+ {
+ skein512_process_block(ctx, 0x30, SKEIN512_BLOCK_SIZE, data);
+ data += SKEIN512_BLOCK_SIZE;
+ length -= SKEIN512_BLOCK_SIZE;
+ }
+ assert (length <= SKEIN512_BLOCK_SIZE);
+ memcpy (ctx->block, data, length);
+ ctx->index = length;
+}
+
+void
+skein512_digest(struct skein512_ctx *ctx,
+ size_t length,
+ uint8_t *digest)
+{
+ static const uint8_t zeros[SKEIN512_BLOCK_SIZE];
+
+ /* FIXME: Should be always true. */
+ if (ctx->index > 0 || ctx->count == 0)
+ {
+ memset (ctx->block + ctx->index, 0,
+ SKEIN512_BLOCK_SIZE - ctx->index);
+ skein512_process_block(ctx, 0xb0, ctx->index, ctx->block);
+ }
+ /* Reset count for output processing. */
+ ctx->count = 0;
+ skein512_process_block(ctx, 0xff, 8, zeros);
+ _nettle_write_le64(length, digest, ctx->state);
+
+ skein512_init(ctx);
+}