mirror of
https://github.com/reactos/reactos.git
synced 2024-10-30 19:41:57 +00:00
508 lines
12 KiB
C
508 lines
12 KiB
C
/* @(#)sha3.c 1.4 15/12/27 2015 J. Schilling */
|
|
#include <schily/mconfig.h>
|
|
#ifndef lint
|
|
static UConst char sccsid[] =
|
|
"@(#)sha3.c 1.4 15/12/27 2015 J. Schilling";
|
|
#endif
|
|
/*
|
|
* SHA3 hash code taken from
|
|
* https://github.com/rhash/RHash/tree/master/librhash
|
|
*
|
|
* Portions Copyright (c) 2015 J. Schilling
|
|
*/
|
|
|
|
/*
|
|
* sha3.c - an implementation of Secure Hash Algorithm 3 (Keccak).
|
|
* based on the
|
|
* The Keccak SHA-3 submission. Submission to NIST (Round 3), 2011
|
|
* by Guido Bertoni, Joan Daemen, Michaël Peeters and Gilles Van Assche
|
|
*
|
|
* Copyright: 2013 Aleksey Kravchenko <rhash.admin@gmail.com>
|
|
*
|
|
* Permission is hereby granted, free of charge, to any person obtaining a
|
|
* copy of this software and associated documentation files (the "Software"),
|
|
* to deal in the Software without restriction, including without limitation
|
|
* the rights to use, copy, modify, merge, publish, distribute, sublicense,
|
|
* and/or sell copies of the Software, and to permit persons to whom the
|
|
* Software is furnished to do so.
|
|
*
|
|
* This program 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. Use this program at your own risk!
|
|
*/
|
|
|
|
#include <schily/assert.h>
|
|
#include <schily/string.h>
|
|
#include "byte_order.h"
|
|
#include <schily/sha3.h>
|
|
|
|
#ifdef HAVE_LONGLONG
|
|
|
|
#if !defined(HAVE_MEMCPY) || !defined(HAVE_MEMSET)
|
|
#include <schily/schily.h>
|
|
#endif
|
|
#if !defined(HAVE_MEMCPY) && !defined(memcpy)
|
|
#define memcpy(s1, s2, n) movebytes(s2, s1, n)
|
|
#endif
|
|
#if !defined(HAVE_MEMSET) && !defined(memset)
|
|
#define memset(s, c, n) fillbytes(s, n, c)
|
|
#endif
|
|
|
|
static void rhash_keccak_init __PR((sha3_ctx *ctx, unsigned bits));
|
|
static void keccak_theta __PR((UInt64_t *A));
|
|
static void keccak_pi __PR((UInt64_t *A));
|
|
static void keccak_chi __PR((UInt64_t *A));
|
|
static void rhash_sha3_permutation __PR((UInt64_t *state));
|
|
static void rhash_sha3_process_block __PR((UInt64_t hash[25],
|
|
const UInt64_t *block,
|
|
size_t block_size));
|
|
|
|
/*
|
|
* The Cygwin compile environment incorrectly implements #pragma weak.
|
|
* The weak symbols are only defined as local symbols making it impossible
|
|
* to use them from outside the scope of this source file.
|
|
* A platform that allows linking with global symbols has HAVE_LINK_WEAK
|
|
* defined.
|
|
*/
|
|
#if defined(HAVE_PRAGMA_WEAK) && defined(HAVE_LINK_WEAK)
|
|
#pragma weak SHA3_224_Init = rhash_sha3_224_init
|
|
#pragma weak SHA3_256_Init = rhash_sha3_256_init
|
|
#pragma weak SHA3_384_Init = rhash_sha3_384_init
|
|
#pragma weak SHA3_512_Init = rhash_sha3_512_init
|
|
#pragma weak SHA3_Update = rhash_sha3_update
|
|
#else
|
|
|
|
void SHA3_224_Init __PR((SHA3_CTX *ctx));
|
|
void SHA3_256_Init __PR((SHA3_CTX *ctx));
|
|
void SHA3_384_Init __PR((SHA3_CTX *ctx));
|
|
void SHA3_512_Init __PR((SHA3_CTX *ctx));
|
|
void SHA3_Update __PR((SHA3_CTX *ctx,
|
|
const unsigned char *msg,
|
|
size_t size));
|
|
|
|
void
|
|
SHA3_224_Init(ctx)
|
|
SHA3_CTX *ctx;
|
|
{
|
|
rhash_sha3_224_init(ctx);
|
|
}
|
|
|
|
void
|
|
SHA3_256_Init(ctx)
|
|
SHA3_CTX *ctx;
|
|
{
|
|
rhash_sha3_256_init(ctx);
|
|
}
|
|
|
|
void
|
|
SHA3_384_Init(ctx)
|
|
SHA3_CTX *ctx;
|
|
{
|
|
rhash_sha3_384_init(ctx);
|
|
}
|
|
|
|
void
|
|
SHA3_512_Init(ctx)
|
|
SHA3_CTX *ctx;
|
|
{
|
|
rhash_sha3_512_init(ctx);
|
|
}
|
|
|
|
void
|
|
SHA3_Update(ctx, msg, size)
|
|
SHA3_CTX *ctx;
|
|
const unsigned char *msg;
|
|
size_t size;
|
|
{
|
|
rhash_sha3_update(ctx, msg, size);
|
|
}
|
|
#endif /* defined(HAVE_PRAGMA_WEAK) && defined(HAVE_LINK_WEAK) */
|
|
|
|
/* constants */
|
|
#define NumberOfRounds 24
|
|
|
|
/* SHA3 (Keccak) constants for 24 rounds */
|
|
static UInt64_t keccak_round_constants[NumberOfRounds] = {
|
|
UI64(0x0000000000000001), UI64(0x0000000000008082),
|
|
UI64(0x800000000000808A), UI64(0x8000000080008000),
|
|
UI64(0x000000000000808B), UI64(0x0000000080000001),
|
|
UI64(0x8000000080008081), UI64(0x8000000000008009),
|
|
UI64(0x000000000000008A), UI64(0x0000000000000088),
|
|
UI64(0x0000000080008009), UI64(0x000000008000000A),
|
|
UI64(0x000000008000808B), UI64(0x800000000000008B),
|
|
UI64(0x8000000000008089), UI64(0x8000000000008003),
|
|
UI64(0x8000000000008002), UI64(0x8000000000000080),
|
|
UI64(0x000000000000800A), UI64(0x800000008000000A),
|
|
UI64(0x8000000080008081), UI64(0x8000000000008080),
|
|
UI64(0x0000000080000001), UI64(0x8000000080008008)
|
|
};
|
|
|
|
/* Initializing a sha3 context for given number of output bits */
|
|
static void
|
|
rhash_keccak_init(ctx, bits)
|
|
sha3_ctx *ctx;
|
|
unsigned bits;
|
|
{
|
|
/* NB: The Keccak capacity parameter = bits * 2 */
|
|
unsigned rate = 1600 - bits * 2;
|
|
|
|
memset(ctx, 0, sizeof (sha3_ctx));
|
|
ctx->block_size = rate / 8;
|
|
assert(rate <= 1600 && (rate % 64) == 0);
|
|
}
|
|
|
|
/*
|
|
* Initialize context before calculating hash.
|
|
*
|
|
* @param ctx context to initialize
|
|
*/
|
|
void
|
|
rhash_sha3_224_init(ctx)
|
|
sha3_ctx *ctx;
|
|
{
|
|
rhash_keccak_init(ctx, 224);
|
|
}
|
|
|
|
/*
|
|
* Initialize context before calculating hash.
|
|
*
|
|
* @param ctx context to initialize
|
|
*/
|
|
void
|
|
rhash_sha3_256_init(ctx)
|
|
sha3_ctx *ctx;
|
|
{
|
|
rhash_keccak_init(ctx, 256);
|
|
}
|
|
|
|
/*
|
|
* Initialize context before calculating hash.
|
|
*
|
|
* @param ctx context to initialize
|
|
*/
|
|
void
|
|
rhash_sha3_384_init(ctx)
|
|
sha3_ctx *ctx;
|
|
{
|
|
rhash_keccak_init(ctx, 384);
|
|
}
|
|
|
|
/*
|
|
* Initialize context before calculating hash.
|
|
*
|
|
* @param ctx context to initialize
|
|
*/
|
|
void
|
|
rhash_sha3_512_init(ctx)
|
|
sha3_ctx *ctx;
|
|
{
|
|
rhash_keccak_init(ctx, 512);
|
|
}
|
|
|
|
/* Keccak theta() transformation */
|
|
static void
|
|
keccak_theta(A)
|
|
UInt64_t *A;
|
|
{
|
|
unsigned int x;
|
|
UInt64_t C[5], D[5];
|
|
|
|
for (x = 0; x < 5; x++) {
|
|
C[x] = A[x] ^ A[x + 5] ^ A[x + 10] ^ A[x + 15] ^ A[x + 20];
|
|
}
|
|
D[0] = ROTL64(C[1], 1) ^ C[4];
|
|
D[1] = ROTL64(C[2], 1) ^ C[0];
|
|
D[2] = ROTL64(C[3], 1) ^ C[1];
|
|
D[3] = ROTL64(C[4], 1) ^ C[2];
|
|
D[4] = ROTL64(C[0], 1) ^ C[3];
|
|
|
|
for (x = 0; x < 5; x++) {
|
|
A[x] ^= D[x];
|
|
A[x + 5] ^= D[x];
|
|
A[x + 10] ^= D[x];
|
|
A[x + 15] ^= D[x];
|
|
A[x + 20] ^= D[x];
|
|
}
|
|
}
|
|
|
|
/* Keccak pi() transformation */
|
|
static void
|
|
keccak_pi(A)
|
|
UInt64_t *A;
|
|
{
|
|
UInt64_t A1;
|
|
A1 = A[1];
|
|
A[ 1] = A[ 6];
|
|
A[ 6] = A[ 9];
|
|
A[ 9] = A[22];
|
|
A[22] = A[14];
|
|
A[14] = A[20];
|
|
A[20] = A[ 2];
|
|
A[ 2] = A[12];
|
|
A[12] = A[13];
|
|
A[13] = A[19];
|
|
A[19] = A[23];
|
|
A[23] = A[15];
|
|
A[15] = A[ 4];
|
|
A[ 4] = A[24];
|
|
A[24] = A[21];
|
|
A[21] = A[ 8];
|
|
A[ 8] = A[16];
|
|
A[16] = A[ 5];
|
|
A[ 5] = A[ 3];
|
|
A[ 3] = A[18];
|
|
A[18] = A[17];
|
|
A[17] = A[11];
|
|
A[11] = A[ 7];
|
|
A[ 7] = A[10];
|
|
A[10] = A1;
|
|
/* note: A[ 0] is left as is */
|
|
}
|
|
|
|
/* Keccak chi() transformation */
|
|
static void
|
|
keccak_chi(A)
|
|
UInt64_t *A;
|
|
{
|
|
int i;
|
|
for (i = 0; i < 25; i += 5) {
|
|
UInt64_t A0 = A[0 + i], A1 = A[1 + i];
|
|
A[0 + i] ^= ~A1 & A[2 + i];
|
|
A[1 + i] ^= ~A[2 + i] & A[3 + i];
|
|
A[2 + i] ^= ~A[3 + i] & A[4 + i];
|
|
A[3 + i] ^= ~A[4 + i] & A0;
|
|
A[4 + i] ^= ~A0 & A1;
|
|
}
|
|
}
|
|
|
|
static void
|
|
rhash_sha3_permutation(state)
|
|
UInt64_t *state;
|
|
{
|
|
int round;
|
|
for (round = 0; round < NumberOfRounds; round++)
|
|
{
|
|
keccak_theta(state);
|
|
|
|
/* apply Keccak rho() transformation */
|
|
state[ 1] = ROTL64(state[ 1], 1);
|
|
state[ 2] = ROTL64(state[ 2], 62);
|
|
state[ 3] = ROTL64(state[ 3], 28);
|
|
state[ 4] = ROTL64(state[ 4], 27);
|
|
state[ 5] = ROTL64(state[ 5], 36);
|
|
state[ 6] = ROTL64(state[ 6], 44);
|
|
state[ 7] = ROTL64(state[ 7], 6);
|
|
state[ 8] = ROTL64(state[ 8], 55);
|
|
state[ 9] = ROTL64(state[ 9], 20);
|
|
state[10] = ROTL64(state[10], 3);
|
|
state[11] = ROTL64(state[11], 10);
|
|
state[12] = ROTL64(state[12], 43);
|
|
state[13] = ROTL64(state[13], 25);
|
|
state[14] = ROTL64(state[14], 39);
|
|
state[15] = ROTL64(state[15], 41);
|
|
state[16] = ROTL64(state[16], 45);
|
|
state[17] = ROTL64(state[17], 15);
|
|
state[18] = ROTL64(state[18], 21);
|
|
state[19] = ROTL64(state[19], 8);
|
|
state[20] = ROTL64(state[20], 18);
|
|
state[21] = ROTL64(state[21], 2);
|
|
state[22] = ROTL64(state[22], 61);
|
|
state[23] = ROTL64(state[23], 56);
|
|
state[24] = ROTL64(state[24], 14);
|
|
|
|
keccak_pi(state);
|
|
keccak_chi(state);
|
|
|
|
/* apply iota(state, round) */
|
|
*state ^= keccak_round_constants[round];
|
|
}
|
|
}
|
|
|
|
/*
|
|
* The core transformation. Process the specified block of data.
|
|
*
|
|
* @param hash the algorithm state
|
|
* @param block the message block to process
|
|
* @param block_size the size of the processed block in bytes
|
|
*/
|
|
static void
|
|
rhash_sha3_process_block(hash, block, block_size)
|
|
UInt64_t hash[25];
|
|
const UInt64_t *block;
|
|
size_t block_size;
|
|
{
|
|
/* expanded loop */
|
|
hash[ 0] ^= le2me_64(block[ 0]);
|
|
hash[ 1] ^= le2me_64(block[ 1]);
|
|
hash[ 2] ^= le2me_64(block[ 2]);
|
|
hash[ 3] ^= le2me_64(block[ 3]);
|
|
hash[ 4] ^= le2me_64(block[ 4]);
|
|
hash[ 5] ^= le2me_64(block[ 5]);
|
|
hash[ 6] ^= le2me_64(block[ 6]);
|
|
hash[ 7] ^= le2me_64(block[ 7]);
|
|
hash[ 8] ^= le2me_64(block[ 8]);
|
|
/* if not sha3-512 */
|
|
if (block_size > 72) {
|
|
hash[ 9] ^= le2me_64(block[ 9]);
|
|
hash[10] ^= le2me_64(block[10]);
|
|
hash[11] ^= le2me_64(block[11]);
|
|
hash[12] ^= le2me_64(block[12]);
|
|
/* if not sha3-384 */
|
|
if (block_size > 104) {
|
|
hash[13] ^= le2me_64(block[13]);
|
|
hash[14] ^= le2me_64(block[14]);
|
|
hash[15] ^= le2me_64(block[15]);
|
|
hash[16] ^= le2me_64(block[16]);
|
|
/* if not sha3-256 */
|
|
if (block_size > 136) {
|
|
hash[17] ^= le2me_64(block[17]);
|
|
#ifdef FULL_SHA3_FAMILY_SUPPORT
|
|
/* if not sha3-224 */
|
|
if (block_size > 144) {
|
|
hash[18] ^= le2me_64(block[18]);
|
|
hash[19] ^= le2me_64(block[19]);
|
|
hash[20] ^= le2me_64(block[20]);
|
|
hash[21] ^= le2me_64(block[21]);
|
|
hash[22] ^= le2me_64(block[22]);
|
|
hash[23] ^= le2me_64(block[23]);
|
|
hash[24] ^= le2me_64(block[24]);
|
|
}
|
|
#endif
|
|
}
|
|
}
|
|
}
|
|
/* make a permutation of the hash */
|
|
rhash_sha3_permutation(hash);
|
|
}
|
|
|
|
#define SHA3_FINALIZED 0x80000000
|
|
|
|
/*
|
|
* Calculate message hash.
|
|
* Can be called repeatedly with chunks of the message to be hashed.
|
|
*
|
|
* @param ctx the algorithm context containing current hashing state
|
|
* @param msg message chunk
|
|
* @param size length of the message chunk
|
|
*/
|
|
void
|
|
rhash_sha3_update(ctx, msg, size)
|
|
sha3_ctx *ctx;
|
|
const unsigned char *msg;
|
|
size_t size;
|
|
{
|
|
size_t idx = (size_t)ctx->rest;
|
|
size_t block_size = (size_t)ctx->block_size;
|
|
|
|
if (ctx->rest & SHA3_FINALIZED)
|
|
return; /* too late for additional input */
|
|
ctx->rest = (unsigned)((ctx->rest + size) % block_size);
|
|
|
|
/* fill partial block */
|
|
if (idx) {
|
|
size_t left = block_size - idx;
|
|
memcpy((char *)ctx->message + idx, msg,
|
|
(size < left ? size : left));
|
|
if (size < left)
|
|
return;
|
|
|
|
/* process partial block */
|
|
rhash_sha3_process_block(ctx->hash, ctx->message, block_size);
|
|
msg += left;
|
|
size -= left;
|
|
}
|
|
while (size >= block_size) {
|
|
UInt64_t *aligned_message_block;
|
|
if (IS_ALIGNED_64(msg)) {
|
|
/*
|
|
* the most common case is processing of an already
|
|
* aligned message without copying it
|
|
*/
|
|
aligned_message_block = (UInt64_t *)msg;
|
|
} else {
|
|
memcpy(ctx->message, msg, block_size);
|
|
aligned_message_block = ctx->message;
|
|
}
|
|
|
|
rhash_sha3_process_block(ctx->hash, aligned_message_block,
|
|
block_size);
|
|
msg += block_size;
|
|
size -= block_size;
|
|
}
|
|
if (size) {
|
|
memcpy(ctx->message, msg, size); /* save leftovers */
|
|
}
|
|
}
|
|
|
|
/*
|
|
* Store calculated hash into the given array.
|
|
*
|
|
* @param ctx the algorithm context containing current hashing state
|
|
* @param result calculated hash in binary form
|
|
*/
|
|
void
|
|
rhash_sha3_final(ctx, result)
|
|
sha3_ctx *ctx;
|
|
unsigned char *result;
|
|
{
|
|
size_t digest_length = 100 - ctx->block_size / 2;
|
|
const size_t block_size = ctx->block_size;
|
|
|
|
if (!(ctx->rest & SHA3_FINALIZED))
|
|
{
|
|
/* clear the rest of the data queue */
|
|
memset((char *)ctx->message + ctx->rest, 0,
|
|
block_size - ctx->rest);
|
|
((char *)ctx->message)[ctx->rest] |= 0x06;
|
|
((char *)ctx->message)[block_size - 1] |= 0x80;
|
|
|
|
/* process final block */
|
|
rhash_sha3_process_block(ctx->hash, ctx->message, block_size);
|
|
ctx->rest = SHA3_FINALIZED; /* mark context as finalized */
|
|
}
|
|
|
|
assert(block_size > digest_length);
|
|
if (result) me64_to_le_str(result, ctx->hash, digest_length);
|
|
}
|
|
|
|
void
|
|
SHA3_Final(result, ctx)
|
|
UInt8_t *result;
|
|
SHA3_CTX *ctx;
|
|
{
|
|
rhash_sha3_final(ctx, result);
|
|
}
|
|
|
|
#ifdef USE_KECCAK
|
|
/*
|
|
* Store calculated hash into the given array.
|
|
*
|
|
* @param ctx the algorithm context containing current hashing state
|
|
* @param result calculated hash in binary form
|
|
*/
|
|
void
|
|
rhash_keccak_final(sha3_ctx *ctx, unsigned char *result)
|
|
{
|
|
size_t digest_length = 100 - ctx->block_size / 2;
|
|
const size_t block_size = ctx->block_size;
|
|
|
|
if (!(ctx->rest & SHA3_FINALIZED)) {
|
|
/* clear the rest of the data queue */
|
|
memset((char *)ctx->message + ctx->rest, 0,
|
|
block_size - ctx->rest);
|
|
((char *)ctx->message)[ctx->rest] |= 0x01;
|
|
((char *)ctx->message)[block_size - 1] |= 0x80;
|
|
|
|
/* process final block */
|
|
rhash_sha3_process_block(ctx->hash, ctx->message, block_size);
|
|
ctx->rest = SHA3_FINALIZED; /* mark context as finalized */
|
|
}
|
|
|
|
assert(block_size > digest_length);
|
|
if (result)
|
|
me64_to_le_str(result, ctx->hash, digest_length);
|
|
}
|
|
#endif /* USE_KECCAK */
|
|
|
|
#endif /* HAVE_LONGLONG */
|