feat: Replace aCAPTCHA with official ALTCHA (altcha.org) Proof-of-Work web component widget
This commit is contained in:
+143
@@ -0,0 +1,143 @@
|
||||
/*
|
||||
adler32.c -- compute the Adler-32 checksum of a data stream
|
||||
Copyright (C) 1995-2011, 2016 Mark Adler
|
||||
|
||||
Licensed under the zlib license:
|
||||
|
||||
Copyright (C) 1995-2017 Jean-loup Gailly and Mark Adler
|
||||
|
||||
This software is provided 'as-is', without any express or implied
|
||||
warranty. In no event will the authors be held liable for any damages
|
||||
arising from the use of this software.
|
||||
|
||||
Permission is granted to anyone to use this software for any purpose,
|
||||
including commercial applications, and to alter it and redistribute it
|
||||
freely, subject to the following restrictions:
|
||||
|
||||
1. The origin of this software must not be misrepresented; you must not
|
||||
claim that you wrote the original software. If you use this software
|
||||
in a product, an acknowledgment in the product documentation would be
|
||||
appreciated but is not required.
|
||||
2. Altered source versions must be plainly marked as such, and must not be
|
||||
misrepresented as being the original software.
|
||||
3. This notice may not be removed or altered from any source distribution.
|
||||
|
||||
Jean-loup Gailly Mark Adler
|
||||
jloup@gzip.org madler@alumni.caltech.edu
|
||||
|
||||
Modified for hash-wasm by Nicholas Sherlock and Dani Biro, 2021
|
||||
*/
|
||||
|
||||
#define WITH_BUFFER
|
||||
#include "hash-wasm.h"
|
||||
|
||||
#define bswap_32(x) __builtin_bswap32(x)
|
||||
|
||||
#define BASE 65521U /* largest prime smaller than 65536 */
|
||||
#define NMAX 5552
|
||||
/* NMAX is the largest n such that 255n(n+1)/2 + (n+1)(BASE-1) <= 2^32-1 */
|
||||
|
||||
#define DO1(b,i) adler += ((b) >> i) & 0xFF; sum2 += adler;
|
||||
#define DO4(buf,i) { uint32_t b = ((uint32_t*)buf)[i]; DO1(b,0); DO1(b,8); DO1(b,16); DO1(b,24); }
|
||||
#define DO16(buf) DO4(buf,0); DO4(buf,1); DO4(buf,2); DO4(buf,3);
|
||||
|
||||
|
||||
#define MOD(a) a %= BASE
|
||||
#define MOD28(a) a %= BASE
|
||||
#define MOD63(a) a %= BASE
|
||||
|
||||
uint32_t previousAdler = 1;
|
||||
|
||||
WASM_EXPORT
|
||||
void Hash_Init() {
|
||||
previousAdler = 1;
|
||||
}
|
||||
|
||||
static uint32_t adler32(uint32_t adler, const uint8_t *buf, uint32_t len) {
|
||||
/* split Adler-32 into component sums */
|
||||
uint32_t sum2 = (adler >> 16) & 0xffff;
|
||||
adler &= 0xffff;
|
||||
|
||||
/* in case user likes doing a byte at a time, keep it fast */
|
||||
if (len == 1) {
|
||||
adler += buf[0];
|
||||
if (adler >= BASE) {
|
||||
adler -= BASE;
|
||||
}
|
||||
sum2 += adler;
|
||||
if (sum2 >= BASE) {
|
||||
sum2 -= BASE;
|
||||
}
|
||||
return adler | (sum2 << 16);
|
||||
}
|
||||
|
||||
/* in case short lengths are provided, keep it somewhat fast */
|
||||
if (len < 16) {
|
||||
while (len--) {
|
||||
adler += *buf++;
|
||||
sum2 += adler;
|
||||
}
|
||||
if (adler >= BASE) {
|
||||
adler -= BASE;
|
||||
}
|
||||
MOD28(sum2); /* only added so many BASE's */
|
||||
return adler | (sum2 << 16);
|
||||
}
|
||||
|
||||
/* do length NMAX blocks -- requires just one modulo operation */
|
||||
while (len >= NMAX) {
|
||||
len -= NMAX;
|
||||
uint32_t n = NMAX / 16; /* NMAX is divisible by 16 */
|
||||
do {
|
||||
DO16(buf); /* 16 sums unrolled */
|
||||
buf += 16;
|
||||
} while (--n);
|
||||
MOD(adler);
|
||||
MOD(sum2);
|
||||
}
|
||||
|
||||
/* do remaining bytes (less than NMAX, still just one modulo) */
|
||||
if (len) { /* avoid modulos if none remaining */
|
||||
while (len >= 16) {
|
||||
len -= 16;
|
||||
DO16(buf);
|
||||
buf += 16;
|
||||
}
|
||||
while (len--) {
|
||||
adler += *buf++;
|
||||
sum2 += adler;
|
||||
}
|
||||
MOD(adler);
|
||||
MOD(sum2);
|
||||
}
|
||||
|
||||
/* return recombined sums */
|
||||
return adler | (sum2 << 16);
|
||||
}
|
||||
|
||||
WASM_EXPORT
|
||||
void Hash_Update(uint32_t len) {
|
||||
const uint8_t *buf = main_buffer;
|
||||
|
||||
previousAdler = adler32(previousAdler, buf, len);
|
||||
}
|
||||
|
||||
WASM_EXPORT
|
||||
void Hash_Final() {
|
||||
((uint32_t*)main_buffer)[0] = bswap_32(previousAdler);
|
||||
}
|
||||
|
||||
WASM_EXPORT
|
||||
const uint32_t STATE_SIZE = sizeof(previousAdler);
|
||||
|
||||
WASM_EXPORT
|
||||
uint8_t* Hash_GetState() {
|
||||
return (uint8_t*) &previousAdler;
|
||||
}
|
||||
|
||||
WASM_EXPORT
|
||||
void Hash_Calculate(uint32_t length) {
|
||||
Hash_Init();
|
||||
Hash_Update(length);
|
||||
Hash_Final();
|
||||
}
|
||||
+237
@@ -0,0 +1,237 @@
|
||||
/*
|
||||
Based on Golang's Argon2 implementation from crypto package
|
||||
|
||||
Written for hash-wasm by Dani Biró
|
||||
*/
|
||||
|
||||
#include "hash-wasm.h"
|
||||
|
||||
#define BYTES_PER_PAGE 65536
|
||||
|
||||
uint8_t *B = NULL;
|
||||
uint64_t B_size = 0;
|
||||
|
||||
WASM_EXPORT
|
||||
int8_t Hash_SetMemorySize(uint32_t total_bytes) {
|
||||
uint32_t bytes_required = total_bytes - B_size;
|
||||
|
||||
if (bytes_required > 0) {
|
||||
uint32_t blocks = bytes_required / BYTES_PER_PAGE;
|
||||
if (blocks * BYTES_PER_PAGE < bytes_required) {
|
||||
blocks += 1;
|
||||
}
|
||||
|
||||
if (__builtin_wasm_memory_grow(0, blocks) == -1) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
B_size += blocks * BYTES_PER_PAGE;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
WASM_EXPORT
|
||||
uint8_t *Hash_GetBuffer() {
|
||||
if (B == NULL) {
|
||||
// start of new memory
|
||||
B = (uint8_t *)(__builtin_wasm_memory_size(0) * BYTES_PER_PAGE);
|
||||
if (Hash_SetMemorySize(512 * 1024) == -1) { // always preallocate 16kb to not cause problems with the other hashes
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
|
||||
return B;
|
||||
}
|
||||
|
||||
static __inline__ uint64_t rotr64(const uint64_t w, const unsigned c) {
|
||||
return (w >> c) | (w << (64 - c));
|
||||
}
|
||||
|
||||
#define G(a, b, c, d) \
|
||||
do { \
|
||||
a = a + b + 2 * (a & 0xFFFFFFFF) * (b & 0xFFFFFFFF); \
|
||||
d = rotr64(d ^ a, 32); \
|
||||
c = c + d + 2 * (c & 0xFFFFFFFF) * (d & 0xFFFFFFFF); \
|
||||
b = rotr64(b ^ c, 24); \
|
||||
a = a + b + 2 * (a & 0xFFFFFFFF) * (b & 0xFFFFFFFF); \
|
||||
d = rotr64(d ^ a, 16); \
|
||||
c = c + d + 2 * (c & 0xFFFFFFFF) * (d & 0xFFFFFFFF); \
|
||||
b = rotr64(b ^ c, 63); \
|
||||
} while (0)
|
||||
|
||||
void P(
|
||||
uint64_t *a0, uint64_t *a1, uint64_t *a2, uint64_t *a3,
|
||||
uint64_t *a4, uint64_t *a5, uint64_t *a6, uint64_t *a7,
|
||||
uint64_t *a8, uint64_t *a9, uint64_t *a10, uint64_t *a11,
|
||||
uint64_t *a12, uint64_t *a13, uint64_t *a14, uint64_t *a15
|
||||
) {
|
||||
G(*a0, *a4, *a8, *a12);
|
||||
G(*a1, *a5, *a9, *a13);
|
||||
G(*a2, *a6, *a10, *a14);
|
||||
G(*a3, *a7, *a11, *a15);
|
||||
G(*a0, *a5, *a10, *a15);
|
||||
G(*a1, *a6, *a11, *a12);
|
||||
G(*a2, *a7, *a8, *a13);
|
||||
G(*a3, *a4, *a9, *a14);
|
||||
}
|
||||
|
||||
uint32_t indexAlpha(
|
||||
uint64_t rand, uint32_t lanes, uint32_t segments,
|
||||
uint32_t parallelism, uint32_t k, uint32_t slice,
|
||||
uint32_t lane, uint32_t index
|
||||
) {
|
||||
uint32_t rlane = ((uint32_t)(rand >> 32)) % parallelism;
|
||||
|
||||
if (k == 0 && slice == 0) {
|
||||
rlane = lane;
|
||||
}
|
||||
|
||||
uint32_t max = segments * 3;
|
||||
uint32_t start = ((slice + 1) % 4) * segments;
|
||||
|
||||
if (lane == rlane) {
|
||||
max += index;
|
||||
}
|
||||
|
||||
if (k == 0) {
|
||||
max = slice * segments;
|
||||
start = 0;
|
||||
if (slice == 0 || lane == rlane) {
|
||||
max += index;
|
||||
}
|
||||
}
|
||||
|
||||
if (index == 0 || lane == rlane) {
|
||||
max--;
|
||||
}
|
||||
|
||||
uint64_t phi = rand & 0xFFFFFFFF;
|
||||
phi = phi * phi >> 32;
|
||||
phi = phi * max >> 32;
|
||||
uint32_t ri = (start + max - 1 - phi) % (uint64_t)lanes;
|
||||
|
||||
return rlane * lanes + ri;
|
||||
}
|
||||
|
||||
uint64_t t[128];
|
||||
|
||||
void block(uint64_t *z, uint64_t *a, uint64_t *b, int32_t xor) {
|
||||
#pragma clang loop unroll(full)
|
||||
for (int i = 0; i < 128; i++) {
|
||||
t[i] = a[i] ^ b[i];
|
||||
}
|
||||
|
||||
#pragma clang loop unroll(full)
|
||||
for (int i = 0; i < 128; i += 16) {
|
||||
P(
|
||||
&t[i], &t[i + 1], &t[i + 2], &t[i + 3], &t[i + 4], &t[i + 5], &t[i + 6], &t[i + 7],
|
||||
&t[i + 8], &t[i + 9], &t[i + 10], &t[i + 11], &t[i + 12], &t[i + 13], &t[i + 14], &t[i + 15]
|
||||
);
|
||||
}
|
||||
|
||||
#pragma clang loop unroll(full)
|
||||
for (int i = 0; i < 16; i += 2) {
|
||||
P(
|
||||
&t[i], &t[i + 1], &t[i + 16], &t[i + 17], &t[i + 32], &t[i + 33], &t[i + 48], &t[i + 49],
|
||||
&t[i + 64], &t[i + 65], &t[i + 80], &t[i + 81], &t[i + 96], &t[i + 97], &t[i + 112], &t[i + 113]
|
||||
);
|
||||
}
|
||||
|
||||
if (xor) {
|
||||
for (int i = 0; i < 128; i++) {
|
||||
z[i] ^= a[i] ^ b[i] ^ t[i];
|
||||
}
|
||||
} else {
|
||||
for (int i = 0; i < 128; i++) {
|
||||
z[i] = a[i] ^ b[i] ^ t[i];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
uint64_t addresses[128];
|
||||
uint64_t zero[128];
|
||||
uint64_t in[128];
|
||||
|
||||
WASM_EXPORT
|
||||
void Hash_Calculate(uint32_t length, uint32_t memorySize) {
|
||||
uint32_t *initVector = (uint32_t *)(B + 1024 * memorySize);
|
||||
uint32_t parallelism = initVector[0];
|
||||
uint32_t hashLength = initVector[1];
|
||||
uint32_t memorySize2 = initVector[2];
|
||||
uint32_t iterations = initVector[3];
|
||||
uint32_t version = initVector[4];
|
||||
uint32_t hashType = initVector[5];
|
||||
if (memorySize2 != memorySize) {
|
||||
return;
|
||||
}
|
||||
|
||||
uint32_t segments = memorySize / (parallelism * 4);
|
||||
memorySize = segments * parallelism * 4;
|
||||
uint32_t lanes = segments * 4;
|
||||
|
||||
in[3] = memorySize;
|
||||
in[4] = iterations;
|
||||
in[5] = hashType;
|
||||
|
||||
for (uint32_t k = 0; k < iterations; k++) {
|
||||
in[0] = k;
|
||||
for (uint8_t slice = 0; slice < 4; slice++) {
|
||||
in[2] = slice;
|
||||
for (uint32_t lane = 0; lane < parallelism; lane++) {
|
||||
in[1] = lane;
|
||||
in[6] = 0;
|
||||
uint32_t index = 0;
|
||||
if (k == 0 && slice == 0) {
|
||||
index = 2;
|
||||
if (hashType == 1 || hashType == 2) {
|
||||
in[6]++;
|
||||
block(addresses, in, zero, 0);
|
||||
block(addresses, addresses, zero, 0);
|
||||
}
|
||||
}
|
||||
uint32_t offset = lane * lanes + slice * segments + index;
|
||||
while (index < segments) {
|
||||
uint32_t prev = offset - 1;
|
||||
if (index == 0 && slice == 0) {
|
||||
prev += lanes;
|
||||
}
|
||||
|
||||
uint64_t rand;
|
||||
if (hashType == 1 || (hashType == 2 && k == 0 && slice < 2)) {
|
||||
if (index % 128 == 0) {
|
||||
in[6]++;
|
||||
block(addresses, in, zero, 0);
|
||||
block(addresses, addresses, zero, 0);
|
||||
}
|
||||
rand = addresses[index % 128];
|
||||
} else {
|
||||
rand = *(uint64_t *)(B + prev * 1024);
|
||||
}
|
||||
uint32_t newOffset = indexAlpha(rand, lanes, segments, parallelism, k, slice, lane, index);
|
||||
|
||||
block(
|
||||
(uint64_t *)&B[offset * 1024],
|
||||
(uint64_t *)&B[prev * 1024],
|
||||
(uint64_t *)&B[newOffset * 1024],
|
||||
1
|
||||
);
|
||||
index++;
|
||||
offset++;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
uint32_t destIndex = (memorySize - 1) * 1024;
|
||||
for (uint32_t lane = 0; lane < parallelism - 1; lane++) {
|
||||
uint32_t sourceIndex = (lane * lanes + lanes - 1) * 1024;
|
||||
for (uint32_t i = 0; i < 1024; i += 8) {
|
||||
*(uint64_t *)&B[destIndex + i] ^= *(uint64_t *)&B[sourceIndex + i];
|
||||
}
|
||||
}
|
||||
|
||||
for (uint16_t i = 0; i < 1024; i += 8) {
|
||||
*(uint64_t *)&B[i] = *(uint64_t *)&B[destIndex + i];
|
||||
}
|
||||
}
|
||||
+787
@@ -0,0 +1,787 @@
|
||||
/*
|
||||
* The crypt_blowfish homepage is:
|
||||
*
|
||||
* http://www.openwall.com/crypt/
|
||||
*
|
||||
* This code comes from John the Ripper password cracker, with reentrant
|
||||
* and crypt(3) interfaces added, but optimizations specific to password
|
||||
* cracking removed.
|
||||
*
|
||||
* Written by Solar Designer <solar at openwall.com> in 1998-2014.
|
||||
* No copyright is claimed, and the software is hereby placed in the public
|
||||
* domain. In case this attempt to disclaim copyright and place the software
|
||||
* in the public domain is deemed null and void, then the software is
|
||||
* Copyright (c) 1998-2014 Solar Designer and it is hereby released to the
|
||||
* general public under the following terms:
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted.
|
||||
*
|
||||
* There's ABSOLUTELY NO WARRANTY, express or implied.
|
||||
*
|
||||
* It is my intent that you should be able to use this on your system,
|
||||
* as part of a software package, or anywhere else to improve security,
|
||||
* ensure compatibility, or for any other purpose. I would appreciate
|
||||
* it if you give credit where it is due and keep your modifications in
|
||||
* the public domain as well, but I don't require that in order to let
|
||||
* you place this code and any modifications you make under a license
|
||||
* of your choice.
|
||||
*
|
||||
* This implementation is fully compatible with OpenBSD's bcrypt.c for prefix
|
||||
* "$2b$", originally by Niels Provos <provos at citi.umich.edu>, and it uses
|
||||
* some of his ideas. The password hashing algorithm was designed by David
|
||||
* Mazieres <dm at lcs.mit.edu>. For information on the level of
|
||||
* compatibility for bcrypt hash prefixes other than "$2b$", please refer to
|
||||
* the comments in BF_set_key() below and to the included crypt(3) man page.
|
||||
*
|
||||
* There's a paper on the algorithm that explains its design decisions:
|
||||
*
|
||||
* http://www.usenix.org/events/usenix99/provos.html
|
||||
*
|
||||
* Some of the tricks in BF_ROUND might be inspired by Eric Young's
|
||||
* Blowfish library (I can't be sure if I would think of something if I
|
||||
* hadn't seen his code).
|
||||
*
|
||||
* Modified for hash-wasm by Dani Biró
|
||||
*/
|
||||
|
||||
#define WITH_BUFFER
|
||||
#include "hash-wasm.h"
|
||||
|
||||
typedef unsigned int BF_word;
|
||||
typedef signed int BF_word_signed;
|
||||
|
||||
/* Number of Blowfish rounds, this is also hardcoded into a few places */
|
||||
#define BF_N 16
|
||||
|
||||
typedef BF_word BF_key[BF_N + 2];
|
||||
|
||||
typedef struct {
|
||||
BF_word S[4][0x100];
|
||||
BF_key P;
|
||||
} BF_ctx;
|
||||
|
||||
/*
|
||||
* Magic IV for 64 Blowfish encryptions that we do at the end.
|
||||
* The string is "OrpheanBeholderScryDoubt" on big-endian.
|
||||
*/
|
||||
static BF_word BF_magic_w[6] = {
|
||||
0x4F727068, 0x65616E42, 0x65686F6C,
|
||||
0x64657253, 0x63727944, 0x6F756274
|
||||
};
|
||||
|
||||
/*
|
||||
* P-box and S-box tables initialized with digits of Pi.
|
||||
*/
|
||||
BF_ctx ctx = {
|
||||
{
|
||||
{
|
||||
0xd1310ba6, 0x98dfb5ac, 0x2ffd72db, 0xd01adfb7,
|
||||
0xb8e1afed, 0x6a267e96, 0xba7c9045, 0xf12c7f99,
|
||||
0x24a19947, 0xb3916cf7, 0x0801f2e2, 0x858efc16,
|
||||
0x636920d8, 0x71574e69, 0xa458fea3, 0xf4933d7e,
|
||||
0x0d95748f, 0x728eb658, 0x718bcd58, 0x82154aee,
|
||||
0x7b54a41d, 0xc25a59b5, 0x9c30d539, 0x2af26013,
|
||||
0xc5d1b023, 0x286085f0, 0xca417918, 0xb8db38ef,
|
||||
0x8e79dcb0, 0x603a180e, 0x6c9e0e8b, 0xb01e8a3e,
|
||||
0xd71577c1, 0xbd314b27, 0x78af2fda, 0x55605c60,
|
||||
0xe65525f3, 0xaa55ab94, 0x57489862, 0x63e81440,
|
||||
0x55ca396a, 0x2aab10b6, 0xb4cc5c34, 0x1141e8ce,
|
||||
0xa15486af, 0x7c72e993, 0xb3ee1411, 0x636fbc2a,
|
||||
0x2ba9c55d, 0x741831f6, 0xce5c3e16, 0x9b87931e,
|
||||
0xafd6ba33, 0x6c24cf5c, 0x7a325381, 0x28958677,
|
||||
0x3b8f4898, 0x6b4bb9af, 0xc4bfe81b, 0x66282193,
|
||||
0x61d809cc, 0xfb21a991, 0x487cac60, 0x5dec8032,
|
||||
0xef845d5d, 0xe98575b1, 0xdc262302, 0xeb651b88,
|
||||
0x23893e81, 0xd396acc5, 0x0f6d6ff3, 0x83f44239,
|
||||
0x2e0b4482, 0xa4842004, 0x69c8f04a, 0x9e1f9b5e,
|
||||
0x21c66842, 0xf6e96c9a, 0x670c9c61, 0xabd388f0,
|
||||
0x6a51a0d2, 0xd8542f68, 0x960fa728, 0xab5133a3,
|
||||
0x6eef0b6c, 0x137a3be4, 0xba3bf050, 0x7efb2a98,
|
||||
0xa1f1651d, 0x39af0176, 0x66ca593e, 0x82430e88,
|
||||
0x8cee8619, 0x456f9fb4, 0x7d84a5c3, 0x3b8b5ebe,
|
||||
0xe06f75d8, 0x85c12073, 0x401a449f, 0x56c16aa6,
|
||||
0x4ed3aa62, 0x363f7706, 0x1bfedf72, 0x429b023d,
|
||||
0x37d0d724, 0xd00a1248, 0xdb0fead3, 0x49f1c09b,
|
||||
0x075372c9, 0x80991b7b, 0x25d479d8, 0xf6e8def7,
|
||||
0xe3fe501a, 0xb6794c3b, 0x976ce0bd, 0x04c006ba,
|
||||
0xc1a94fb6, 0x409f60c4, 0x5e5c9ec2, 0x196a2463,
|
||||
0x68fb6faf, 0x3e6c53b5, 0x1339b2eb, 0x3b52ec6f,
|
||||
0x6dfc511f, 0x9b30952c, 0xcc814544, 0xaf5ebd09,
|
||||
0xbee3d004, 0xde334afd, 0x660f2807, 0x192e4bb3,
|
||||
0xc0cba857, 0x45c8740f, 0xd20b5f39, 0xb9d3fbdb,
|
||||
0x5579c0bd, 0x1a60320a, 0xd6a100c6, 0x402c7279,
|
||||
0x679f25fe, 0xfb1fa3cc, 0x8ea5e9f8, 0xdb3222f8,
|
||||
0x3c7516df, 0xfd616b15, 0x2f501ec8, 0xad0552ab,
|
||||
0x323db5fa, 0xfd238760, 0x53317b48, 0x3e00df82,
|
||||
0x9e5c57bb, 0xca6f8ca0, 0x1a87562e, 0xdf1769db,
|
||||
0xd542a8f6, 0x287effc3, 0xac6732c6, 0x8c4f5573,
|
||||
0x695b27b0, 0xbbca58c8, 0xe1ffa35d, 0xb8f011a0,
|
||||
0x10fa3d98, 0xfd2183b8, 0x4afcb56c, 0x2dd1d35b,
|
||||
0x9a53e479, 0xb6f84565, 0xd28e49bc, 0x4bfb9790,
|
||||
0xe1ddf2da, 0xa4cb7e33, 0x62fb1341, 0xcee4c6e8,
|
||||
0xef20cada, 0x36774c01, 0xd07e9efe, 0x2bf11fb4,
|
||||
0x95dbda4d, 0xae909198, 0xeaad8e71, 0x6b93d5a0,
|
||||
0xd08ed1d0, 0xafc725e0, 0x8e3c5b2f, 0x8e7594b7,
|
||||
0x8ff6e2fb, 0xf2122b64, 0x8888b812, 0x900df01c,
|
||||
0x4fad5ea0, 0x688fc31c, 0xd1cff191, 0xb3a8c1ad,
|
||||
0x2f2f2218, 0xbe0e1777, 0xea752dfe, 0x8b021fa1,
|
||||
0xe5a0cc0f, 0xb56f74e8, 0x18acf3d6, 0xce89e299,
|
||||
0xb4a84fe0, 0xfd13e0b7, 0x7cc43b81, 0xd2ada8d9,
|
||||
0x165fa266, 0x80957705, 0x93cc7314, 0x211a1477,
|
||||
0xe6ad2065, 0x77b5fa86, 0xc75442f5, 0xfb9d35cf,
|
||||
0xebcdaf0c, 0x7b3e89a0, 0xd6411bd3, 0xae1e7e49,
|
||||
0x00250e2d, 0x2071b35e, 0x226800bb, 0x57b8e0af,
|
||||
0x2464369b, 0xf009b91e, 0x5563911d, 0x59dfa6aa,
|
||||
0x78c14389, 0xd95a537f, 0x207d5ba2, 0x02e5b9c5,
|
||||
0x83260376, 0x6295cfa9, 0x11c81968, 0x4e734a41,
|
||||
0xb3472dca, 0x7b14a94a, 0x1b510052, 0x9a532915,
|
||||
0xd60f573f, 0xbc9bc6e4, 0x2b60a476, 0x81e67400,
|
||||
0x08ba6fb5, 0x571be91f, 0xf296ec6b, 0x2a0dd915,
|
||||
0xb6636521, 0xe7b9f9b6, 0xff34052e, 0xc5855664,
|
||||
0x53b02d5d, 0xa99f8fa1, 0x08ba4799, 0x6e85076a
|
||||
}, {
|
||||
0x4b7a70e9, 0xb5b32944, 0xdb75092e, 0xc4192623,
|
||||
0xad6ea6b0, 0x49a7df7d, 0x9cee60b8, 0x8fedb266,
|
||||
0xecaa8c71, 0x699a17ff, 0x5664526c, 0xc2b19ee1,
|
||||
0x193602a5, 0x75094c29, 0xa0591340, 0xe4183a3e,
|
||||
0x3f54989a, 0x5b429d65, 0x6b8fe4d6, 0x99f73fd6,
|
||||
0xa1d29c07, 0xefe830f5, 0x4d2d38e6, 0xf0255dc1,
|
||||
0x4cdd2086, 0x8470eb26, 0x6382e9c6, 0x021ecc5e,
|
||||
0x09686b3f, 0x3ebaefc9, 0x3c971814, 0x6b6a70a1,
|
||||
0x687f3584, 0x52a0e286, 0xb79c5305, 0xaa500737,
|
||||
0x3e07841c, 0x7fdeae5c, 0x8e7d44ec, 0x5716f2b8,
|
||||
0xb03ada37, 0xf0500c0d, 0xf01c1f04, 0x0200b3ff,
|
||||
0xae0cf51a, 0x3cb574b2, 0x25837a58, 0xdc0921bd,
|
||||
0xd19113f9, 0x7ca92ff6, 0x94324773, 0x22f54701,
|
||||
0x3ae5e581, 0x37c2dadc, 0xc8b57634, 0x9af3dda7,
|
||||
0xa9446146, 0x0fd0030e, 0xecc8c73e, 0xa4751e41,
|
||||
0xe238cd99, 0x3bea0e2f, 0x3280bba1, 0x183eb331,
|
||||
0x4e548b38, 0x4f6db908, 0x6f420d03, 0xf60a04bf,
|
||||
0x2cb81290, 0x24977c79, 0x5679b072, 0xbcaf89af,
|
||||
0xde9a771f, 0xd9930810, 0xb38bae12, 0xdccf3f2e,
|
||||
0x5512721f, 0x2e6b7124, 0x501adde6, 0x9f84cd87,
|
||||
0x7a584718, 0x7408da17, 0xbc9f9abc, 0xe94b7d8c,
|
||||
0xec7aec3a, 0xdb851dfa, 0x63094366, 0xc464c3d2,
|
||||
0xef1c1847, 0x3215d908, 0xdd433b37, 0x24c2ba16,
|
||||
0x12a14d43, 0x2a65c451, 0x50940002, 0x133ae4dd,
|
||||
0x71dff89e, 0x10314e55, 0x81ac77d6, 0x5f11199b,
|
||||
0x043556f1, 0xd7a3c76b, 0x3c11183b, 0x5924a509,
|
||||
0xf28fe6ed, 0x97f1fbfa, 0x9ebabf2c, 0x1e153c6e,
|
||||
0x86e34570, 0xeae96fb1, 0x860e5e0a, 0x5a3e2ab3,
|
||||
0x771fe71c, 0x4e3d06fa, 0x2965dcb9, 0x99e71d0f,
|
||||
0x803e89d6, 0x5266c825, 0x2e4cc978, 0x9c10b36a,
|
||||
0xc6150eba, 0x94e2ea78, 0xa5fc3c53, 0x1e0a2df4,
|
||||
0xf2f74ea7, 0x361d2b3d, 0x1939260f, 0x19c27960,
|
||||
0x5223a708, 0xf71312b6, 0xebadfe6e, 0xeac31f66,
|
||||
0xe3bc4595, 0xa67bc883, 0xb17f37d1, 0x018cff28,
|
||||
0xc332ddef, 0xbe6c5aa5, 0x65582185, 0x68ab9802,
|
||||
0xeecea50f, 0xdb2f953b, 0x2aef7dad, 0x5b6e2f84,
|
||||
0x1521b628, 0x29076170, 0xecdd4775, 0x619f1510,
|
||||
0x13cca830, 0xeb61bd96, 0x0334fe1e, 0xaa0363cf,
|
||||
0xb5735c90, 0x4c70a239, 0xd59e9e0b, 0xcbaade14,
|
||||
0xeecc86bc, 0x60622ca7, 0x9cab5cab, 0xb2f3846e,
|
||||
0x648b1eaf, 0x19bdf0ca, 0xa02369b9, 0x655abb50,
|
||||
0x40685a32, 0x3c2ab4b3, 0x319ee9d5, 0xc021b8f7,
|
||||
0x9b540b19, 0x875fa099, 0x95f7997e, 0x623d7da8,
|
||||
0xf837889a, 0x97e32d77, 0x11ed935f, 0x16681281,
|
||||
0x0e358829, 0xc7e61fd6, 0x96dedfa1, 0x7858ba99,
|
||||
0x57f584a5, 0x1b227263, 0x9b83c3ff, 0x1ac24696,
|
||||
0xcdb30aeb, 0x532e3054, 0x8fd948e4, 0x6dbc3128,
|
||||
0x58ebf2ef, 0x34c6ffea, 0xfe28ed61, 0xee7c3c73,
|
||||
0x5d4a14d9, 0xe864b7e3, 0x42105d14, 0x203e13e0,
|
||||
0x45eee2b6, 0xa3aaabea, 0xdb6c4f15, 0xfacb4fd0,
|
||||
0xc742f442, 0xef6abbb5, 0x654f3b1d, 0x41cd2105,
|
||||
0xd81e799e, 0x86854dc7, 0xe44b476a, 0x3d816250,
|
||||
0xcf62a1f2, 0x5b8d2646, 0xfc8883a0, 0xc1c7b6a3,
|
||||
0x7f1524c3, 0x69cb7492, 0x47848a0b, 0x5692b285,
|
||||
0x095bbf00, 0xad19489d, 0x1462b174, 0x23820e00,
|
||||
0x58428d2a, 0x0c55f5ea, 0x1dadf43e, 0x233f7061,
|
||||
0x3372f092, 0x8d937e41, 0xd65fecf1, 0x6c223bdb,
|
||||
0x7cde3759, 0xcbee7460, 0x4085f2a7, 0xce77326e,
|
||||
0xa6078084, 0x19f8509e, 0xe8efd855, 0x61d99735,
|
||||
0xa969a7aa, 0xc50c06c2, 0x5a04abfc, 0x800bcadc,
|
||||
0x9e447a2e, 0xc3453484, 0xfdd56705, 0x0e1e9ec9,
|
||||
0xdb73dbd3, 0x105588cd, 0x675fda79, 0xe3674340,
|
||||
0xc5c43465, 0x713e38d8, 0x3d28f89e, 0xf16dff20,
|
||||
0x153e21e7, 0x8fb03d4a, 0xe6e39f2b, 0xdb83adf7
|
||||
}, {
|
||||
0xe93d5a68, 0x948140f7, 0xf64c261c, 0x94692934,
|
||||
0x411520f7, 0x7602d4f7, 0xbcf46b2e, 0xd4a20068,
|
||||
0xd4082471, 0x3320f46a, 0x43b7d4b7, 0x500061af,
|
||||
0x1e39f62e, 0x97244546, 0x14214f74, 0xbf8b8840,
|
||||
0x4d95fc1d, 0x96b591af, 0x70f4ddd3, 0x66a02f45,
|
||||
0xbfbc09ec, 0x03bd9785, 0x7fac6dd0, 0x31cb8504,
|
||||
0x96eb27b3, 0x55fd3941, 0xda2547e6, 0xabca0a9a,
|
||||
0x28507825, 0x530429f4, 0x0a2c86da, 0xe9b66dfb,
|
||||
0x68dc1462, 0xd7486900, 0x680ec0a4, 0x27a18dee,
|
||||
0x4f3ffea2, 0xe887ad8c, 0xb58ce006, 0x7af4d6b6,
|
||||
0xaace1e7c, 0xd3375fec, 0xce78a399, 0x406b2a42,
|
||||
0x20fe9e35, 0xd9f385b9, 0xee39d7ab, 0x3b124e8b,
|
||||
0x1dc9faf7, 0x4b6d1856, 0x26a36631, 0xeae397b2,
|
||||
0x3a6efa74, 0xdd5b4332, 0x6841e7f7, 0xca7820fb,
|
||||
0xfb0af54e, 0xd8feb397, 0x454056ac, 0xba489527,
|
||||
0x55533a3a, 0x20838d87, 0xfe6ba9b7, 0xd096954b,
|
||||
0x55a867bc, 0xa1159a58, 0xcca92963, 0x99e1db33,
|
||||
0xa62a4a56, 0x3f3125f9, 0x5ef47e1c, 0x9029317c,
|
||||
0xfdf8e802, 0x04272f70, 0x80bb155c, 0x05282ce3,
|
||||
0x95c11548, 0xe4c66d22, 0x48c1133f, 0xc70f86dc,
|
||||
0x07f9c9ee, 0x41041f0f, 0x404779a4, 0x5d886e17,
|
||||
0x325f51eb, 0xd59bc0d1, 0xf2bcc18f, 0x41113564,
|
||||
0x257b7834, 0x602a9c60, 0xdff8e8a3, 0x1f636c1b,
|
||||
0x0e12b4c2, 0x02e1329e, 0xaf664fd1, 0xcad18115,
|
||||
0x6b2395e0, 0x333e92e1, 0x3b240b62, 0xeebeb922,
|
||||
0x85b2a20e, 0xe6ba0d99, 0xde720c8c, 0x2da2f728,
|
||||
0xd0127845, 0x95b794fd, 0x647d0862, 0xe7ccf5f0,
|
||||
0x5449a36f, 0x877d48fa, 0xc39dfd27, 0xf33e8d1e,
|
||||
0x0a476341, 0x992eff74, 0x3a6f6eab, 0xf4f8fd37,
|
||||
0xa812dc60, 0xa1ebddf8, 0x991be14c, 0xdb6e6b0d,
|
||||
0xc67b5510, 0x6d672c37, 0x2765d43b, 0xdcd0e804,
|
||||
0xf1290dc7, 0xcc00ffa3, 0xb5390f92, 0x690fed0b,
|
||||
0x667b9ffb, 0xcedb7d9c, 0xa091cf0b, 0xd9155ea3,
|
||||
0xbb132f88, 0x515bad24, 0x7b9479bf, 0x763bd6eb,
|
||||
0x37392eb3, 0xcc115979, 0x8026e297, 0xf42e312d,
|
||||
0x6842ada7, 0xc66a2b3b, 0x12754ccc, 0x782ef11c,
|
||||
0x6a124237, 0xb79251e7, 0x06a1bbe6, 0x4bfb6350,
|
||||
0x1a6b1018, 0x11caedfa, 0x3d25bdd8, 0xe2e1c3c9,
|
||||
0x44421659, 0x0a121386, 0xd90cec6e, 0xd5abea2a,
|
||||
0x64af674e, 0xda86a85f, 0xbebfe988, 0x64e4c3fe,
|
||||
0x9dbc8057, 0xf0f7c086, 0x60787bf8, 0x6003604d,
|
||||
0xd1fd8346, 0xf6381fb0, 0x7745ae04, 0xd736fccc,
|
||||
0x83426b33, 0xf01eab71, 0xb0804187, 0x3c005e5f,
|
||||
0x77a057be, 0xbde8ae24, 0x55464299, 0xbf582e61,
|
||||
0x4e58f48f, 0xf2ddfda2, 0xf474ef38, 0x8789bdc2,
|
||||
0x5366f9c3, 0xc8b38e74, 0xb475f255, 0x46fcd9b9,
|
||||
0x7aeb2661, 0x8b1ddf84, 0x846a0e79, 0x915f95e2,
|
||||
0x466e598e, 0x20b45770, 0x8cd55591, 0xc902de4c,
|
||||
0xb90bace1, 0xbb8205d0, 0x11a86248, 0x7574a99e,
|
||||
0xb77f19b6, 0xe0a9dc09, 0x662d09a1, 0xc4324633,
|
||||
0xe85a1f02, 0x09f0be8c, 0x4a99a025, 0x1d6efe10,
|
||||
0x1ab93d1d, 0x0ba5a4df, 0xa186f20f, 0x2868f169,
|
||||
0xdcb7da83, 0x573906fe, 0xa1e2ce9b, 0x4fcd7f52,
|
||||
0x50115e01, 0xa70683fa, 0xa002b5c4, 0x0de6d027,
|
||||
0x9af88c27, 0x773f8641, 0xc3604c06, 0x61a806b5,
|
||||
0xf0177a28, 0xc0f586e0, 0x006058aa, 0x30dc7d62,
|
||||
0x11e69ed7, 0x2338ea63, 0x53c2dd94, 0xc2c21634,
|
||||
0xbbcbee56, 0x90bcb6de, 0xebfc7da1, 0xce591d76,
|
||||
0x6f05e409, 0x4b7c0188, 0x39720a3d, 0x7c927c24,
|
||||
0x86e3725f, 0x724d9db9, 0x1ac15bb4, 0xd39eb8fc,
|
||||
0xed545578, 0x08fca5b5, 0xd83d7cd3, 0x4dad0fc4,
|
||||
0x1e50ef5e, 0xb161e6f8, 0xa28514d9, 0x6c51133c,
|
||||
0x6fd5c7e7, 0x56e14ec4, 0x362abfce, 0xddc6c837,
|
||||
0xd79a3234, 0x92638212, 0x670efa8e, 0x406000e0
|
||||
}, {
|
||||
0x3a39ce37, 0xd3faf5cf, 0xabc27737, 0x5ac52d1b,
|
||||
0x5cb0679e, 0x4fa33742, 0xd3822740, 0x99bc9bbe,
|
||||
0xd5118e9d, 0xbf0f7315, 0xd62d1c7e, 0xc700c47b,
|
||||
0xb78c1b6b, 0x21a19045, 0xb26eb1be, 0x6a366eb4,
|
||||
0x5748ab2f, 0xbc946e79, 0xc6a376d2, 0x6549c2c8,
|
||||
0x530ff8ee, 0x468dde7d, 0xd5730a1d, 0x4cd04dc6,
|
||||
0x2939bbdb, 0xa9ba4650, 0xac9526e8, 0xbe5ee304,
|
||||
0xa1fad5f0, 0x6a2d519a, 0x63ef8ce2, 0x9a86ee22,
|
||||
0xc089c2b8, 0x43242ef6, 0xa51e03aa, 0x9cf2d0a4,
|
||||
0x83c061ba, 0x9be96a4d, 0x8fe51550, 0xba645bd6,
|
||||
0x2826a2f9, 0xa73a3ae1, 0x4ba99586, 0xef5562e9,
|
||||
0xc72fefd3, 0xf752f7da, 0x3f046f69, 0x77fa0a59,
|
||||
0x80e4a915, 0x87b08601, 0x9b09e6ad, 0x3b3ee593,
|
||||
0xe990fd5a, 0x9e34d797, 0x2cf0b7d9, 0x022b8b51,
|
||||
0x96d5ac3a, 0x017da67d, 0xd1cf3ed6, 0x7c7d2d28,
|
||||
0x1f9f25cf, 0xadf2b89b, 0x5ad6b472, 0x5a88f54c,
|
||||
0xe029ac71, 0xe019a5e6, 0x47b0acfd, 0xed93fa9b,
|
||||
0xe8d3c48d, 0x283b57cc, 0xf8d56629, 0x79132e28,
|
||||
0x785f0191, 0xed756055, 0xf7960e44, 0xe3d35e8c,
|
||||
0x15056dd4, 0x88f46dba, 0x03a16125, 0x0564f0bd,
|
||||
0xc3eb9e15, 0x3c9057a2, 0x97271aec, 0xa93a072a,
|
||||
0x1b3f6d9b, 0x1e6321f5, 0xf59c66fb, 0x26dcf319,
|
||||
0x7533d928, 0xb155fdf5, 0x03563482, 0x8aba3cbb,
|
||||
0x28517711, 0xc20ad9f8, 0xabcc5167, 0xccad925f,
|
||||
0x4de81751, 0x3830dc8e, 0x379d5862, 0x9320f991,
|
||||
0xea7a90c2, 0xfb3e7bce, 0x5121ce64, 0x774fbe32,
|
||||
0xa8b6e37e, 0xc3293d46, 0x48de5369, 0x6413e680,
|
||||
0xa2ae0810, 0xdd6db224, 0x69852dfd, 0x09072166,
|
||||
0xb39a460a, 0x6445c0dd, 0x586cdecf, 0x1c20c8ae,
|
||||
0x5bbef7dd, 0x1b588d40, 0xccd2017f, 0x6bb4e3bb,
|
||||
0xdda26a7e, 0x3a59ff45, 0x3e350a44, 0xbcb4cdd5,
|
||||
0x72eacea8, 0xfa6484bb, 0x8d6612ae, 0xbf3c6f47,
|
||||
0xd29be463, 0x542f5d9e, 0xaec2771b, 0xf64e6370,
|
||||
0x740e0d8d, 0xe75b1357, 0xf8721671, 0xaf537d5d,
|
||||
0x4040cb08, 0x4eb4e2cc, 0x34d2466a, 0x0115af84,
|
||||
0xe1b00428, 0x95983a1d, 0x06b89fb4, 0xce6ea048,
|
||||
0x6f3f3b82, 0x3520ab82, 0x011a1d4b, 0x277227f8,
|
||||
0x611560b1, 0xe7933fdc, 0xbb3a792b, 0x344525bd,
|
||||
0xa08839e1, 0x51ce794b, 0x2f32c9b7, 0xa01fbac9,
|
||||
0xe01cc87e, 0xbcc7d1f6, 0xcf0111c3, 0xa1e8aac7,
|
||||
0x1a908749, 0xd44fbd9a, 0xd0dadecb, 0xd50ada38,
|
||||
0x0339c32a, 0xc6913667, 0x8df9317c, 0xe0b12b4f,
|
||||
0xf79e59b7, 0x43f5bb3a, 0xf2d519ff, 0x27d9459c,
|
||||
0xbf97222c, 0x15e6fc2a, 0x0f91fc71, 0x9b941525,
|
||||
0xfae59361, 0xceb69ceb, 0xc2a86459, 0x12baa8d1,
|
||||
0xb6c1075e, 0xe3056a0c, 0x10d25065, 0xcb03a442,
|
||||
0xe0ec6e0e, 0x1698db3b, 0x4c98a0be, 0x3278e964,
|
||||
0x9f1f9532, 0xe0d392df, 0xd3a0342b, 0x8971f21e,
|
||||
0x1b0a7441, 0x4ba3348c, 0xc5be7120, 0xc37632d8,
|
||||
0xdf359f8d, 0x9b992f2e, 0xe60b6f47, 0x0fe3f11d,
|
||||
0xe54cda54, 0x1edad891, 0xce6279cf, 0xcd3e7e6f,
|
||||
0x1618b166, 0xfd2c1d05, 0x848fd2c5, 0xf6fb2299,
|
||||
0xf523f357, 0xa6327623, 0x93a83531, 0x56cccd02,
|
||||
0xacf08162, 0x5a75ebb5, 0x6e163697, 0x88d273cc,
|
||||
0xde966292, 0x81b949d0, 0x4c50901b, 0x71c65614,
|
||||
0xe6c6c7bd, 0x327a140a, 0x45e1d006, 0xc3f27b9a,
|
||||
0xc9aa53fd, 0x62a80f00, 0xbb25bfe2, 0x35bdd2f6,
|
||||
0x71126905, 0xb2040222, 0xb6cbcf7c, 0xcd769c2b,
|
||||
0x53113ec0, 0x1640e3d3, 0x38abbd60, 0x2547adf0,
|
||||
0xba38209c, 0xf746ce76, 0x77afa1c5, 0x20756060,
|
||||
0x85cbfe4e, 0x8ae88dd8, 0x7aaaf9b0, 0x4cf9aa7e,
|
||||
0x1948c25c, 0x02fb8a8c, 0x01c36ae4, 0xd6ebe1f9,
|
||||
0x90d4f869, 0xa65cdea0, 0x3f09252d, 0xc208e69f,
|
||||
0xb74e6132, 0xce77e25b, 0x578fdfe3, 0x3ac372e6
|
||||
}
|
||||
}, {
|
||||
0x243f6a88, 0x85a308d3, 0x13198a2e, 0x03707344,
|
||||
0xa4093822, 0x299f31d0, 0x082efa98, 0xec4e6c89,
|
||||
0x452821e6, 0x38d01377, 0xbe5466cf, 0x34e90c6c,
|
||||
0xc0ac29b7, 0xc97c50dd, 0x3f84d5b5, 0xb5470917,
|
||||
0x9216d5d9, 0x8979fb1b
|
||||
}
|
||||
};
|
||||
|
||||
static unsigned char BF_itoa64[64 + 1] =
|
||||
"./ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789";
|
||||
|
||||
static unsigned char BF_atoi64[0x60] = {
|
||||
64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 0, 1,
|
||||
54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64, 64, 64, 64, 64, 64,
|
||||
64, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16,
|
||||
17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 64, 64, 64, 64, 64,
|
||||
64, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42,
|
||||
43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 64, 64, 64, 64, 64
|
||||
};
|
||||
|
||||
union {
|
||||
BF_word LR[2];
|
||||
uint64_t LR64;
|
||||
} block;
|
||||
|
||||
// EM_JS(void, print_memory, (uint32_t offset, uint32_t len), {
|
||||
// console.log(x);
|
||||
// });
|
||||
|
||||
#define BF_safe_atoi64(dst, src) \
|
||||
{ \
|
||||
tmp = (unsigned char)(src); \
|
||||
if ((unsigned int)(tmp -= 0x20) >= 0x60) return -1; \
|
||||
tmp = BF_atoi64[tmp]; \
|
||||
if (tmp > 63) return -1; \
|
||||
(dst) = tmp; \
|
||||
}
|
||||
|
||||
static int BF_decode(BF_word *dst, const char *src, int size)
|
||||
{
|
||||
unsigned char *dptr = (unsigned char *)dst;
|
||||
unsigned char *end = dptr + size;
|
||||
const unsigned char *sptr = (const unsigned char *)src;
|
||||
unsigned int tmp, c1, c2, c3, c4;
|
||||
|
||||
do {
|
||||
BF_safe_atoi64(c1, *sptr++);
|
||||
BF_safe_atoi64(c2, *sptr++);
|
||||
*dptr++ = (c1 << 2) | ((c2 & 0x30) >> 4);
|
||||
if (dptr >= end) break;
|
||||
|
||||
BF_safe_atoi64(c3, *sptr++);
|
||||
*dptr++ = ((c2 & 0x0F) << 4) | ((c3 & 0x3C) >> 2);
|
||||
if (dptr >= end) break;
|
||||
|
||||
BF_safe_atoi64(c4, *sptr++);
|
||||
*dptr++ = ((c3 & 0x03) << 6) | c4;
|
||||
} while (dptr < end);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void BF_encode(char *dst, const BF_word *src, int size)
|
||||
{
|
||||
const unsigned char *sptr = (const unsigned char *)src;
|
||||
const unsigned char *end = sptr + size;
|
||||
unsigned char *dptr = (unsigned char *)dst;
|
||||
unsigned int c1, c2;
|
||||
|
||||
do {
|
||||
c1 = *sptr++;
|
||||
*dptr++ = BF_itoa64[c1 >> 2];
|
||||
c1 = (c1 & 0x03) << 4;
|
||||
if (sptr >= end) {
|
||||
*dptr++ = BF_itoa64[c1];
|
||||
break;
|
||||
}
|
||||
|
||||
c2 = *sptr++;
|
||||
c1 |= c2 >> 4;
|
||||
*dptr++ = BF_itoa64[c1];
|
||||
c1 = (c2 & 0x0f) << 2;
|
||||
if (sptr >= end) {
|
||||
*dptr++ = BF_itoa64[c1];
|
||||
break;
|
||||
}
|
||||
|
||||
c2 = *sptr++;
|
||||
c1 |= c2 >> 6;
|
||||
*dptr++ = BF_itoa64[c1];
|
||||
*dptr++ = BF_itoa64[c2 & 0x3f];
|
||||
} while (sptr < end);
|
||||
}
|
||||
|
||||
static void BF_swap(BF_word *x, int count)
|
||||
{
|
||||
BF_word tmp;
|
||||
|
||||
do {
|
||||
tmp = *x;
|
||||
tmp = (tmp << 16) | (tmp >> 16);
|
||||
*x++ = ((tmp & 0x00FF00FF) << 8) | ((tmp >> 8) & 0x00FF00FF);
|
||||
} while (--count);
|
||||
}
|
||||
|
||||
#define BF_ROUND(L, R, N) \
|
||||
tmp1 = ctx.S[3][L & 0xFF]; \
|
||||
tmp2 = ctx.S[2][(L >> 8) & 0xFF]; \
|
||||
tmp3 = ctx.S[1][(L >> 16) & 0xFF]; \
|
||||
tmp3 += ctx.S[0][L >> 24]; \
|
||||
tmp3 ^= tmp2; \
|
||||
R ^= ctx.P[N + 1]; \
|
||||
tmp3 += tmp1; \
|
||||
R ^= tmp3;
|
||||
|
||||
/*
|
||||
* Encrypt one block, BF_N is hardcoded here.
|
||||
*/
|
||||
#define BF_ENCRYPT \
|
||||
block.LR[0] ^= ctx.P[0]; \
|
||||
BF_ROUND(block.LR[0], block.LR[1], 0); \
|
||||
BF_ROUND(block.LR[1], block.LR[0], 1); \
|
||||
BF_ROUND(block.LR[0], block.LR[1], 2); \
|
||||
BF_ROUND(block.LR[1], block.LR[0], 3); \
|
||||
BF_ROUND(block.LR[0], block.LR[1], 4); \
|
||||
BF_ROUND(block.LR[1], block.LR[0], 5); \
|
||||
BF_ROUND(block.LR[0], block.LR[1], 6); \
|
||||
BF_ROUND(block.LR[1], block.LR[0], 7); \
|
||||
BF_ROUND(block.LR[0], block.LR[1], 8); \
|
||||
BF_ROUND(block.LR[1], block.LR[0], 9); \
|
||||
BF_ROUND(block.LR[0], block.LR[1], 10); \
|
||||
BF_ROUND(block.LR[1], block.LR[0], 11); \
|
||||
BF_ROUND(block.LR[0], block.LR[1], 12); \
|
||||
BF_ROUND(block.LR[1], block.LR[0], 13); \
|
||||
BF_ROUND(block.LR[0], block.LR[1], 14); \
|
||||
BF_ROUND(block.LR[1], block.LR[0], 15); \
|
||||
tmp4 = block.LR[1]; \
|
||||
block.LR[1] = block.LR[0]; \
|
||||
block.LR[0] = tmp4 ^ ctx.P[BF_N + 1];
|
||||
|
||||
#define BF_body() \
|
||||
block.LR64 = 0; \
|
||||
ptr = ctx.P; \
|
||||
do { \
|
||||
ptr += 2; \
|
||||
BF_ENCRYPT; \
|
||||
*(ptr - 2) = block.LR[0]; \
|
||||
*(ptr - 1) = block.LR[1]; \
|
||||
} while (ptr < &ctx.P[BF_N + 2]); \
|
||||
\
|
||||
ptr = ctx.S[0]; \
|
||||
do { \
|
||||
ptr += 2; \
|
||||
BF_ENCRYPT; \
|
||||
*(ptr - 2) = block.LR[0]; \
|
||||
*(ptr - 1) = block.LR[1]; \
|
||||
} while (ptr < &ctx.S[3][0xFF]);
|
||||
|
||||
static void BF_set_key(const char *key, BF_key expanded, BF_key initial,
|
||||
unsigned char flags)
|
||||
{
|
||||
const char *ptr = key;
|
||||
unsigned int bug, i, j;
|
||||
BF_word safety, sign, diff, tmp[2];
|
||||
|
||||
/*
|
||||
* There was a sign extension bug in older revisions of this function. While
|
||||
* we would have liked to simply fix the bug and move on, we have to provide
|
||||
* a backwards compatibility feature (essentially the bug) for some systems and
|
||||
* a safety measure for some others. The latter is needed because for certain
|
||||
* multiple inputs to the buggy algorithm there exist easily found inputs to
|
||||
* the correct algorithm that produce the same hash. Thus, we optionally
|
||||
* deviate from the correct algorithm just enough to avoid such collisions.
|
||||
* While the bug itself affected the majority of passwords containing
|
||||
* characters with the 8th bit set (although only a percentage of those in a
|
||||
* collision-producing way), the anti-collision safety measure affects
|
||||
* only a subset of passwords containing the '\xff' character (not even all of
|
||||
* those passwords, just some of them). This character is not found in valid
|
||||
* UTF-8 sequences and is rarely used in popular 8-bit character encodings.
|
||||
* Thus, the safety measure is unlikely to cause much annoyance, and is a
|
||||
* reasonable tradeoff to use when authenticating against existing hashes that
|
||||
* are not reliably known to have been computed with the correct algorithm.
|
||||
*
|
||||
* We use an approach that tries to minimize side-channel leaks of password
|
||||
* information - that is, we mostly use fixed-cost bitwise operations instead
|
||||
* of branches or table lookups. (One conditional branch based on password
|
||||
* length remains. It is not part of the bug aftermath, though, and is
|
||||
* difficult and possibly unreasonable to avoid given the use of C strings by
|
||||
* the caller, which results in similar timing leaks anyway.)
|
||||
*
|
||||
* For actual implementation, we set an array index in the variable "bug"
|
||||
* (0 means no bug, 1 means sign extension bug emulation) and a flag in the
|
||||
* variable "safety" (bit 16 is set when the safety measure is requested).
|
||||
* Valid combinations of settings are:
|
||||
*
|
||||
* Prefix "$2a$": bug = 0, safety = 0x10000
|
||||
* Prefix "$2b$": bug = 0, safety = 0
|
||||
* Prefix "$2x$": bug = 1, safety = 0
|
||||
* Prefix "$2y$": bug = 0, safety = 0
|
||||
*/
|
||||
bug = (unsigned int)flags & 1;
|
||||
safety = ((BF_word)flags & 2) << 15;
|
||||
|
||||
sign = diff = 0;
|
||||
|
||||
for (i = 0; i < BF_N + 2; i++) {
|
||||
tmp[0] = tmp[1] = 0;
|
||||
for (j = 0; j < 4; j++) {
|
||||
tmp[0] <<= 8;
|
||||
tmp[0] |= (unsigned char)*ptr; /* correct */
|
||||
tmp[1] <<= 8;
|
||||
tmp[1] |= (BF_word_signed)(signed char)*ptr; /* bug */
|
||||
/*
|
||||
* Sign extension in the first char has no effect - nothing to overwrite yet,
|
||||
* and those extra 24 bits will be fully shifted out of the 32-bit word. For
|
||||
* chars 2, 3, 4 in each four-char block, we set bit 7 of "sign" if sign
|
||||
* extension in tmp[1] occurs. Once this flag is set, it remains set.
|
||||
*/
|
||||
if (j)
|
||||
sign |= tmp[1] & 0x80;
|
||||
if (!*ptr)
|
||||
ptr = key;
|
||||
else
|
||||
ptr++;
|
||||
}
|
||||
diff |= tmp[0] ^ tmp[1]; /* Non-zero on any differences */
|
||||
|
||||
expanded[i] = tmp[bug];
|
||||
initial[i] = ctx.P[i] ^ tmp[bug];
|
||||
}
|
||||
|
||||
/*
|
||||
* At this point, "diff" is zero iff the correct and buggy algorithms produced
|
||||
* exactly the same result. If so and if "sign" is non-zero, which indicates
|
||||
* that there was a non-benign sign extension, this means that we have a
|
||||
* collision between the correctly computed hash for this password and a set of
|
||||
* passwords that could be supplied to the buggy algorithm. Our safety measure
|
||||
* is meant to protect from such many-buggy to one-correct collisions, by
|
||||
* deviating from the correct algorithm in such cases. Let's check for this.
|
||||
*/
|
||||
diff |= diff >> 16; /* still zero iff exact match */
|
||||
diff &= 0xffff; /* ditto */
|
||||
diff += 0xffff; /* bit 16 set iff "diff" was non-zero (on non-match) */
|
||||
sign <<= 9; /* move the non-benign sign extension flag to bit 16 */
|
||||
sign &= ~diff & safety; /* action needed? */
|
||||
|
||||
/*
|
||||
* If we have determined that we need to deviate from the correct algorithm,
|
||||
* flip bit 16 in initial expanded key. (The choice of 16 is arbitrary, but
|
||||
* let's stick to it now. It came out of the approach we used above, and it's
|
||||
* not any worse than any other choice we could make.)
|
||||
*
|
||||
* It is crucial that we don't do the same to the expanded key used in the main
|
||||
* Eksblowfish loop. By doing it to only one of these two, we deviate from a
|
||||
* state that could be directly specified by a password to the buggy algorithm
|
||||
* (and to the fully correct one as well, but that's a side-effect).
|
||||
*/
|
||||
initial[0] ^= sign;
|
||||
}
|
||||
|
||||
static const unsigned char flags_by_subtype[26] =
|
||||
{2, 4, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 4, 0};
|
||||
|
||||
static char *BF_crypt(const char *key, const char *setting,
|
||||
char *output, int size,
|
||||
BF_word min, int should_encode)
|
||||
{
|
||||
BF_key expanded_key;
|
||||
union {
|
||||
BF_word salt[4];
|
||||
BF_word output[6];
|
||||
} binary;
|
||||
BF_word tmp1, tmp2, tmp3, tmp4;
|
||||
BF_word *ptr;
|
||||
uint64_t *ptr64;
|
||||
BF_word count;
|
||||
int i;
|
||||
|
||||
count = (BF_word)1 << ((setting[4] - '0') * 10 + (setting[5] - '0'));
|
||||
if (count < min || BF_decode(binary.salt, &setting[7], 16)) {
|
||||
return NULL;
|
||||
}
|
||||
BF_swap(binary.salt, 4);
|
||||
|
||||
BF_set_key(key, expanded_key, ctx.P,
|
||||
flags_by_subtype[(unsigned int)(unsigned char)setting[2] - 'a']);
|
||||
|
||||
block.LR64 = 0;
|
||||
for (i = 0; i < BF_N + 2; i += 2) {
|
||||
block.LR64 ^= *(uint64_t*)&binary.salt[i & 2];
|
||||
BF_ENCRYPT;
|
||||
*(uint64_t*)&ctx.P[i] = block.LR64;
|
||||
}
|
||||
|
||||
ptr = ctx.S[0];
|
||||
do {
|
||||
ptr += 4;
|
||||
block.LR[0] ^= binary.salt[(BF_N + 2) & 3];
|
||||
block.LR[1] ^= binary.salt[(BF_N + 3) & 3];
|
||||
BF_ENCRYPT;
|
||||
*(ptr - 4) = block.LR[0];
|
||||
*(ptr - 3) = block.LR[1];
|
||||
|
||||
block.LR[0] ^= binary.salt[(BF_N + 4) & 3];
|
||||
block.LR[1] ^= binary.salt[(BF_N + 5) & 3];
|
||||
BF_ENCRYPT;
|
||||
*(ptr - 2) = block.LR[0];
|
||||
*(ptr - 1) = block.LR[1];
|
||||
} while (ptr < &ctx.S[3][0xFF]);
|
||||
|
||||
do {
|
||||
int done;
|
||||
|
||||
for (i = 0; i < BF_N + 2; i += 2) {
|
||||
ctx.P[i] ^= expanded_key[i];
|
||||
ctx.P[i + 1] ^= expanded_key[i + 1];
|
||||
}
|
||||
|
||||
done = 0;
|
||||
uint64_t tmp1x = ((uint64_t*)binary.salt)[0];
|
||||
uint64_t tmp3x = ((uint64_t*)binary.salt)[1];
|
||||
do {
|
||||
BF_body();
|
||||
if (done)
|
||||
break;
|
||||
done = 1;
|
||||
|
||||
for (i = 0; i < BF_N; i += 4) {
|
||||
*(uint64_t*)(&ctx.P[i]) ^= tmp1x;
|
||||
*(uint64_t*)(&ctx.P[i + 2]) ^= tmp3x;
|
||||
}
|
||||
*(uint64_t*)(&ctx.P[16]) ^= tmp1x;
|
||||
} while (1);
|
||||
} while (--count);
|
||||
|
||||
for (i = 0; i < 6; i += 2) {
|
||||
block.LR64 = *(uint64_t*)&BF_magic_w[i];
|
||||
|
||||
count = 64;
|
||||
do {
|
||||
BF_ENCRYPT;
|
||||
} while (--count);
|
||||
|
||||
*(uint64_t*)&(binary.output[i]) = block.LR64;
|
||||
}
|
||||
|
||||
// memcpy(output, setting, 7 + 22 - 1);
|
||||
for (uint8_t z = 0; z < 7; z++) {
|
||||
((uint32_t*)output)[z] = ((uint32_t*)setting)[z];
|
||||
}
|
||||
|
||||
output[28] = BF_itoa64[(int)
|
||||
BF_atoi64[(int)setting[28] - 0x20] & 0x30];
|
||||
|
||||
/* This has to be bug-compatible with the original implementation, so
|
||||
* only encode 23 of the 24 bytes. :-) */
|
||||
BF_swap(binary.output, 6);
|
||||
|
||||
if (should_encode) {
|
||||
BF_encode(&output[7 + 22], binary.output, 23);
|
||||
} else {
|
||||
uint8_t *source = (uint8_t*)binary.output;
|
||||
for (uint8_t z = 0; z < 3; z++) {
|
||||
((uint64_t*)output)[z] = ((uint64_t*)source)[z];
|
||||
}
|
||||
}
|
||||
output[7 + 22 + 31] = '\0';
|
||||
|
||||
return output;
|
||||
}
|
||||
|
||||
int _crypt_output_magic(const char *setting, char *output, int size)
|
||||
{
|
||||
if (size < 3)
|
||||
return -1;
|
||||
|
||||
output[0] = '*';
|
||||
output[1] = '0';
|
||||
output[2] = '\0';
|
||||
|
||||
if (setting[0] == '*' && setting[1] == '0')
|
||||
output[1] = '1';
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
char *_crypt_blowfish_rn(const char *key, const char *setting,
|
||||
char *output, int size, int should_encode)
|
||||
{
|
||||
_crypt_output_magic(setting, output, size);
|
||||
return BF_crypt(key, setting, output, size, 16, should_encode);
|
||||
}
|
||||
|
||||
char *_crypt_gensalt_blowfish_rn(const char *prefix, unsigned long count,
|
||||
const char *input, char *output)
|
||||
{
|
||||
output[0] = '$';
|
||||
output[1] = '2';
|
||||
output[2] = prefix[2];
|
||||
output[3] = '$';
|
||||
output[4] = '0' + count / 10;
|
||||
output[5] = '0' + count % 10;
|
||||
output[6] = '$';
|
||||
|
||||
BF_encode(&output[7], (const BF_word *)input, 16);
|
||||
output[7 + 22] = '\0';
|
||||
|
||||
return output;
|
||||
}
|
||||
|
||||
WASM_EXPORT
|
||||
void bcrypt(uint32_t password_length, uint32_t cost_factor, uint32_t should_encode) {
|
||||
uint8_t *salt = &main_buffer[0];
|
||||
uint8_t *key = &main_buffer[16];
|
||||
key[password_length] = 0;
|
||||
|
||||
uint8_t setting[30];
|
||||
_crypt_gensalt_blowfish_rn("$2a", cost_factor, (char*)salt, (char*)setting);
|
||||
|
||||
uint8_t output[60];
|
||||
_crypt_blowfish_rn((char*)key, (char*)setting, (char*)output, 60, should_encode);
|
||||
|
||||
for (uint8_t i = 0; i < 60; i++) {
|
||||
main_buffer[i] = output[i];
|
||||
}
|
||||
}
|
||||
|
||||
WASM_EXPORT
|
||||
uint32_t bcrypt_verify(uint32_t passwordLength) {
|
||||
uint8_t *hash = &main_buffer[0];
|
||||
uint8_t *key = &main_buffer[60];
|
||||
key[passwordLength] = 0;
|
||||
|
||||
uint8_t output[60];
|
||||
_crypt_blowfish_rn((char*)key, (char*)main_buffer, (char*)output, 60, 1);
|
||||
|
||||
// 0-28 => setting
|
||||
// 29-59 => hash
|
||||
|
||||
uint8_t res = 0;
|
||||
uint64_t *out64 = (uint64_t*)&output[28];
|
||||
uint64_t *hash64 = (uint64_t*)&hash[28];
|
||||
for (uint8_t i = 0; i < 4; i++) {
|
||||
res += out64[i] != hash64[i];
|
||||
}
|
||||
return res == 0;
|
||||
}
|
||||
+298
@@ -0,0 +1,298 @@
|
||||
/*
|
||||
BLAKE2 reference source code package - reference C implementations
|
||||
Copyright 2012, Samuel Neves <sneves@dei.uc.pt>. You may use this under the
|
||||
terms of the CC0, the OpenSSL Licence, or the Apache Public License 2.0, at
|
||||
your option. The terms of these licenses can be found at:
|
||||
- CC0 1.0 Universal : http://creativecommons.org/publicdomain/zero/1.0
|
||||
- OpenSSL license : https://www.openssl.org/source/license.html
|
||||
- Apache 2.0 : http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
More information about the BLAKE2 hash function can be found at
|
||||
https://blake2.net.
|
||||
|
||||
Modified for hash-wasm by Dani Biró
|
||||
*/
|
||||
|
||||
#define WITH_BUFFER
|
||||
#include "hash-wasm.h"
|
||||
|
||||
#define BLAKE2_PACKED(x) x __attribute__((packed))
|
||||
|
||||
enum blake2b_constant {
|
||||
BLAKE2B_BLOCKBYTES = 128,
|
||||
BLAKE2B_OUTBYTES = 64,
|
||||
BLAKE2B_KEYBYTES = 64,
|
||||
BLAKE2B_SALTBYTES = 16,
|
||||
BLAKE2B_PERSONALBYTES = 16
|
||||
};
|
||||
|
||||
typedef struct blake2b_state__ {
|
||||
uint64_t h[8];
|
||||
uint64_t t[2];
|
||||
uint64_t f[2];
|
||||
uint8_t buf[BLAKE2B_BLOCKBYTES];
|
||||
int buflen;
|
||||
int outlen;
|
||||
uint8_t last_node;
|
||||
} blake2b_state;
|
||||
|
||||
blake2b_state S[1];
|
||||
|
||||
BLAKE2_PACKED(struct blake2b_param__ {
|
||||
uint8_t digest_length; /* 1 */
|
||||
uint8_t key_length; /* 2 */
|
||||
uint8_t fanout; /* 3 */
|
||||
uint8_t depth; /* 4 */
|
||||
uint32_t leaf_length; /* 8 */
|
||||
uint32_t node_offset; /* 12 */
|
||||
uint32_t xof_length; /* 16 */
|
||||
uint8_t node_depth; /* 17 */
|
||||
uint8_t inner_length; /* 18 */
|
||||
uint8_t reserved[14]; /* 32 */
|
||||
uint8_t salt[BLAKE2B_SALTBYTES]; /* 48 */
|
||||
uint8_t personal[BLAKE2B_PERSONALBYTES]; /* 64 */
|
||||
});
|
||||
|
||||
typedef struct blake2b_param__ blake2b_param;
|
||||
|
||||
blake2b_param P[1];
|
||||
|
||||
static __inline__ uint64_t load64(const void *src) {
|
||||
return *(uint64_t *)src;
|
||||
}
|
||||
|
||||
static __inline__ void store64(void *dst, uint64_t w) {
|
||||
*(uint64_t *)dst = w;
|
||||
}
|
||||
|
||||
static __inline__ uint64_t rotr64(const uint64_t w, const unsigned c) {
|
||||
return (w >> c) | (w << (64 - c));
|
||||
}
|
||||
|
||||
static const uint64_t blake2b_IV[8] = {
|
||||
0x6a09e667f3bcc908ULL, 0xbb67ae8584caa73bULL,
|
||||
0x3c6ef372fe94f82bULL, 0xa54ff53a5f1d36f1ULL,
|
||||
0x510e527fade682d1ULL, 0x9b05688c2b3e6c1fULL,
|
||||
0x1f83d9abfb41bd6bULL, 0x5be0cd19137e2179ULL
|
||||
};
|
||||
|
||||
static const uint8_t blake2b_sigma[12][16] = {
|
||||
{ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15 },
|
||||
{ 14, 10, 4, 8, 9, 15, 13, 6, 1, 12, 0, 2, 11, 7, 5, 3 },
|
||||
{ 11, 8, 12, 0, 5, 2, 15, 13, 10, 14, 3, 6, 7, 1, 9, 4 },
|
||||
{ 7, 9, 3, 1, 13, 12, 11, 14, 2, 6, 5, 10, 4, 0, 15, 8 },
|
||||
{ 9, 0, 5, 7, 2, 4, 10, 15, 14, 1, 11, 12, 6, 8, 3, 13 },
|
||||
{ 2, 12, 6, 10, 0, 11, 8, 3, 4, 13, 7, 5, 15, 14, 1, 9 },
|
||||
{ 12, 5, 1, 15, 14, 13, 4, 10, 0, 7, 6, 3, 9, 2, 8, 11 },
|
||||
{ 13, 11, 7, 14, 12, 1, 3, 9, 5, 0, 15, 4, 8, 6, 2, 10 },
|
||||
{ 6, 15, 14, 9, 11, 3, 0, 8, 12, 2, 13, 7, 1, 4, 10, 5 },
|
||||
{ 10, 2, 8, 4, 7, 6, 1, 5, 15, 11, 9, 14, 3, 12, 13 , 0 },
|
||||
{ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15 },
|
||||
{ 14, 10, 4, 8, 9, 15, 13, 6, 1, 12, 0, 2, 11, 7, 5, 3 }
|
||||
};
|
||||
|
||||
static __inline__ void blake2b_set_lastnode() { S->f[1] = (uint64_t)-1; }
|
||||
|
||||
/* Some helper functions, not necessarily useful */
|
||||
static __inline__ int blake2b_is_lastblock() { return S->f[0] != 0; }
|
||||
|
||||
static __inline__ void blake2b_set_lastblock() {
|
||||
if (S->last_node) blake2b_set_lastnode();
|
||||
|
||||
S->f[0] = (uint64_t)-1;
|
||||
}
|
||||
|
||||
static __inline__ void blake2b_increment_counter(const uint64_t inc) {
|
||||
S->t[0] += inc;
|
||||
S->t[1] += (S->t[0] < inc);
|
||||
}
|
||||
|
||||
#define G(r, i, a, b, c, d) \
|
||||
do { \
|
||||
a = a + b + m[blake2b_sigma[r][2 * i + 0]]; \
|
||||
d = rotr64(d ^ a, 32); \
|
||||
c = c + d; \
|
||||
b = rotr64(b ^ c, 24); \
|
||||
a = a + b + m[blake2b_sigma[r][2 * i + 1]]; \
|
||||
d = rotr64(d ^ a, 16); \
|
||||
c = c + d; \
|
||||
b = rotr64(b ^ c, 63); \
|
||||
} while (0)
|
||||
|
||||
static void round(uint32_t r, uint64_t m[16], uint64_t v[16]) {
|
||||
G(r, 0, v[0], v[4], v[8], v[12]);
|
||||
G(r, 1, v[1], v[5], v[9], v[13]);
|
||||
G(r, 2, v[2], v[6], v[10], v[14]);
|
||||
G(r, 3, v[3], v[7], v[11], v[15]);
|
||||
G(r, 4, v[0], v[5], v[10], v[15]);
|
||||
G(r, 5, v[1], v[6], v[11], v[12]);
|
||||
G(r, 6, v[2], v[7], v[8], v[13]);
|
||||
G(r, 7, v[3], v[4], v[9], v[14]);
|
||||
};
|
||||
|
||||
static void blake2b_compress(const uint8_t block[BLAKE2B_BLOCKBYTES]) {
|
||||
uint64_t m[16];
|
||||
uint64_t v[16];
|
||||
|
||||
#pragma clang loop unroll(full)
|
||||
for (int i = 0; i < 16; ++i) {
|
||||
m[i] = load64(block + i * sizeof(m[i]));
|
||||
}
|
||||
|
||||
#pragma clang loop unroll(full)
|
||||
for (int i = 0; i < 8; ++i) {
|
||||
v[i] = S->h[i];
|
||||
}
|
||||
|
||||
v[8] = blake2b_IV[0];
|
||||
v[9] = blake2b_IV[1];
|
||||
v[10] = blake2b_IV[2];
|
||||
v[11] = blake2b_IV[3];
|
||||
v[12] = blake2b_IV[4] ^ S->t[0];
|
||||
v[13] = blake2b_IV[5] ^ S->t[1];
|
||||
v[14] = blake2b_IV[6] ^ S->f[0];
|
||||
v[15] = blake2b_IV[7] ^ S->f[1];
|
||||
|
||||
#pragma clang loop unroll(full)
|
||||
for (int i = 0; i < 12; ++i) {
|
||||
round(i, m, v);
|
||||
}
|
||||
|
||||
#pragma clang loop unroll(full)
|
||||
for (int i = 0; i < 8; ++i) {
|
||||
S->h[i] = S->h[i] ^ v[i] ^ v[i + 8];
|
||||
}
|
||||
}
|
||||
|
||||
#undef G
|
||||
|
||||
void blake2b_update(const void *pin, int inlen) {
|
||||
const unsigned char *in = (const unsigned char *)pin;
|
||||
if (inlen > 0) {
|
||||
int left = S->buflen;
|
||||
int fill = BLAKE2B_BLOCKBYTES - left;
|
||||
if (inlen > fill) {
|
||||
S->buflen = 0;
|
||||
/* Fill buffer */
|
||||
for (uint8_t i = 0; i < fill; i++) {
|
||||
S->buf[left + i] = in[i];
|
||||
}
|
||||
blake2b_increment_counter(BLAKE2B_BLOCKBYTES);
|
||||
blake2b_compress(S->buf); /* Compress */
|
||||
in += fill;
|
||||
inlen -= fill;
|
||||
while (inlen > BLAKE2B_BLOCKBYTES) {
|
||||
blake2b_increment_counter(BLAKE2B_BLOCKBYTES);
|
||||
blake2b_compress(in);
|
||||
in += BLAKE2B_BLOCKBYTES;
|
||||
inlen -= BLAKE2B_BLOCKBYTES;
|
||||
}
|
||||
}
|
||||
for (uint8_t i = 0; i < inlen; i++) {
|
||||
S->buf[S->buflen + i] = in[i];
|
||||
}
|
||||
S->buflen += inlen;
|
||||
}
|
||||
}
|
||||
|
||||
WASM_EXPORT
|
||||
void Hash_Final() {
|
||||
int outlen = S->outlen;
|
||||
uint8_t buffer[BLAKE2B_OUTBYTES] = {0};
|
||||
|
||||
if (blake2b_is_lastblock()) {
|
||||
return;
|
||||
}
|
||||
|
||||
blake2b_increment_counter(S->buflen);
|
||||
blake2b_set_lastblock();
|
||||
for (int i = 0; i < BLAKE2B_BLOCKBYTES - S->buflen; i++) { /* Padding */
|
||||
(S->buf + S->buflen)[i] = 0;
|
||||
}
|
||||
blake2b_compress(S->buf);
|
||||
|
||||
for (int i = 0; i < 8; ++i) {
|
||||
/* Output full hash to temp buffer */
|
||||
store64(buffer + sizeof(S->h[i]) * i, S->h[i]);
|
||||
}
|
||||
|
||||
for (uint8_t i = 0; i < S->outlen; i++) {
|
||||
main_buffer[i] = buffer[i];
|
||||
}
|
||||
}
|
||||
|
||||
static void blake2b_init0() {
|
||||
memset(S, 0, sizeof(blake2b_state));
|
||||
|
||||
for (int i = 0; i < 8; ++i) {
|
||||
S->h[i] = blake2b_IV[i];
|
||||
}
|
||||
}
|
||||
|
||||
/* init xors IV with input parameter block */
|
||||
void blake2b_init_param() {
|
||||
const uint8_t *p = (const uint8_t *)(P);
|
||||
int i;
|
||||
|
||||
blake2b_init0();
|
||||
|
||||
/* IV XOR ParamBlock */
|
||||
for (i = 0; i < 8; ++i) {
|
||||
S->h[i] ^= load64(p + sizeof(S->h[i]) * i);
|
||||
}
|
||||
|
||||
S->outlen = P->digest_length;
|
||||
}
|
||||
|
||||
void blake2b_init_key(int outlen, const uint8_t *key, int keylen) {
|
||||
P->digest_length = (uint8_t)outlen;
|
||||
P->key_length = (uint8_t)keylen;
|
||||
P->fanout = 1;
|
||||
P->depth = 1;
|
||||
// P->leaf_length = 0;
|
||||
// P->node_offset = 0;
|
||||
// P->xof_length = 0;
|
||||
// P->node_depth = 0;
|
||||
// P->inner_length = 0;
|
||||
// memset(P->reserved, 0, sizeof(P->reserved));
|
||||
// memset(P->salt, 0, sizeof(P->salt));
|
||||
// memset(P->personal, 0, sizeof(P->personal));
|
||||
|
||||
blake2b_init_param();
|
||||
|
||||
if (keylen > 0) {
|
||||
uint8_t block[BLAKE2B_BLOCKBYTES];
|
||||
memset128(block, 0);
|
||||
for (uint8_t i = 0; i < keylen; i++) {
|
||||
block[i] = key[i];
|
||||
}
|
||||
blake2b_update(block, BLAKE2B_BLOCKBYTES);
|
||||
}
|
||||
}
|
||||
|
||||
WASM_EXPORT
|
||||
void Hash_Init(uint32_t bits) {
|
||||
int outlen = bits & 0xFFFF;
|
||||
int keylen = bits >> 16;
|
||||
blake2b_init_key(outlen / 8, main_buffer, keylen);
|
||||
}
|
||||
|
||||
WASM_EXPORT
|
||||
void Hash_Update(uint32_t size) {
|
||||
blake2b_update(main_buffer, size);
|
||||
}
|
||||
|
||||
WASM_EXPORT
|
||||
const uint32_t STATE_SIZE = sizeof(S);
|
||||
|
||||
WASM_EXPORT
|
||||
uint8_t* Hash_GetState() {
|
||||
return (uint8_t*) S;
|
||||
}
|
||||
|
||||
WASM_EXPORT
|
||||
void Hash_Calculate(uint32_t length, uint32_t initParam) {
|
||||
Hash_Init(initParam);
|
||||
Hash_Update(length);
|
||||
Hash_Final();
|
||||
}
|
||||
+284
@@ -0,0 +1,284 @@
|
||||
/*
|
||||
BLAKE2 reference source code package - reference C implementations
|
||||
Copyright 2012, Samuel Neves <sneves@dei.uc.pt>. You may use this under the
|
||||
terms of the CC0, the OpenSSL Licence, or the Apache Public License 2.0, at
|
||||
your option. The terms of these licenses can be found at:
|
||||
- CC0 1.0 Universal : http://creativecommons.org/publicdomain/zero/1.0
|
||||
- OpenSSL license : https://www.openssl.org/source/license.html
|
||||
- Apache 2.0 : http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
More information about the BLAKE2 hash function can be found at
|
||||
https://blake2.net.
|
||||
|
||||
Modified for hash-wasm by Dani Biró
|
||||
*/
|
||||
|
||||
#define WITH_BUFFER
|
||||
#include "hash-wasm.h"
|
||||
|
||||
#define BLAKE2_PACKED(x) x __attribute__((packed))
|
||||
|
||||
enum blake2s_constant {
|
||||
BLAKE2S_BLOCKBYTES = 64,
|
||||
BLAKE2S_OUTBYTES = 32,
|
||||
BLAKE2S_KEYBYTES = 32,
|
||||
BLAKE2S_SALTBYTES = 8,
|
||||
BLAKE2S_PERSONALBYTES = 8
|
||||
};
|
||||
|
||||
typedef struct blake2s_state__ {
|
||||
uint32_t h[8];
|
||||
uint32_t t[2];
|
||||
uint32_t f[2];
|
||||
uint8_t buf[BLAKE2S_BLOCKBYTES];
|
||||
int buflen;
|
||||
int outlen;
|
||||
uint8_t last_node;
|
||||
} blake2s_state;
|
||||
|
||||
blake2s_state S[1];
|
||||
|
||||
BLAKE2_PACKED(struct blake2s_param__ {
|
||||
uint8_t digest_length; /* 1 */
|
||||
uint8_t key_length; /* 2 */
|
||||
uint8_t fanout; /* 3 */
|
||||
uint8_t depth; /* 4 */
|
||||
uint32_t leaf_length; /* 8 */
|
||||
uint32_t node_offset; /* 12 */
|
||||
uint16_t xof_length; /* 14 */
|
||||
uint8_t node_depth; /* 15 */
|
||||
uint8_t inner_length; /* 16 */
|
||||
uint8_t salt[BLAKE2S_SALTBYTES]; /* 24 */
|
||||
uint8_t personal[BLAKE2S_PERSONALBYTES]; /* 32 */
|
||||
});
|
||||
|
||||
typedef struct blake2s_param__ blake2s_param;
|
||||
|
||||
blake2s_param P[1];
|
||||
|
||||
static __inline__ uint32_t load32(const void *src) {
|
||||
return *(uint32_t *)src;
|
||||
}
|
||||
|
||||
static __inline__ void store32(void *dst, uint32_t w) {
|
||||
*(uint32_t *)dst = w;
|
||||
}
|
||||
|
||||
static __inline__ uint64_t rotr32(const uint32_t w, const unsigned c) {
|
||||
return (w >> c) | (w << (32 - c));
|
||||
}
|
||||
|
||||
static const uint32_t blake2s_IV[8] = {
|
||||
0x6A09E667UL, 0xBB67AE85UL, 0x3C6EF372UL, 0xA54FF53AUL,
|
||||
0x510E527FUL, 0x9B05688CUL, 0x1F83D9ABUL, 0x5BE0CD19UL
|
||||
};
|
||||
|
||||
static const uint8_t blake2s_sigma[10][16] = {
|
||||
{ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15 } ,
|
||||
{ 14, 10, 4, 8, 9, 15, 13, 6, 1, 12, 0, 2, 11, 7, 5, 3 } ,
|
||||
{ 11, 8, 12, 0, 5, 2, 15, 13, 10, 14, 3, 6, 7, 1, 9, 4 } ,
|
||||
{ 7, 9, 3, 1, 13, 12, 11, 14, 2, 6, 5, 10, 4, 0, 15, 8 } ,
|
||||
{ 9, 0, 5, 7, 2, 4, 10, 15, 14, 1, 11, 12, 6, 8, 3, 13 } ,
|
||||
{ 2, 12, 6, 10, 0, 11, 8, 3, 4, 13, 7, 5, 15, 14, 1, 9 } ,
|
||||
{ 12, 5, 1, 15, 14, 13, 4, 10, 0, 7, 6, 3, 9, 2, 8, 11 } ,
|
||||
{ 13, 11, 7, 14, 12, 1, 3, 9, 5, 0, 15, 4, 8, 6, 2, 10 } ,
|
||||
{ 6, 15, 14, 9, 11, 3, 0, 8, 12, 2, 13, 7, 1, 4, 10, 5 } ,
|
||||
{ 10, 2, 8, 4, 7, 6, 1, 5, 15, 11, 9, 14, 3, 12, 13 , 0 }
|
||||
};
|
||||
|
||||
static __inline__ void blake2s_set_lastnode() { S->f[1] = (uint32_t)-1; }
|
||||
|
||||
/* Some helper functions, not necessarily useful */
|
||||
static __inline__ int blake2s_is_lastblock() { return S->f[0] != 0; }
|
||||
|
||||
static __inline__ void blake2s_set_lastblock() {
|
||||
if (S->last_node) blake2s_set_lastnode();
|
||||
|
||||
S->f[0] = (uint32_t)-1;
|
||||
}
|
||||
|
||||
static __inline__ void blake2s_increment_counter(const uint32_t inc) {
|
||||
S->t[0] += inc;
|
||||
S->t[1] += (S->t[0] < inc);
|
||||
}
|
||||
|
||||
#define G(r, i, a, b, c, d) \
|
||||
do { \
|
||||
a = a + b + m[blake2s_sigma[r][2 * i + 0]]; \
|
||||
d = rotr32(d ^ a, 16); \
|
||||
c = c + d; \
|
||||
b = rotr32(b ^ c, 12); \
|
||||
a = a + b + m[blake2s_sigma[r][2 * i + 1]]; \
|
||||
d = rotr32(d ^ a, 8); \
|
||||
c = c + d; \
|
||||
b = rotr32(b ^ c, 7); \
|
||||
} while (0)
|
||||
|
||||
static void round(uint32_t r, uint32_t m[16], uint32_t v[16]) {
|
||||
G(r, 0, v[0], v[4], v[8], v[12]);
|
||||
G(r, 1, v[1], v[5], v[9], v[13]);
|
||||
G(r, 2, v[2], v[6], v[10], v[14]);
|
||||
G(r, 3, v[3], v[7], v[11], v[15]);
|
||||
G(r, 4, v[0], v[5], v[10], v[15]);
|
||||
G(r, 5, v[1], v[6], v[11], v[12]);
|
||||
G(r, 6, v[2], v[7], v[8], v[13]);
|
||||
G(r, 7, v[3], v[4], v[9], v[14]);
|
||||
}
|
||||
|
||||
static void blake2s_compress(const uint8_t block[BLAKE2S_BLOCKBYTES]) {
|
||||
uint32_t m[16];
|
||||
uint32_t v[16];
|
||||
|
||||
memcpy64(m, block);
|
||||
|
||||
memcpy32(v, S->h);
|
||||
|
||||
uint64_t* v64 = (uint64_t*)v;
|
||||
uint64_t* blake2s_IV64 = (uint64_t*)blake2s_IV;
|
||||
uint64_t* st64 = (uint64_t*)S->t;
|
||||
uint64_t* sf64 = (uint64_t*)S->f;
|
||||
v64[4] = blake2s_IV64[0];
|
||||
v64[5] = blake2s_IV64[1];
|
||||
v64[6] = blake2s_IV64[2] ^ st64[0];
|
||||
v64[7] = blake2s_IV64[3] ^ sf64[0];
|
||||
|
||||
#pragma clang loop unroll(full)
|
||||
for (int i = 0; i < 10; ++i) {
|
||||
round(i, m, v);
|
||||
}
|
||||
|
||||
uint64_t* sh64 = (uint64_t*)S->h;
|
||||
#pragma clang loop unroll(full)
|
||||
for (int i = 0; i < 4; ++i) {
|
||||
sh64[i] = sh64[i] ^ v64[i] ^ v64[i + 4];
|
||||
}
|
||||
}
|
||||
|
||||
#undef G
|
||||
|
||||
void blake2s_update(const void *pin, int inlen) {
|
||||
const unsigned char *in = (const unsigned char *)pin;
|
||||
if (inlen > 0) {
|
||||
int left = S->buflen;
|
||||
int fill = BLAKE2S_BLOCKBYTES - left;
|
||||
if (inlen > fill) {
|
||||
S->buflen = 0;
|
||||
/* Fill buffer */
|
||||
memcpy(&S->buf[left], in, fill);
|
||||
blake2s_increment_counter(BLAKE2S_BLOCKBYTES);
|
||||
blake2s_compress(S->buf); /* Compress */
|
||||
in += fill;
|
||||
inlen -= fill;
|
||||
while (inlen > BLAKE2S_BLOCKBYTES) {
|
||||
blake2s_increment_counter(BLAKE2S_BLOCKBYTES);
|
||||
blake2s_compress(in);
|
||||
in += BLAKE2S_BLOCKBYTES;
|
||||
inlen -= BLAKE2S_BLOCKBYTES;
|
||||
}
|
||||
}
|
||||
memcpy(&S->buf[S->buflen], in, inlen);
|
||||
S->buflen += inlen;
|
||||
}
|
||||
}
|
||||
|
||||
WASM_EXPORT
|
||||
void Hash_Final() {
|
||||
int outlen = S->outlen;
|
||||
uint8_t buffer[BLAKE2S_OUTBYTES] = {0};
|
||||
|
||||
if (blake2s_is_lastblock()) {
|
||||
return;
|
||||
}
|
||||
|
||||
blake2s_increment_counter(S->buflen);
|
||||
blake2s_set_lastblock();
|
||||
for (int i = 0; i < BLAKE2S_BLOCKBYTES - S->buflen; i++) { /* Padding */
|
||||
(S->buf + S->buflen)[i] = 0;
|
||||
}
|
||||
blake2s_compress(S->buf);
|
||||
|
||||
for (int i = 0; i < 8; ++i) {
|
||||
/* Output full hash to temp buffer */
|
||||
store32(buffer + sizeof(S->h[i]) * i, S->h[i]);
|
||||
}
|
||||
|
||||
for (uint8_t i = 0; i < S->outlen; i++) {
|
||||
main_buffer[i] = buffer[i];
|
||||
}
|
||||
}
|
||||
|
||||
static void blake2s_init0() {
|
||||
for (int i = 0; i < sizeof(blake2s_state); i++) {
|
||||
((uint8_t*)S)[i] = 0;
|
||||
}
|
||||
|
||||
for (int i = 0; i < 8; ++i) {
|
||||
S->h[i] = blake2s_IV[i];
|
||||
}
|
||||
}
|
||||
|
||||
/* init xors IV with input parameter block */
|
||||
void blake2s_init_param() {
|
||||
const uint8_t *p = (const uint8_t *)(P);
|
||||
|
||||
blake2s_init0();
|
||||
|
||||
/* IV XOR ParamBlock */
|
||||
for (int i = 0; i < 8; ++i) {
|
||||
S->h[i] ^= load32(p + sizeof(S->h[i]) * i);
|
||||
}
|
||||
|
||||
S->outlen = P->digest_length;
|
||||
}
|
||||
|
||||
void blake2s_init_key(int outlen, const uint8_t *key, int keylen) {
|
||||
P->digest_length = (uint8_t)outlen;
|
||||
P->key_length = (uint8_t)keylen;
|
||||
P->fanout = 1;
|
||||
P->depth = 1;
|
||||
// P->leaf_length = 0;
|
||||
// P->node_offset = 0;
|
||||
// P->xof_length = 0;
|
||||
// P->node_depth = 0;
|
||||
// P->inner_length = 0;
|
||||
// memset(P->reserved, 0, sizeof(P->reserved));
|
||||
// memset(P->salt, 0, sizeof(P->salt));
|
||||
// memset(P->personal, 0, sizeof(P->personal));
|
||||
|
||||
blake2s_init_param();
|
||||
|
||||
if (keylen > 0) {
|
||||
uint8_t block[BLAKE2S_BLOCKBYTES] = { 0 };
|
||||
for (uint8_t i = 0; i < keylen; i++) {
|
||||
block[i] = key[i];
|
||||
}
|
||||
blake2s_update(block, BLAKE2S_BLOCKBYTES);
|
||||
}
|
||||
}
|
||||
|
||||
WASM_EXPORT
|
||||
void Hash_Init(uint32_t bits) {
|
||||
int outlen = bits & 0xFFFF;
|
||||
int keylen = bits >> 16;
|
||||
blake2s_init_key(outlen / 8, main_buffer, keylen);
|
||||
}
|
||||
|
||||
WASM_EXPORT
|
||||
void Hash_Update(uint32_t size) {
|
||||
blake2s_update(main_buffer, size);
|
||||
}
|
||||
|
||||
WASM_EXPORT
|
||||
const uint32_t STATE_SIZE = sizeof(S);
|
||||
|
||||
WASM_EXPORT
|
||||
uint8_t* Hash_GetState() {
|
||||
return (uint8_t*) S;
|
||||
}
|
||||
|
||||
WASM_EXPORT
|
||||
void Hash_Calculate(uint32_t length, uint32_t initParam) {
|
||||
Hash_Init(initParam);
|
||||
Hash_Update(length);
|
||||
Hash_Final();
|
||||
}
|
||||
+867
@@ -0,0 +1,867 @@
|
||||
/*
|
||||
BLAKE3 - reference C implementation
|
||||
https://github.com/BLAKE3-team/BLAKE3
|
||||
This work is released into the public domain with CC0 1.0. Alternatively, it
|
||||
is licensed under the Apache License 2.0.
|
||||
|
||||
Modified for hash-wasm by Dani Biró
|
||||
*/
|
||||
|
||||
#define WITH_BUFFER
|
||||
#include <stddef.h>
|
||||
#include <stdint.h>
|
||||
|
||||
#include "hash-wasm.h"
|
||||
|
||||
#define BLAKE3_VERSION_STRING "0.3.7"
|
||||
#define BLAKE3_KEY_LEN 32
|
||||
#define BLAKE3_OUT_LEN 32
|
||||
#define BLAKE3_BLOCK_LEN 64
|
||||
#define BLAKE3_CHUNK_LEN 1024
|
||||
#define BLAKE3_MAX_DEPTH 54
|
||||
#define MAX_SIMD_DEGREE 1
|
||||
#define MAX_SIMD_DEGREE_OR_2 (MAX_SIMD_DEGREE > 2 ? MAX_SIMD_DEGREE : 2)
|
||||
#define bool uint8_t
|
||||
#define true 1
|
||||
#define false 0
|
||||
|
||||
enum blake3_flags {
|
||||
CHUNK_START = 1 << 0,
|
||||
CHUNK_END = 1 << 1,
|
||||
PARENT = 1 << 2,
|
||||
ROOT = 1 << 3,
|
||||
KEYED_HASH = 1 << 4,
|
||||
DERIVE_KEY_CONTEXT = 1 << 5,
|
||||
DERIVE_KEY_MATERIAL = 1 << 6,
|
||||
};
|
||||
|
||||
static const uint32_t IV[8] =
|
||||
{
|
||||
0x6A09E667UL, 0xBB67AE85UL, 0x3C6EF372UL,
|
||||
0xA54FF53AUL, 0x510E527FUL, 0x9B05688CUL,
|
||||
0x1F83D9ABUL, 0x5BE0CD19UL
|
||||
};
|
||||
|
||||
static const uint8_t MSG_SCHEDULE[7][16] = {
|
||||
{0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15},
|
||||
{2, 6, 3, 10, 7, 0, 4, 13, 1, 11, 12, 5, 9, 14, 15, 8},
|
||||
{3, 4, 10, 12, 13, 2, 7, 14, 6, 5, 9, 0, 11, 15, 8, 1},
|
||||
{10, 7, 12, 9, 14, 3, 13, 15, 4, 0, 11, 2, 5, 8, 1, 6},
|
||||
{12, 13, 9, 11, 15, 10, 14, 8, 7, 2, 5, 3, 0, 1, 6, 4},
|
||||
{9, 14, 11, 5, 8, 12, 15, 1, 13, 3, 0, 10, 2, 6, 4, 7},
|
||||
{11, 15, 5, 0, 1, 9, 8, 6, 14, 10, 2, 12, 3, 4, 7, 13},
|
||||
};
|
||||
|
||||
size_t blake3_simd_degree(void) { return 1; }
|
||||
|
||||
// This struct is a private implementation detail. It has to be here because
|
||||
// it's part of blake3_hasher below.
|
||||
typedef struct {
|
||||
uint32_t cv[8];
|
||||
uint64_t chunk_counter;
|
||||
uint8_t buf[BLAKE3_BLOCK_LEN];
|
||||
uint8_t buf_len;
|
||||
uint8_t blocks_compressed;
|
||||
uint8_t flags;
|
||||
} blake3_chunk_state;
|
||||
|
||||
typedef struct {
|
||||
uint32_t key[8];
|
||||
blake3_chunk_state chunk;
|
||||
uint8_t cv_stack_len;
|
||||
// The stack size is MAX_DEPTH + 1 because we do lazy merging. For example,
|
||||
// with 7 chunks, we have 3 entries in the stack. Adding an 8th chunk
|
||||
// requires a 4th entry, rather than merging everything down to 1, because we
|
||||
// don't know whether more input is coming. This is different from how the
|
||||
// reference implementation does things.
|
||||
uint8_t cv_stack[(BLAKE3_MAX_DEPTH + 1) * BLAKE3_OUT_LEN];
|
||||
} blake3_hasher;
|
||||
|
||||
/* Find index of the highest set bit */
|
||||
/* x is assumed to be nonzero. */
|
||||
static unsigned int highest_one(uint64_t x) {
|
||||
return 63 ^ __builtin_clzll(x);
|
||||
}
|
||||
|
||||
// Count the number of 1 bits.
|
||||
static __inline__ unsigned int popcnt(uint64_t x) {
|
||||
return __builtin_popcountll(x);
|
||||
}
|
||||
|
||||
static __inline__ uint64_t round_down_to_power_of_2(uint64_t x) {
|
||||
return 1ULL << highest_one(x | 1);
|
||||
}
|
||||
|
||||
static __inline__ uint32_t counter_low(uint64_t counter) {
|
||||
return (uint32_t)counter;
|
||||
}
|
||||
|
||||
static __inline__ uint32_t counter_high(uint64_t counter) {
|
||||
return (uint32_t)(counter >> 32);
|
||||
}
|
||||
|
||||
static __inline__ uint32_t load32(const void *src) {
|
||||
const uint32_t *p = (const uint32_t *)src;
|
||||
return p[0];
|
||||
}
|
||||
|
||||
static __inline__ uint32_t rotr32(uint32_t w, uint32_t c) {
|
||||
return (w >> c) | (w << (32 - c));
|
||||
}
|
||||
|
||||
static __inline__ void g(
|
||||
uint32_t *state, size_t a, size_t b, size_t c, size_t d, uint32_t x, uint32_t y
|
||||
) {
|
||||
state[a] = state[a] + state[b] + x;
|
||||
state[d] = rotr32(state[d] ^ state[a], 16);
|
||||
state[c] = state[c] + state[d];
|
||||
state[b] = rotr32(state[b] ^ state[c], 12);
|
||||
state[a] = state[a] + state[b] + y;
|
||||
state[d] = rotr32(state[d] ^ state[a], 8);
|
||||
state[c] = state[c] + state[d];
|
||||
state[b] = rotr32(state[b] ^ state[c], 7);
|
||||
}
|
||||
|
||||
static __inline__ void round_fn(
|
||||
uint32_t state[16], const uint32_t *msg, size_t round
|
||||
) {
|
||||
// Select the message schedule based on the round.
|
||||
const uint8_t *schedule = MSG_SCHEDULE[round];
|
||||
|
||||
// Mix the columns.
|
||||
g(state, 0, 4, 8, 12, msg[schedule[0]], msg[schedule[1]]);
|
||||
g(state, 1, 5, 9, 13, msg[schedule[2]], msg[schedule[3]]);
|
||||
g(state, 2, 6, 10, 14, msg[schedule[4]], msg[schedule[5]]);
|
||||
g(state, 3, 7, 11, 15, msg[schedule[6]], msg[schedule[7]]);
|
||||
|
||||
// Mix the rows.
|
||||
g(state, 0, 5, 10, 15, msg[schedule[8]], msg[schedule[9]]);
|
||||
g(state, 1, 6, 11, 12, msg[schedule[10]], msg[schedule[11]]);
|
||||
g(state, 2, 7, 8, 13, msg[schedule[12]], msg[schedule[13]]);
|
||||
g(state, 3, 4, 9, 14, msg[schedule[14]], msg[schedule[15]]);
|
||||
}
|
||||
|
||||
static __inline__ void load_key_words(
|
||||
const uint8_t key[BLAKE3_KEY_LEN], uint32_t key_words[8]
|
||||
) {
|
||||
memcpy32(key_words, key);
|
||||
}
|
||||
|
||||
static __inline__ void compress_pre(
|
||||
uint32_t state[16], const uint32_t cv[8], const uint8_t block[BLAKE3_BLOCK_LEN],
|
||||
uint8_t block_len, uint64_t counter, uint8_t flags
|
||||
) {
|
||||
uint32_t block_words[16];
|
||||
|
||||
memcpy64(block_words, block);
|
||||
memcpy32(state, cv);
|
||||
memcpy16(&state[8], IV);
|
||||
|
||||
state[12] = counter_low(counter);
|
||||
state[13] = counter_high(counter);
|
||||
state[14] = (uint32_t)block_len;
|
||||
state[15] = (uint32_t)flags;
|
||||
|
||||
#pragma clang loop unroll(full)
|
||||
for (int i = 0; i < 7; i++) {
|
||||
// Select the message schedule based on the round.
|
||||
round_fn(state, &block_words[0], i);
|
||||
}
|
||||
}
|
||||
|
||||
static __inline__ void store32(void *dst, uint32_t w) {
|
||||
uint32_t *p = (uint32_t *)dst;
|
||||
p[0] = w;
|
||||
}
|
||||
|
||||
static __inline__ void store_cv_words(uint8_t bytes_out[32], uint32_t cv_words[8]) {
|
||||
memcpy32(bytes_out, cv_words);
|
||||
}
|
||||
|
||||
void blake3_compress_xof_portable(
|
||||
const uint32_t cv[8], const uint8_t block[BLAKE3_BLOCK_LEN], uint8_t block_len,
|
||||
uint64_t counter, uint8_t flags, uint8_t out[64]
|
||||
) {
|
||||
uint32_t state[16];
|
||||
compress_pre(state, cv, block, block_len, counter, flags);
|
||||
|
||||
uint64_t *state64 = (uint64_t *)state;
|
||||
uint64_t *out64 = (uint64_t *)out;
|
||||
uint64_t *cv64 = (uint64_t *)cv;
|
||||
|
||||
out64[0] = state64[0] ^ state64[4];
|
||||
out64[1] = state64[1] ^ state64[5];
|
||||
out64[2] = state64[2] ^ state64[6];
|
||||
out64[3] = state64[3] ^ state64[7];
|
||||
out64[4] = state64[4] ^ cv64[0];
|
||||
out64[5] = state64[5] ^ cv64[1];
|
||||
out64[6] = state64[6] ^ cv64[2];
|
||||
out64[7] = state64[7] ^ cv64[3];
|
||||
}
|
||||
|
||||
void blake3_compress_in_place_portable(
|
||||
uint32_t cv[8], const uint8_t block[BLAKE3_BLOCK_LEN], uint8_t block_len,
|
||||
uint64_t counter, uint8_t flags
|
||||
) {
|
||||
uint32_t state[16];
|
||||
compress_pre(state, cv, block, block_len, counter, flags);
|
||||
|
||||
uint64_t *state64 = (uint64_t *)state;
|
||||
uint64_t *cv64 = (uint64_t *)cv;
|
||||
cv64[0] = state64[0] ^ state64[4];
|
||||
cv64[1] = state64[1] ^ state64[5];
|
||||
cv64[2] = state64[2] ^ state64[6];
|
||||
cv64[3] = state64[3] ^ state64[7];
|
||||
}
|
||||
|
||||
static __inline__ void chunk_state_init(blake3_chunk_state *self, const uint32_t key[8], uint8_t flags) {
|
||||
memcpy32(self->cv, key);
|
||||
self->chunk_counter = 0;
|
||||
memset64(self->buf, 0);
|
||||
self->buf_len = 0;
|
||||
self->blocks_compressed = 0;
|
||||
self->flags = flags;
|
||||
}
|
||||
|
||||
static __inline__ void chunk_state_reset(
|
||||
blake3_chunk_state *self, const uint32_t key[8], uint64_t chunk_counter
|
||||
) {
|
||||
memcpy32(self->cv, key);
|
||||
self->chunk_counter = chunk_counter;
|
||||
self->blocks_compressed = 0;
|
||||
memset64(self->buf, 0);
|
||||
self->buf_len = 0;
|
||||
}
|
||||
|
||||
static __inline__ size_t chunk_state_len(const blake3_chunk_state *self) {
|
||||
return (BLAKE3_BLOCK_LEN * (size_t)self->blocks_compressed) + ((size_t)self->buf_len);
|
||||
}
|
||||
|
||||
static __inline__ size_t chunk_state_fill_buf(
|
||||
blake3_chunk_state *self, const uint8_t *input, size_t input_len
|
||||
) {
|
||||
size_t take = BLAKE3_BLOCK_LEN - ((size_t)self->buf_len);
|
||||
if (take > input_len) {
|
||||
take = input_len;
|
||||
}
|
||||
uint8_t *dest = self->buf + ((size_t)self->buf_len);
|
||||
for (size_t i = 0; i < take; i++) {
|
||||
dest[i] = input[i];
|
||||
}
|
||||
self->buf_len += (uint8_t)take;
|
||||
return take;
|
||||
}
|
||||
|
||||
static __inline__ uint8_t chunk_state_maybe_start_flag(const blake3_chunk_state *self) {
|
||||
if (self->blocks_compressed == 0) {
|
||||
return CHUNK_START;
|
||||
} else {
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
typedef struct {
|
||||
uint32_t input_cv[8];
|
||||
uint64_t counter;
|
||||
uint8_t block[BLAKE3_BLOCK_LEN];
|
||||
uint8_t block_len;
|
||||
uint8_t flags;
|
||||
} output_t;
|
||||
|
||||
static __inline__ output_t make_output(
|
||||
const uint32_t input_cv[8], const uint8_t block[BLAKE3_BLOCK_LEN],
|
||||
uint8_t block_len, uint64_t counter, uint8_t flags
|
||||
) {
|
||||
output_t ret;
|
||||
memcpy32(ret.input_cv, input_cv);
|
||||
memcpy64(ret.block, block);
|
||||
|
||||
ret.block_len = block_len;
|
||||
ret.counter = counter;
|
||||
ret.flags = flags;
|
||||
return ret;
|
||||
}
|
||||
|
||||
// Chaining values within a given chunk (specifically the compress_in_place
|
||||
// interface) are represented as words. This avoids unnecessary bytes<->words
|
||||
// conversion overhead in the portable implementation. However, the hash_many
|
||||
// interface handles both user input and parent node blocks, so it accepts
|
||||
// bytes. For that reason, chaining values in the CV stack are represented as
|
||||
// bytes.
|
||||
static __inline__ void output_chaining_value(const output_t *self, uint8_t cv[32]) {
|
||||
uint32_t cv_words[8];
|
||||
memcpy32(cv_words, self->input_cv);
|
||||
blake3_compress_in_place_portable(
|
||||
cv_words, self->block, self->block_len, self->counter, self->flags
|
||||
);
|
||||
store_cv_words(cv, cv_words);
|
||||
}
|
||||
|
||||
static __inline__ void output_root_bytes(
|
||||
const output_t *self, uint64_t seek, uint8_t *out, size_t out_len
|
||||
) {
|
||||
uint64_t output_block_counter = seek / 64;
|
||||
size_t offset_within_block = seek % 64;
|
||||
uint8_t wide_buf[64];
|
||||
while (out_len > 0) {
|
||||
blake3_compress_xof_portable(
|
||||
self->input_cv, self->block, self->block_len, output_block_counter, self->flags | ROOT, wide_buf
|
||||
);
|
||||
size_t available_bytes = 64 - offset_within_block;
|
||||
size_t memcpy_len;
|
||||
if (out_len > available_bytes) {
|
||||
memcpy_len = available_bytes;
|
||||
} else {
|
||||
memcpy_len = out_len;
|
||||
}
|
||||
memcpy(out, wide_buf + offset_within_block, memcpy_len);
|
||||
out += memcpy_len;
|
||||
out_len -= memcpy_len;
|
||||
output_block_counter += 1;
|
||||
offset_within_block = 0;
|
||||
}
|
||||
}
|
||||
|
||||
static __inline__ void chunk_state_update(
|
||||
blake3_chunk_state *self, const uint8_t *input, size_t input_len
|
||||
) {
|
||||
if (self->buf_len > 0) {
|
||||
size_t take = chunk_state_fill_buf(self, input, input_len);
|
||||
input += take;
|
||||
input_len -= take;
|
||||
if (input_len > 0) {
|
||||
blake3_compress_in_place_portable(
|
||||
self->cv, self->buf, BLAKE3_BLOCK_LEN, self->chunk_counter,
|
||||
self->flags | chunk_state_maybe_start_flag(self)
|
||||
);
|
||||
self->blocks_compressed += 1;
|
||||
self->buf_len = 0;
|
||||
memset64(self->buf, 0);
|
||||
}
|
||||
}
|
||||
|
||||
while (input_len > BLAKE3_BLOCK_LEN) {
|
||||
blake3_compress_in_place_portable(
|
||||
self->cv, input, BLAKE3_BLOCK_LEN, self->chunk_counter,
|
||||
self->flags | chunk_state_maybe_start_flag(self)
|
||||
);
|
||||
self->blocks_compressed += 1;
|
||||
input += BLAKE3_BLOCK_LEN;
|
||||
input_len -= BLAKE3_BLOCK_LEN;
|
||||
}
|
||||
|
||||
size_t take = chunk_state_fill_buf(self, input, input_len);
|
||||
input += take;
|
||||
input_len -= take;
|
||||
}
|
||||
|
||||
static __inline__ output_t chunk_state_output(const blake3_chunk_state *self) {
|
||||
uint8_t block_flags =
|
||||
self->flags | chunk_state_maybe_start_flag(self) | CHUNK_END;
|
||||
|
||||
return make_output(self->cv, self->buf, self->buf_len, self->chunk_counter, block_flags);
|
||||
}
|
||||
|
||||
static __inline__ output_t parent_output(
|
||||
const uint8_t block[BLAKE3_BLOCK_LEN], const uint32_t key[8], uint8_t flags
|
||||
) {
|
||||
return make_output(key, block, BLAKE3_BLOCK_LEN, 0, flags | PARENT);
|
||||
}
|
||||
|
||||
// Given some input larger than one chunk, return the number of bytes that
|
||||
// should go in the left subtree. This is the largest power-of-2 number of
|
||||
// chunks that leaves at least 1 byte for the right subtree.
|
||||
static __inline__ size_t left_len(size_t content_len) {
|
||||
// Subtract 1 to reserve at least one byte for the right side. content_len
|
||||
// should always be greater than BLAKE3_CHUNK_LEN.
|
||||
size_t full_chunks = (content_len - 1) / BLAKE3_CHUNK_LEN;
|
||||
return round_down_to_power_of_2(full_chunks) * BLAKE3_CHUNK_LEN;
|
||||
}
|
||||
|
||||
static __inline__ void hash_one_portable(
|
||||
const uint8_t *input, size_t blocks, const uint32_t key[8], uint64_t counter,
|
||||
uint8_t flags, uint8_t flags_start, uint8_t flags_end, uint8_t out[BLAKE3_OUT_LEN]
|
||||
) {
|
||||
uint32_t cv[8];
|
||||
memcpy32(cv, key);
|
||||
uint8_t block_flags = flags | flags_start;
|
||||
while (blocks > 0) {
|
||||
if (blocks == 1) {
|
||||
block_flags |= flags_end;
|
||||
}
|
||||
blake3_compress_in_place_portable(cv, input, BLAKE3_BLOCK_LEN, counter, block_flags);
|
||||
input = &input[BLAKE3_BLOCK_LEN];
|
||||
blocks -= 1;
|
||||
block_flags = flags;
|
||||
}
|
||||
store_cv_words(out, cv);
|
||||
}
|
||||
|
||||
void blake3_hash_many_portable(
|
||||
const uint8_t *const *inputs, size_t num_inputs, size_t blocks, const uint32_t key[8],
|
||||
uint64_t counter, bool increment_counter, uint8_t flags, uint8_t flags_start,
|
||||
uint8_t flags_end, uint8_t *out
|
||||
) {
|
||||
while (num_inputs > 0) {
|
||||
hash_one_portable(inputs[0], blocks, key, counter, flags, flags_start, flags_end, out);
|
||||
if (increment_counter) {
|
||||
counter += 1;
|
||||
}
|
||||
inputs += 1;
|
||||
num_inputs -= 1;
|
||||
out = &out[BLAKE3_OUT_LEN];
|
||||
}
|
||||
}
|
||||
|
||||
// Use SIMD parallelism to hash up to MAX_SIMD_DEGREE chunks at the same time
|
||||
// on a single thread. Write out the chunk chaining values and return the
|
||||
// number of chunks hashed. These chunks are never the root and never empty;
|
||||
// those cases use a different codepath.
|
||||
static __inline__ size_t compress_chunks_parallel(
|
||||
const uint8_t *input, size_t input_len, const uint32_t key[8],
|
||||
uint64_t chunk_counter, uint8_t flags, uint8_t *out
|
||||
) {
|
||||
const uint8_t *chunks_array[MAX_SIMD_DEGREE];
|
||||
size_t input_position = 0;
|
||||
size_t chunks_array_len = 0;
|
||||
while (input_len - input_position >= BLAKE3_CHUNK_LEN) {
|
||||
chunks_array[chunks_array_len] = &input[input_position];
|
||||
input_position += BLAKE3_CHUNK_LEN;
|
||||
chunks_array_len += 1;
|
||||
}
|
||||
|
||||
blake3_hash_many_portable(
|
||||
chunks_array, chunks_array_len, BLAKE3_CHUNK_LEN / BLAKE3_BLOCK_LEN, key,
|
||||
chunk_counter, true, flags, CHUNK_START, CHUNK_END, out
|
||||
);
|
||||
|
||||
// Hash the remaining partial chunk, if there is one. Note that the empty
|
||||
// chunk (meaning the empty message) is a different codepath.
|
||||
if (input_len > input_position) {
|
||||
uint64_t counter = chunk_counter + (uint64_t)chunks_array_len;
|
||||
blake3_chunk_state chunk_state;
|
||||
chunk_state_init(&chunk_state, key, flags);
|
||||
chunk_state.chunk_counter = counter;
|
||||
chunk_state_update(
|
||||
&chunk_state, &input[input_position], input_len - input_position
|
||||
);
|
||||
output_t output = chunk_state_output(&chunk_state);
|
||||
output_chaining_value(&output, &out[chunks_array_len * BLAKE3_OUT_LEN]);
|
||||
return chunks_array_len + 1;
|
||||
} else {
|
||||
return chunks_array_len;
|
||||
}
|
||||
}
|
||||
|
||||
// Use SIMD parallelism to hash up to MAX_SIMD_DEGREE parents at the same time
|
||||
// on a single thread. Write out the parent chaining values and return the
|
||||
// number of parents hashed. (If there's an odd input chaining value left over,
|
||||
// return it as an additional output.) These parents are never the root and
|
||||
// never empty; those cases use a different codepath.
|
||||
static __inline__ size_t compress_parents_parallel(
|
||||
const uint8_t *child_chaining_values, size_t num_chaining_values,
|
||||
const uint32_t key[8], uint8_t flags, uint8_t *out
|
||||
) {
|
||||
const uint8_t *parents_array[MAX_SIMD_DEGREE_OR_2];
|
||||
size_t parents_array_len = 0;
|
||||
|
||||
while (num_chaining_values - (2 * parents_array_len) >= 2) {
|
||||
parents_array[parents_array_len] =
|
||||
&child_chaining_values[2 * parents_array_len * BLAKE3_OUT_LEN];
|
||||
parents_array_len += 1;
|
||||
}
|
||||
|
||||
blake3_hash_many_portable(parents_array, parents_array_len, 1, key,
|
||||
0, // Parents always use counter 0.
|
||||
false, flags | PARENT,
|
||||
0, // Parents have no start flags.
|
||||
0, // Parents have no end flags.
|
||||
out);
|
||||
|
||||
// If there's an odd child left over, it becomes an output.
|
||||
if (num_chaining_values > 2 * parents_array_len) {
|
||||
memcpy32(
|
||||
&out[parents_array_len * BLAKE3_OUT_LEN],
|
||||
&child_chaining_values[2 * parents_array_len * BLAKE3_OUT_LEN]
|
||||
);
|
||||
return parents_array_len + 1;
|
||||
} else {
|
||||
return parents_array_len;
|
||||
}
|
||||
}
|
||||
|
||||
// The wide helper function returns (writes out) an array of chaining values
|
||||
// and returns the length of that array. The number of chaining values returned
|
||||
// is the dyanmically detected SIMD degree, at most MAX_SIMD_DEGREE. Or fewer,
|
||||
// if the input is shorter than that many chunks. The reason for maintaining a
|
||||
// wide array of chaining values going back up the tree, is to allow the
|
||||
// implementation to hash as many parents in parallel as possible.
|
||||
//
|
||||
// As a special case when the SIMD degree is 1, this function will still return
|
||||
// at least 2 outputs. This guarantees that this function doesn't perform the
|
||||
// root compression. (If it did, it would use the wrong flags, and also we
|
||||
// wouldn't be able to implement exendable ouput.) Note that this function is
|
||||
// not used when the whole input is only 1 chunk long; that's a different
|
||||
// codepath.
|
||||
//
|
||||
// Why not just have the caller split the input on the first update(), instead
|
||||
// of implementing this special rule? Because we don't want to limit SIMD or
|
||||
// multi-threading parallelism for that update().
|
||||
static size_t blake3_compress_subtree_wide(
|
||||
const uint8_t *input, size_t input_len, const uint32_t key[8],
|
||||
uint64_t chunk_counter, uint8_t flags, uint8_t *out
|
||||
) {
|
||||
// Note that the single chunk case does *not* bump the SIMD degree up to 2
|
||||
// when it is 1. If this implementation adds multi-threading in the future,
|
||||
// this gives us the option of multi-threading even the 2-chunk case, which
|
||||
// can help performance on smaller platforms.
|
||||
if (input_len <= blake3_simd_degree() * BLAKE3_CHUNK_LEN) {
|
||||
return compress_chunks_parallel(input, input_len, key, chunk_counter, flags, out);
|
||||
}
|
||||
|
||||
// With more than simd_degree chunks, we need to recurse. Start by dividing
|
||||
// the input into left and right subtrees. (Note that this is only optimal
|
||||
// as long as the SIMD degree is a power of 2. If we ever get a SIMD degree
|
||||
// of 3 or something, we'll need a more complicated strategy.)
|
||||
size_t left_input_len = left_len(input_len);
|
||||
size_t right_input_len = input_len - left_input_len;
|
||||
const uint8_t *right_input = &input[left_input_len];
|
||||
|
||||
uint64_t right_chunk_counter =
|
||||
chunk_counter + (uint64_t)(left_input_len / BLAKE3_CHUNK_LEN);
|
||||
|
||||
// Make space for the child outputs. Here we use MAX_SIMD_DEGREE_OR_2 to
|
||||
// account for the special case of returning 2 outputs when the SIMD degree
|
||||
// is 1.
|
||||
uint8_t cv_array[2 * MAX_SIMD_DEGREE_OR_2 * BLAKE3_OUT_LEN];
|
||||
size_t degree = blake3_simd_degree();
|
||||
if (left_input_len > BLAKE3_CHUNK_LEN && degree == 1) {
|
||||
// The special case: We always use a degree of at least two, to make
|
||||
// sure there are two outputs. Except, as noted above, at the chunk
|
||||
// level, where we allow degree=1. (Note that the 1-chunk-input case is
|
||||
// a different codepath.)
|
||||
degree = 2;
|
||||
}
|
||||
|
||||
uint8_t *right_cvs = &cv_array[degree * BLAKE3_OUT_LEN];
|
||||
|
||||
// Recurse! If this implementation adds multi-threading support in the
|
||||
// future, this is where it will go.
|
||||
size_t left_n = blake3_compress_subtree_wide(input, left_input_len, key, chunk_counter, flags, cv_array);
|
||||
size_t right_n = blake3_compress_subtree_wide(right_input, right_input_len, key, right_chunk_counter, flags, right_cvs);
|
||||
|
||||
// The special case again. If simd_degree=1, then we'll have left_n=1 and
|
||||
// right_n=1. Rather than compressing them into a single output, return
|
||||
// them directly, to make sure we always have at least two outputs.
|
||||
if (left_n == 1) {
|
||||
memcpy64(out, cv_array);
|
||||
return 2;
|
||||
}
|
||||
|
||||
// Otherwise, do one layer of parent node compression.
|
||||
size_t num_chaining_values = left_n + right_n;
|
||||
return compress_parents_parallel(cv_array, num_chaining_values, key, flags, out);
|
||||
}
|
||||
|
||||
// Hash a subtree with compress_subtree_wide(), and then condense the resulting
|
||||
// list of chaining values down to a single parent node. Don't compress that
|
||||
// last parent node, however. Instead, return its message bytes (the
|
||||
// concatenated chaining values of its children). This is necessary when the
|
||||
// first call to update() supplies a complete subtree, because the topmost
|
||||
// parent node of that subtree could end up being the root. It's also necessary
|
||||
// for extended output in the general case.
|
||||
//
|
||||
// As with compress_subtree_wide(), this function is not used on inputs of 1
|
||||
// chunk or less. That's a different codepath.
|
||||
static __inline__ void compress_subtree_to_parent_node(
|
||||
const uint8_t *input, size_t input_len, const uint32_t key[8],
|
||||
uint64_t chunk_counter, uint8_t flags, uint8_t out[2 * BLAKE3_OUT_LEN]
|
||||
) {
|
||||
uint8_t cv_array[MAX_SIMD_DEGREE_OR_2 * BLAKE3_OUT_LEN];
|
||||
size_t num_cvs = blake3_compress_subtree_wide(input, input_len, key, chunk_counter, flags, cv_array);
|
||||
|
||||
// If MAX_SIMD_DEGREE is greater than 2 and there's enough input,
|
||||
// compress_subtree_wide() returns more than 2 chaining values. Condense
|
||||
// them into 2 by forming parent nodes repeatedly.
|
||||
uint8_t out_array[MAX_SIMD_DEGREE_OR_2 * BLAKE3_OUT_LEN / 2];
|
||||
while (num_cvs > 2) {
|
||||
num_cvs =
|
||||
compress_parents_parallel(cv_array, num_cvs, key, flags, out_array);
|
||||
if (num_cvs > 0) {
|
||||
memcpy32(cv_array, out_array);
|
||||
}
|
||||
}
|
||||
memcpy64(out, cv_array);
|
||||
}
|
||||
|
||||
static __inline__ void hasher_init_base(blake3_hasher *self, const uint32_t key[8], uint8_t flags) {
|
||||
memcpy32(self->key, key);
|
||||
chunk_state_init(&self->chunk, key, flags);
|
||||
self->cv_stack_len = 0;
|
||||
}
|
||||
|
||||
void blake3_hasher_init(blake3_hasher *self) {
|
||||
hasher_init_base(self, IV, 0);
|
||||
}
|
||||
|
||||
void blake3_hasher_init_keyed(blake3_hasher *self, const uint8_t key[BLAKE3_KEY_LEN]) {
|
||||
uint32_t key_words[8];
|
||||
load_key_words(key, key_words);
|
||||
hasher_init_base(self, key_words, KEYED_HASH);
|
||||
}
|
||||
|
||||
// As described in hasher_push_cv() below, we do "lazy merging", delaying
|
||||
// merges until right before the next CV is about to be added. This is
|
||||
// different from the reference implementation. Another difference is that we
|
||||
// aren't always merging 1 chunk at a time. Instead, each CV might represent
|
||||
// any power-of-two number of chunks, as long as the smaller-above-larger stack
|
||||
// order is maintained. Instead of the "count the trailing 0-bits" algorithm
|
||||
// described in the spec, we use a "count the total number of 1-bits" variant
|
||||
// that doesn't require us to retain the subtree size of the CV on top of the
|
||||
// stack. The principle is the same: each CV that should remain in the stack is
|
||||
// represented by a 1-bit in the total number of chunks (or bytes) so far.
|
||||
static __inline__ void hasher_merge_cv_stack(blake3_hasher *self, uint64_t total_len) {
|
||||
size_t post_merge_stack_len = (size_t)popcnt(total_len);
|
||||
while (self->cv_stack_len > post_merge_stack_len) {
|
||||
uint8_t *parent_node =
|
||||
&self->cv_stack[(self->cv_stack_len - 2) * BLAKE3_OUT_LEN];
|
||||
output_t output = parent_output(parent_node, self->key, self->chunk.flags);
|
||||
output_chaining_value(&output, parent_node);
|
||||
self->cv_stack_len -= 1;
|
||||
}
|
||||
}
|
||||
|
||||
// In reference_impl.rs, we merge the new CV with existing CVs from the stack
|
||||
// before pushing it. We can do that because we know more input is coming, so
|
||||
// we know none of the merges are root.
|
||||
//
|
||||
// This setting is different. We want to feed as much input as possible to
|
||||
// compress_subtree_wide(), without setting aside anything for the chunk_state.
|
||||
// If the user gives us 64 KiB, we want to parallelize over all 64 KiB at once
|
||||
// as a single subtree, if at all possible.
|
||||
//
|
||||
// This leads to two problems:
|
||||
// 1) This 64 KiB input might be the only call that ever gets made to update.
|
||||
// In this case, the root node of the 64 KiB subtree would be the root node
|
||||
// of the whole tree, and it would need to be ROOT finalized. We can't
|
||||
// compress it until we know.
|
||||
// 2) This 64 KiB input might complete a larger tree, whose root node is
|
||||
// similarly going to be the the root of the whole tree. For example, maybe
|
||||
// we have 196 KiB (that is, 128 + 64) hashed so far. We can't compress the
|
||||
// node at the root of the 256 KiB subtree until we know how to finalize it.
|
||||
//
|
||||
// The second problem is solved with "lazy merging". That is, when we're about
|
||||
// to add a CV to the stack, we don't merge it with anything first, as the
|
||||
// reference impl does. Instead we do merges using the *previous* CV that was
|
||||
// added, which is sitting on top of the stack, and we put the new CV
|
||||
// (unmerged) on top of the stack afterwards. This guarantees that we never
|
||||
// merge the root node until finalize().
|
||||
//
|
||||
// Solving the first problem requires an additional tool,
|
||||
// compress_subtree_to_parent_node(). That function always returns the top
|
||||
// *two* chaining values of the subtree it's compressing. We then do lazy
|
||||
// merging with each of them separately, so that the second CV will always
|
||||
// remain unmerged. (That also helps us support extendable output when we're
|
||||
// hashing an input all-at-once.)
|
||||
static __inline__ void hasher_push_cv(
|
||||
blake3_hasher *self, uint8_t new_cv[BLAKE3_OUT_LEN], uint64_t chunk_counter
|
||||
) {
|
||||
hasher_merge_cv_stack(self, chunk_counter);
|
||||
memcpy32(&self->cv_stack[self->cv_stack_len * BLAKE3_OUT_LEN], new_cv);
|
||||
self->cv_stack_len += 1;
|
||||
}
|
||||
|
||||
void blake3_hasher_update(blake3_hasher *self, const void *input, size_t input_len) {
|
||||
// Explicitly checking for zero avoids causing UB by passing a null pointer
|
||||
// to memcpy. This comes up in practice with things like:
|
||||
// std::vector<uint8_t> v;
|
||||
// blake3_hasher_update(&hasher, v.data(), v.size());
|
||||
if (input_len == 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
const uint8_t *input_bytes = (const uint8_t *)input;
|
||||
|
||||
// If we have some partial chunk bytes in the internal chunk_state, we need
|
||||
// to finish that chunk first.
|
||||
if (chunk_state_len(&self->chunk) > 0) {
|
||||
size_t take = BLAKE3_CHUNK_LEN - chunk_state_len(&self->chunk);
|
||||
if (take > input_len) {
|
||||
take = input_len;
|
||||
}
|
||||
chunk_state_update(&self->chunk, input_bytes, take);
|
||||
input_bytes += take;
|
||||
input_len -= take;
|
||||
// If we've filled the current chunk and there's more coming, finalize this
|
||||
// chunk and proceed. In this case we know it's not the root.
|
||||
if (input_len > 0) {
|
||||
output_t output = chunk_state_output(&self->chunk);
|
||||
uint8_t chunk_cv[32];
|
||||
output_chaining_value(&output, chunk_cv);
|
||||
hasher_push_cv(self, chunk_cv, self->chunk.chunk_counter);
|
||||
chunk_state_reset(&self->chunk, self->key, self->chunk.chunk_counter + 1);
|
||||
} else {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// Now the chunk_state is clear, and we have more input. If there's more than
|
||||
// a single chunk (so, definitely not the root chunk), hash the largest whole
|
||||
// subtree we can, with the full benefits of SIMD (and maybe in the future,
|
||||
// multi-threading) parallelism. Two restrictions:
|
||||
// - The subtree has to be a power-of-2 number of chunks. Only subtrees along
|
||||
// the right edge can be incomplete, and we don't know where the right edge
|
||||
// is going to be until we get to finalize().
|
||||
// - The subtree must evenly divide the total number of chunks up until this
|
||||
// point (if total is not 0). If the current incomplete subtree is only
|
||||
// waiting for 1 more chunk, we can't hash a subtree of 4 chunks. We have
|
||||
// to complete the current subtree first.
|
||||
// Because we might need to break up the input to form powers of 2, or to
|
||||
// evenly divide what we already have, this part runs in a loop.
|
||||
while (input_len > BLAKE3_CHUNK_LEN) {
|
||||
size_t subtree_len = round_down_to_power_of_2(input_len);
|
||||
uint64_t count_so_far = self->chunk.chunk_counter * BLAKE3_CHUNK_LEN;
|
||||
// Shrink the subtree_len until it evenly divides the count so far. We know
|
||||
// that subtree_len itself is a power of 2, so we can use a bitmasking
|
||||
// trick instead of an actual remainder operation. (Note that if the caller
|
||||
// consistently passes power-of-2 inputs of the same size, as is hopefully
|
||||
// typical, this loop condition will always fail, and subtree_len will
|
||||
// always be the full length of the input.)
|
||||
//
|
||||
// An aside: We don't have to shrink subtree_len quite this much. For
|
||||
// example, if count_so_far is 1, we could pass 2 chunks to
|
||||
// compress_subtree_to_parent_node. Since we'll get 2 CVs back, we'll still
|
||||
// get the right answer in the end, and we might get to use 2-way SIMD
|
||||
// parallelism. The problem with this optimization, is that it gets us
|
||||
// stuck always hashing 2 chunks. The total number of chunks will remain
|
||||
// odd, and we'll never graduate to higher degrees of parallelism. See
|
||||
// https://github.com/BLAKE3-team/BLAKE3/issues/69.
|
||||
while ((((uint64_t)(subtree_len - 1)) & count_so_far) != 0) {
|
||||
subtree_len /= 2;
|
||||
}
|
||||
// The shrunken subtree_len might now be 1 chunk long. If so, hash that one
|
||||
// chunk by itself. Otherwise, compress the subtree into a pair of CVs.
|
||||
uint64_t subtree_chunks = subtree_len / BLAKE3_CHUNK_LEN;
|
||||
if (subtree_len <= BLAKE3_CHUNK_LEN) {
|
||||
blake3_chunk_state chunk_state;
|
||||
chunk_state_init(&chunk_state, self->key, self->chunk.flags);
|
||||
chunk_state.chunk_counter = self->chunk.chunk_counter;
|
||||
chunk_state_update(&chunk_state, input_bytes, subtree_len);
|
||||
output_t output = chunk_state_output(&chunk_state);
|
||||
uint8_t cv[BLAKE3_OUT_LEN];
|
||||
output_chaining_value(&output, cv);
|
||||
hasher_push_cv(self, cv, chunk_state.chunk_counter);
|
||||
} else {
|
||||
// This is the high-performance happy path, though getting here depends
|
||||
// on the caller giving us a long enough input.
|
||||
uint8_t cv_pair[2 * BLAKE3_OUT_LEN];
|
||||
compress_subtree_to_parent_node(
|
||||
input_bytes, subtree_len, self->key,
|
||||
self->chunk.chunk_counter, self->chunk.flags, cv_pair
|
||||
);
|
||||
hasher_push_cv(self, cv_pair, self->chunk.chunk_counter);
|
||||
hasher_push_cv(
|
||||
self, &cv_pair[BLAKE3_OUT_LEN], self->chunk.chunk_counter + (subtree_chunks / 2)
|
||||
);
|
||||
}
|
||||
self->chunk.chunk_counter += subtree_chunks;
|
||||
input_bytes += subtree_len;
|
||||
input_len -= subtree_len;
|
||||
}
|
||||
|
||||
// If there's any remaining input less than a full chunk, add it to the chunk
|
||||
// state. In that case, also do a final merge loop to make sure the subtree
|
||||
// stack doesn't contain any unmerged pairs. The remaining input means we
|
||||
// know these merges are non-root. This merge loop isn't strictly necessary
|
||||
// here, because hasher_push_chunk_cv already does its own merge loop, but it
|
||||
// simplifies blake3_hasher_finalize below.
|
||||
if (input_len > 0) {
|
||||
chunk_state_update(&self->chunk, input_bytes, input_len);
|
||||
hasher_merge_cv_stack(self, self->chunk.chunk_counter);
|
||||
}
|
||||
}
|
||||
|
||||
void blake3_hasher_finalize_seek(
|
||||
const blake3_hasher *self, uint64_t seek, uint8_t *out, size_t out_len
|
||||
) {
|
||||
// Explicitly checking for zero avoids causing UB by passing a null pointer
|
||||
// to memcpy. This comes up in practice with things like:
|
||||
// std::vector<uint8_t> v;
|
||||
// blake3_hasher_finalize(&hasher, v.data(), v.size());
|
||||
if (out_len == 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
// If the subtree stack is empty, then the current chunk is the root.
|
||||
if (self->cv_stack_len == 0) {
|
||||
output_t output = chunk_state_output(&self->chunk);
|
||||
output_root_bytes(&output, seek, out, out_len);
|
||||
return;
|
||||
}
|
||||
// If there are any bytes in the chunk state, finalize that chunk and do a
|
||||
// roll-up merge between that chunk hash and every subtree in the stack. In
|
||||
// this case, the extra merge loop at the end of blake3_hasher_update
|
||||
// guarantees that none of the subtrees in the stack need to be merged with
|
||||
// each other first. Otherwise, if there are no bytes in the chunk state,
|
||||
// then the top of the stack is a chunk hash, and we start the merge from
|
||||
// that.
|
||||
output_t output;
|
||||
size_t cvs_remaining;
|
||||
if (chunk_state_len(&self->chunk) > 0) {
|
||||
cvs_remaining = self->cv_stack_len;
|
||||
output = chunk_state_output(&self->chunk);
|
||||
} else {
|
||||
// There are always at least 2 CVs in the stack in this case.
|
||||
cvs_remaining = self->cv_stack_len - 2;
|
||||
output = parent_output(&self->cv_stack[cvs_remaining * 32], self->key, self->chunk.flags);
|
||||
}
|
||||
while (cvs_remaining > 0) {
|
||||
cvs_remaining -= 1;
|
||||
uint8_t parent_block[BLAKE3_BLOCK_LEN];
|
||||
memcpy32(parent_block, &self->cv_stack[cvs_remaining * 32]);
|
||||
output_chaining_value(&output, &parent_block[32]);
|
||||
output = parent_output(parent_block, self->key, self->chunk.flags);
|
||||
}
|
||||
output_root_bytes(&output, seek, out, out_len);
|
||||
}
|
||||
|
||||
void blake3_hasher_finalize(const blake3_hasher *self, uint8_t *out, size_t out_len) {
|
||||
blake3_hasher_finalize_seek(self, 0, out, out_len);
|
||||
}
|
||||
|
||||
blake3_hasher hasher;
|
||||
|
||||
WASM_EXPORT
|
||||
void Hash_Init(uint32_t keyLen) {
|
||||
if (keyLen == 32) {
|
||||
blake3_hasher_init_keyed(&hasher, main_buffer);
|
||||
} else {
|
||||
blake3_hasher_init(&hasher);
|
||||
}
|
||||
}
|
||||
|
||||
WASM_EXPORT
|
||||
void Hash_Update(uint32_t len) {
|
||||
blake3_hasher_update(&hasher, main_buffer, len);
|
||||
}
|
||||
|
||||
/* Add padding and return the message digest. */
|
||||
WASM_EXPORT
|
||||
void Hash_Final(uint32_t digestBytes) {
|
||||
blake3_hasher_finalize(&hasher, main_buffer, digestBytes);
|
||||
}
|
||||
|
||||
WASM_EXPORT
|
||||
const uint32_t STATE_SIZE = sizeof(hasher);
|
||||
|
||||
WASM_EXPORT
|
||||
uint8_t* Hash_GetState() {
|
||||
return (uint8_t*) &hasher;
|
||||
}
|
||||
|
||||
WASM_EXPORT
|
||||
void Hash_Calculate(uint32_t length, uint32_t initParam, uint32_t digestBytes) {
|
||||
Hash_Init(initParam);
|
||||
Hash_Update(length);
|
||||
Hash_Final(digestBytes);
|
||||
}
|
||||
+94
@@ -0,0 +1,94 @@
|
||||
// //////////////////////////////////////////////////////////
|
||||
// Crc32.cpp
|
||||
// Copyright (c) 2011-2019 Stephan Brumme. All rights reserved.
|
||||
// Slicing-by-16 contributed by Bulat Ziganshin
|
||||
// Tableless bytewise CRC contributed by Hagai Gold
|
||||
// see http://create.stephan-brumme.com/disclaimer.html
|
||||
//
|
||||
// Modified for hash-wasm by Dani Biró
|
||||
//
|
||||
|
||||
#define WITH_BUFFER
|
||||
#include "hash-wasm.h"
|
||||
|
||||
#define bswap_32(x) __builtin_bswap32(x)
|
||||
|
||||
alignas(128) static uint32_t crc32_lookup[8][256] = {0};
|
||||
|
||||
void init_lut(uint32_t polynomial) {
|
||||
for (int i = 0; i < 256; ++i) {
|
||||
uint32_t crc = i;
|
||||
for (int j = 0; j < 8; ++j) {
|
||||
crc = (crc >> 1) ^ (-(int32_t)(crc & 1) & polynomial);
|
||||
}
|
||||
crc32_lookup[0][i] = crc;
|
||||
}
|
||||
|
||||
for (int i = 1; i < 256; ++i) {
|
||||
uint32_t lv = crc32_lookup[0][i];
|
||||
for (int j = 1; j < 8; ++j) {
|
||||
lv = (lv >> 8) ^ crc32_lookup[0][lv & 255];
|
||||
crc32_lookup[j][i] = lv;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
uint32_t crc32_lut_initialized_to = 0;
|
||||
uint32_t previous_crc32 = 0;
|
||||
|
||||
WASM_EXPORT
|
||||
void Hash_Init(uint32_t polynomial) {
|
||||
if (crc32_lut_initialized_to != polynomial) {
|
||||
init_lut(polynomial);
|
||||
crc32_lut_initialized_to = polynomial;
|
||||
}
|
||||
|
||||
previous_crc32 = 0;
|
||||
}
|
||||
|
||||
WASM_EXPORT
|
||||
void Hash_Update(uint32_t length) {
|
||||
const uint8_t *data = main_buffer;
|
||||
|
||||
uint32_t crc = ~previous_crc32; // same as previous_crc32 ^ 0xFFFFFFFF
|
||||
const uint32_t *current = (const uint32_t *)data;
|
||||
|
||||
// process eight bytes at once (Slicing-by-8)
|
||||
while (length >= 8) {
|
||||
uint32_t one = *current++ ^ crc;
|
||||
uint32_t two = *current++;
|
||||
crc = crc32_lookup[0][(two >> 24) & 0xFF] ^
|
||||
crc32_lookup[1][(two >> 16) & 0xFF] ^
|
||||
crc32_lookup[2][(two >> 8) & 0xFF] ^ crc32_lookup[3][two & 0xFF] ^
|
||||
crc32_lookup[4][(one >> 24) & 0xFF] ^
|
||||
crc32_lookup[5][(one >> 16) & 0xFF] ^
|
||||
crc32_lookup[6][(one >> 8) & 0xFF] ^ crc32_lookup[7][one & 0xFF];
|
||||
|
||||
length -= 8;
|
||||
}
|
||||
|
||||
const uint8_t *currentChar = (const uint8_t *)current;
|
||||
|
||||
// remaining 1 to 7 bytes (standard algorithm)
|
||||
while (length-- != 0) {
|
||||
crc = (crc >> 8) ^ crc32_lookup[0][(crc & 0xFF) ^ *currentChar++];
|
||||
}
|
||||
|
||||
previous_crc32 = ~crc; // same as crc ^ 0xFFFFFFFF
|
||||
}
|
||||
|
||||
WASM_EXPORT
|
||||
void Hash_Final() { ((uint32_t *)main_buffer)[0] = bswap_32(previous_crc32); }
|
||||
|
||||
WASM_EXPORT
|
||||
const uint32_t STATE_SIZE = sizeof(previous_crc32);
|
||||
|
||||
WASM_EXPORT
|
||||
uint8_t *Hash_GetState() { return (uint8_t *)&previous_crc32; }
|
||||
|
||||
WASM_EXPORT
|
||||
void Hash_Calculate(uint32_t length, uint32_t initParam) {
|
||||
Hash_Init(initParam);
|
||||
Hash_Update(length);
|
||||
Hash_Final();
|
||||
}
|
||||
+86
@@ -0,0 +1,86 @@
|
||||
// Based on crc32.c implementation of Stephan Brumme
|
||||
// Modified for hash-wasm by Dani Biró
|
||||
|
||||
#include <stdint.h>
|
||||
#define WITH_BUFFER
|
||||
#include "hash-wasm.h"
|
||||
|
||||
#define bswap_64(x) __builtin_bswap64(x)
|
||||
|
||||
alignas(128) static uint64_t crc64_lookup[8][256] = {0};
|
||||
|
||||
void init_lut(uint64_t polynomial) {
|
||||
for (int i = 0; i < 256; ++i) {
|
||||
uint64_t crc = i;
|
||||
for (int j = 0; j < 8; ++j) {
|
||||
crc = (crc >> 1) ^ (-(int64_t)(crc & 1) & polynomial);
|
||||
}
|
||||
crc64_lookup[0][i] = crc;
|
||||
}
|
||||
|
||||
for (int i = 1; i < 256; ++i) {
|
||||
uint64_t lv = crc64_lookup[0][i];
|
||||
for (int j = 1; j < 8; ++j) {
|
||||
lv = (lv >> 8) ^ crc64_lookup[0][lv & 255];
|
||||
crc64_lookup[j][i] = lv;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
uint64_t crc64_lut_initialized_to = 0;
|
||||
uint64_t previous_crc64 = 0;
|
||||
|
||||
WASM_EXPORT
|
||||
void Hash_Init() {
|
||||
// polynomial is at the memory object
|
||||
uint64_t polynomial = *((uint64_t *)main_buffer);
|
||||
|
||||
if (crc64_lut_initialized_to != polynomial) {
|
||||
init_lut(polynomial);
|
||||
crc64_lut_initialized_to = polynomial;
|
||||
}
|
||||
|
||||
previous_crc64 = 0;
|
||||
}
|
||||
|
||||
WASM_EXPORT
|
||||
void Hash_Update(uint32_t length) {
|
||||
const uint8_t *data = main_buffer;
|
||||
|
||||
uint64_t crc = ~previous_crc64; // same as previous_crc64 ^ 0xFFFFFFFF
|
||||
const uint64_t *current = (const uint64_t *)data;
|
||||
|
||||
// process eight bytes at once (Slicing-by-8)
|
||||
while (length >= 8) {
|
||||
uint64_t val = *current++ ^ crc;
|
||||
crc = crc64_lookup[0][(val >> 56)] ^ crc64_lookup[1][(val >> 48) & 0xFF] ^
|
||||
crc64_lookup[2][(val >> 40) & 0xFF] ^
|
||||
crc64_lookup[3][(val >> 32) & 0xFF] ^
|
||||
crc64_lookup[4][(val >> 24) & 0xFF] ^
|
||||
crc64_lookup[5][(val >> 16) & 0xFF] ^
|
||||
crc64_lookup[6][(val >> 8) & 0xFF] ^ crc64_lookup[7][val & 0xFF];
|
||||
|
||||
length -= 8;
|
||||
}
|
||||
|
||||
const uint8_t *currentChar = (const uint8_t *)current;
|
||||
|
||||
// remaining 1 to 7 bytes (standard algorithm)
|
||||
while (length-- != 0) {
|
||||
crc = (crc >> 8) ^ crc64_lookup[0][(crc & 0xFF) ^ *currentChar++];
|
||||
}
|
||||
|
||||
previous_crc64 = ~crc;
|
||||
}
|
||||
|
||||
WASM_EXPORT
|
||||
void Hash_Final() { ((uint64_t *)main_buffer)[0] = bswap_64(previous_crc64); }
|
||||
|
||||
WASM_EXPORT
|
||||
const uint32_t STATE_SIZE = sizeof(previous_crc64);
|
||||
|
||||
WASM_EXPORT
|
||||
uint8_t *Hash_GetState() { return (uint8_t *)&previous_crc64; }
|
||||
|
||||
WASM_EXPORT
|
||||
void Hash_Calculate() { return; }
|
||||
+132
@@ -0,0 +1,132 @@
|
||||
#include <stdint.h>
|
||||
#include <stdalign.h>
|
||||
|
||||
#ifndef NULL
|
||||
#define NULL 0
|
||||
#endif
|
||||
|
||||
#ifdef _MSC_VER
|
||||
#define WASM_EXPORT
|
||||
#define __inline__
|
||||
#else
|
||||
#define WASM_EXPORT __attribute__((visibility("default")))
|
||||
#endif
|
||||
|
||||
#ifdef WITH_BUFFER
|
||||
|
||||
#define MAIN_BUFFER_SIZE 16 * 1024
|
||||
alignas(128) uint8_t main_buffer[MAIN_BUFFER_SIZE];
|
||||
|
||||
WASM_EXPORT
|
||||
uint8_t *Hash_GetBuffer() {
|
||||
return main_buffer;
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
// Sometimes LLVM emits these functions during the optimization step
|
||||
// even with -nostdlib -fno-builtin flags
|
||||
static __inline__ void* memcpy(void* dst, const void* src, uint32_t cnt) {
|
||||
uint8_t *destination = dst;
|
||||
const uint8_t *source = src;
|
||||
while (cnt) {
|
||||
*(destination++)= *(source++);
|
||||
--cnt;
|
||||
}
|
||||
return dst;
|
||||
}
|
||||
|
||||
static __inline__ void* memset(void* dst, const uint8_t value, uint32_t cnt) {
|
||||
uint8_t *p = dst;
|
||||
while (cnt--) {
|
||||
*p++ = value;
|
||||
}
|
||||
return dst;
|
||||
}
|
||||
|
||||
static __inline__ void* memcpy2(void* dst, const void* src, uint32_t cnt) {
|
||||
uint64_t *destination64 = dst;
|
||||
const uint64_t *source64 = src;
|
||||
while (cnt >= 8) {
|
||||
*(destination64++)= *(source64++);
|
||||
cnt -= 8;
|
||||
}
|
||||
|
||||
uint8_t *destination = (uint8_t*)destination64;
|
||||
const uint8_t *source = (uint8_t*)source64;
|
||||
while (cnt) {
|
||||
*(destination++)= *(source++);
|
||||
--cnt;
|
||||
}
|
||||
return dst;
|
||||
}
|
||||
|
||||
static __inline__ void memcpy16(void* dst, const void* src) {
|
||||
uint64_t* dst64 = (uint64_t*)dst;
|
||||
uint64_t* src64 = (uint64_t*)src;
|
||||
|
||||
dst64[0] = src64[0];
|
||||
dst64[1] = src64[1];
|
||||
}
|
||||
|
||||
static __inline__ void memcpy32(void* dst, const void* src) {
|
||||
uint64_t* dst64 = (uint64_t*)dst;
|
||||
uint64_t* src64 = (uint64_t*)src;
|
||||
|
||||
#pragma clang loop unroll(full)
|
||||
for (int i = 0; i < 4; i++) {
|
||||
dst64[i] = src64[i];
|
||||
}
|
||||
}
|
||||
|
||||
static __inline__ void memcpy64(void* dst, const void* src) {
|
||||
uint64_t* dst64 = (uint64_t*)dst;
|
||||
uint64_t* src64 = (uint64_t*)src;
|
||||
|
||||
#pragma clang loop unroll(full)
|
||||
for (int i = 0; i < 8; i++) {
|
||||
dst64[i] = src64[i];
|
||||
}
|
||||
}
|
||||
|
||||
static __inline__ uint64_t widen8to64(const uint8_t value) {
|
||||
return value | (value << 8) | (value << 16) | (value << 24);
|
||||
}
|
||||
|
||||
static __inline__ void memset16(void* dst, const uint8_t value) {
|
||||
uint64_t val = widen8to64(value);
|
||||
uint64_t* dst64 = (uint64_t*)dst;
|
||||
|
||||
dst64[0] = val;
|
||||
dst64[1] = val;
|
||||
}
|
||||
|
||||
static __inline__ void memset32(void* dst, const uint8_t value) {
|
||||
uint64_t val = widen8to64(value);
|
||||
uint64_t* dst64 = (uint64_t*)dst;
|
||||
|
||||
#pragma clang loop unroll(full)
|
||||
for (int i = 0; i < 4; i++) {
|
||||
dst64[i] = val;
|
||||
}
|
||||
}
|
||||
|
||||
static __inline__ void memset64(void* dst, const uint8_t value) {
|
||||
uint64_t val = widen8to64(value);
|
||||
uint64_t* dst64 = (uint64_t*)dst;
|
||||
|
||||
#pragma clang loop unroll(full)
|
||||
for (int i = 0; i < 8; i++) {
|
||||
dst64[i] = val;
|
||||
}
|
||||
}
|
||||
|
||||
static __inline__ void memset128(void* dst, const uint8_t value) {
|
||||
uint64_t val = widen8to64(value);
|
||||
uint64_t* dst64 = (uint64_t*)dst;
|
||||
|
||||
#pragma clang loop unroll(full)
|
||||
for (int i = 0; i < 16; i++) {
|
||||
dst64[i] = val;
|
||||
}
|
||||
}
|
||||
+290
@@ -0,0 +1,290 @@
|
||||
/*
|
||||
* This is an OpenSSL-compatible implementation of the RSA Data Security, Inc.
|
||||
* MD4 Message-Digest Algorithm (RFC 1320).
|
||||
*
|
||||
* Homepage:
|
||||
* http://openwall.info/wiki/people/solar/software/public-domain-source-code/md4
|
||||
*
|
||||
* Author:
|
||||
* Alexander Peslyak, better known as Solar Designer <solar at openwall.com>
|
||||
*
|
||||
* This software was written by Alexander Peslyak in 2001. No copyright is
|
||||
* claimed, and the software is hereby placed in the public domain.
|
||||
* In case this attempt to disclaim copyright and place the software in the
|
||||
* public domain is deemed null and void, then the software is
|
||||
* Copyright (c) 2001 Alexander Peslyak and it is hereby released to the
|
||||
* general public under the following terms:
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted.
|
||||
*
|
||||
* There's ABSOLUTELY NO WARRANTY, express or implied.
|
||||
*
|
||||
* (This is a heavily cut-down "BSD license".)
|
||||
*
|
||||
* This differs from Colin Plumb's older public domain implementation in that
|
||||
* no exactly 32-bit integer data type is required (any 32-bit or wider
|
||||
* unsigned integer data type will do), there's no compile-time endianness
|
||||
* configuration, and the function prototypes match OpenSSL's. No code from
|
||||
* Colin Plumb's implementation has been reused; this comment merely compares
|
||||
* the properties of the two independent implementations.
|
||||
*
|
||||
* The primary goals of this implementation are portability and ease of use.
|
||||
* It is meant to be fast, but not as fast as possible. Some known
|
||||
* optimizations are not included to reduce source code size and avoid
|
||||
* compile-time configuration.
|
||||
*
|
||||
* Modified for hash-wasm by Dani Biró
|
||||
*/
|
||||
|
||||
#define WITH_BUFFER
|
||||
#include "hash-wasm.h"
|
||||
|
||||
struct MD4_CTX {
|
||||
uint32_t lo, hi;
|
||||
uint32_t a, b, c, d;
|
||||
uint8_t buffer[64];
|
||||
uint32_t block[16];
|
||||
};
|
||||
|
||||
struct MD4_CTX sctx;
|
||||
struct MD4_CTX *ctx = &sctx;
|
||||
|
||||
/*
|
||||
* The basic MD4 functions.
|
||||
*
|
||||
* F and G are optimized compared to their RFC 1320 definitions, with the
|
||||
* optimization for F borrowed from Colin Plumb's MD5 implementation.
|
||||
*/
|
||||
#define F(x, y, z) ((z) ^ ((x) & ((y) ^ (z))))
|
||||
#define G(x, y, z) (((x) & ((y) | (z))) | ((y) & (z)))
|
||||
#define H(x, y, z) ((x) ^ (y) ^ (z))
|
||||
|
||||
/*
|
||||
* The MD4 transformation for all three rounds.
|
||||
*/
|
||||
#define STEP(f, a, b, c, d, x, s) \
|
||||
(a) += f((b), (c), (d)) + (x); \
|
||||
(a) = (((a) << (s)) | (((a) & 0xffffffff) >> (32 - (s))));
|
||||
|
||||
/*
|
||||
* SET reads 4 input bytes in little-endian byte order and stores them in a
|
||||
* properly aligned word in host byte order.
|
||||
*
|
||||
* The check for little-endian architectures that tolerate unaligned memory
|
||||
* accesses is just an optimization. Nothing will break if it fails to detect
|
||||
* a suitable architecture.
|
||||
*
|
||||
* Unfortunately, this optimization may be a C strict aliasing rules violation
|
||||
* if the caller's data buffer has effective type that cannot be aliased by
|
||||
* uint32_t. In practice, this problem may occur if these MD4 routines are
|
||||
* inlined into a calling function, or with future and dangerously advanced
|
||||
* link-time optimizations. For the time being, keeping these MD4 routines in
|
||||
* their own translation unit avoids the problem.
|
||||
*/
|
||||
|
||||
#define SET(n) (*(uint32_t *)&ptr[(n)*4])
|
||||
#define GET(n) SET(n)
|
||||
|
||||
/*
|
||||
* This processes one or more 64-byte data blocks, but does NOT update the bit
|
||||
* counters. There are no alignment requirements.
|
||||
*/
|
||||
static const void *body(const void *data, uint32_t size) {
|
||||
const uint8_t *ptr;
|
||||
uint32_t a, b, c, d;
|
||||
uint32_t saved_a, saved_b, saved_c, saved_d;
|
||||
const uint32_t ac1 = 0x5a827999, ac2 = 0x6ed9eba1;
|
||||
|
||||
ptr = (const uint8_t *)data;
|
||||
|
||||
a = ctx->a;
|
||||
b = ctx->b;
|
||||
c = ctx->c;
|
||||
d = ctx->d;
|
||||
|
||||
do {
|
||||
saved_a = a;
|
||||
saved_b = b;
|
||||
saved_c = c;
|
||||
saved_d = d;
|
||||
|
||||
/* Round 1 */
|
||||
STEP(F, a, b, c, d, SET(0), 3)
|
||||
STEP(F, d, a, b, c, SET(1), 7)
|
||||
STEP(F, c, d, a, b, SET(2), 11)
|
||||
STEP(F, b, c, d, a, SET(3), 19)
|
||||
STEP(F, a, b, c, d, SET(4), 3)
|
||||
STEP(F, d, a, b, c, SET(5), 7)
|
||||
STEP(F, c, d, a, b, SET(6), 11)
|
||||
STEP(F, b, c, d, a, SET(7), 19)
|
||||
STEP(F, a, b, c, d, SET(8), 3)
|
||||
STEP(F, d, a, b, c, SET(9), 7)
|
||||
STEP(F, c, d, a, b, SET(10), 11)
|
||||
STEP(F, b, c, d, a, SET(11), 19)
|
||||
STEP(F, a, b, c, d, SET(12), 3)
|
||||
STEP(F, d, a, b, c, SET(13), 7)
|
||||
STEP(F, c, d, a, b, SET(14), 11)
|
||||
STEP(F, b, c, d, a, SET(15), 19)
|
||||
|
||||
/* Round 2 */
|
||||
STEP(G, a, b, c, d, GET(0) + ac1, 3)
|
||||
STEP(G, d, a, b, c, GET(4) + ac1, 5)
|
||||
STEP(G, c, d, a, b, GET(8) + ac1, 9)
|
||||
STEP(G, b, c, d, a, GET(12) + ac1, 13)
|
||||
STEP(G, a, b, c, d, GET(1) + ac1, 3)
|
||||
STEP(G, d, a, b, c, GET(5) + ac1, 5)
|
||||
STEP(G, c, d, a, b, GET(9) + ac1, 9)
|
||||
STEP(G, b, c, d, a, GET(13) + ac1, 13)
|
||||
STEP(G, a, b, c, d, GET(2) + ac1, 3)
|
||||
STEP(G, d, a, b, c, GET(6) + ac1, 5)
|
||||
STEP(G, c, d, a, b, GET(10) + ac1, 9)
|
||||
STEP(G, b, c, d, a, GET(14) + ac1, 13)
|
||||
STEP(G, a, b, c, d, GET(3) + ac1, 3)
|
||||
STEP(G, d, a, b, c, GET(7) + ac1, 5)
|
||||
STEP(G, c, d, a, b, GET(11) + ac1, 9)
|
||||
STEP(G, b, c, d, a, GET(15) + ac1, 13)
|
||||
|
||||
/* Round 3 */
|
||||
STEP(H, a, b, c, d, GET(0) + ac2, 3)
|
||||
STEP(H, d, a, b, c, GET(8) + ac2, 9)
|
||||
STEP(H, c, d, a, b, GET(4) + ac2, 11)
|
||||
STEP(H, b, c, d, a, GET(12) + ac2, 15)
|
||||
STEP(H, a, b, c, d, GET(2) + ac2, 3)
|
||||
STEP(H, d, a, b, c, GET(10) + ac2, 9)
|
||||
STEP(H, c, d, a, b, GET(6) + ac2, 11)
|
||||
STEP(H, b, c, d, a, GET(14) + ac2, 15)
|
||||
STEP(H, a, b, c, d, GET(1) + ac2, 3)
|
||||
STEP(H, d, a, b, c, GET(9) + ac2, 9)
|
||||
STEP(H, c, d, a, b, GET(5) + ac2, 11)
|
||||
STEP(H, b, c, d, a, GET(13) + ac2, 15)
|
||||
STEP(H, a, b, c, d, GET(3) + ac2, 3)
|
||||
STEP(H, d, a, b, c, GET(11) + ac2, 9)
|
||||
STEP(H, c, d, a, b, GET(7) + ac2, 11)
|
||||
STEP(H, b, c, d, a, GET(15) + ac2, 15)
|
||||
|
||||
a += saved_a;
|
||||
b += saved_b;
|
||||
c += saved_c;
|
||||
d += saved_d;
|
||||
|
||||
ptr += 64;
|
||||
} while (size -= 64);
|
||||
|
||||
ctx->a = a;
|
||||
ctx->b = b;
|
||||
ctx->c = c;
|
||||
ctx->d = d;
|
||||
|
||||
return ptr;
|
||||
}
|
||||
|
||||
WASM_EXPORT
|
||||
void Hash_Init() {
|
||||
ctx->a = 0x67452301;
|
||||
ctx->b = 0xefcdab89;
|
||||
ctx->c = 0x98badcfe;
|
||||
ctx->d = 0x10325476;
|
||||
|
||||
ctx->lo = 0;
|
||||
ctx->hi = 0;
|
||||
}
|
||||
|
||||
WASM_EXPORT
|
||||
void Hash_Update(uint32_t size) {
|
||||
const uint8_t *data = main_buffer;
|
||||
uint32_t saved_lo;
|
||||
uint32_t used, available;
|
||||
|
||||
saved_lo = ctx->lo;
|
||||
if ((ctx->lo = (saved_lo + size) & 0x1fffffff) < saved_lo) {
|
||||
ctx->hi++;
|
||||
}
|
||||
ctx->hi += size >> 29;
|
||||
|
||||
used = saved_lo & 0x3f;
|
||||
|
||||
if (used) {
|
||||
available = 64 - used;
|
||||
|
||||
if (size < available) {
|
||||
for (uint32_t i = 0; i < size; i++) {
|
||||
ctx->buffer[used + i] = data[i];
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
for (uint32_t i = 0; i < available; i++) {
|
||||
ctx->buffer[used + i] = data[i];
|
||||
}
|
||||
data = (const uint8_t *)data + available;
|
||||
size -= available;
|
||||
body(ctx->buffer, 64);
|
||||
}
|
||||
|
||||
if (size >= 64) {
|
||||
data = body(data, size & ~(uint32_t)0x3f);
|
||||
size &= 0x3f;
|
||||
}
|
||||
|
||||
for (uint32_t i = 0; i < size; i++) {
|
||||
ctx->buffer[i] = data[i];
|
||||
}
|
||||
}
|
||||
|
||||
#define OUT(dst, src) \
|
||||
(dst)[0] = (uint8_t)(src); \
|
||||
(dst)[1] = (uint8_t)((src) >> 8); \
|
||||
(dst)[2] = (uint8_t)((src) >> 16); \
|
||||
(dst)[3] = (uint8_t)((src) >> 24);
|
||||
|
||||
WASM_EXPORT
|
||||
void Hash_Final() {
|
||||
uint8_t *result = main_buffer;
|
||||
uint32_t used, available;
|
||||
|
||||
used = ctx->lo & 0x3f;
|
||||
|
||||
ctx->buffer[used++] = 0x80;
|
||||
|
||||
available = 64 - used;
|
||||
|
||||
if (available < 8) {
|
||||
for (int i = 0; i < available; i++) {
|
||||
ctx->buffer[used + i] = 0;
|
||||
}
|
||||
body(ctx->buffer, 64);
|
||||
used = 0;
|
||||
available = 64;
|
||||
}
|
||||
|
||||
for (int i = available - 9; i >= 0; i--) {
|
||||
ctx->buffer[used + i] = 0;
|
||||
}
|
||||
|
||||
ctx->lo <<= 3;
|
||||
OUT(&ctx->buffer[56], ctx->lo)
|
||||
OUT(&ctx->buffer[60], ctx->hi)
|
||||
|
||||
body(ctx->buffer, 64);
|
||||
|
||||
OUT(&result[0], ctx->a)
|
||||
OUT(&result[4], ctx->b)
|
||||
OUT(&result[8], ctx->c)
|
||||
OUT(&result[12], ctx->d)
|
||||
}
|
||||
|
||||
WASM_EXPORT
|
||||
const uint32_t STATE_SIZE = sizeof(*ctx);
|
||||
|
||||
WASM_EXPORT
|
||||
uint8_t* Hash_GetState() {
|
||||
return (uint8_t*) ctx;
|
||||
}
|
||||
|
||||
WASM_EXPORT
|
||||
void Hash_Calculate(uint32_t length) {
|
||||
Hash_Init();
|
||||
Hash_Update(length);
|
||||
Hash_Final();
|
||||
}
|
||||
+311
@@ -0,0 +1,311 @@
|
||||
/*
|
||||
* This is an OpenSSL-compatible implementation of the RSA Data Security, Inc.
|
||||
* MD5 Message-Digest Algorithm (RFC 1321).
|
||||
*
|
||||
* Homepage:
|
||||
* http://openwall.info/wiki/people/solar/software/public-domain-source-code/md5
|
||||
*
|
||||
* Author:
|
||||
* Alexander Peslyak, better known as Solar Designer <solar at openwall.com>
|
||||
*
|
||||
* This software was written by Alexander Peslyak in 2001. No copyright is
|
||||
* claimed, and the software is hereby placed in the public domain.
|
||||
* In case this attempt to disclaim copyright and place the software in the
|
||||
* public domain is deemed null and void, then the software is
|
||||
* Copyright (c) 2001 Alexander Peslyak and it is hereby released to the
|
||||
* general public under the following terms:
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted.
|
||||
*
|
||||
* There's ABSOLUTELY NO WARRANTY, express or implied.
|
||||
*
|
||||
* (This is a heavily cut-down "BSD license".)
|
||||
*
|
||||
* This differs from Colin Plumb's older public domain implementation in that
|
||||
* no exactly 32-bit integer data type is required (any 32-bit or wider
|
||||
* unsigned integer data type will do), there's no compile-time endianness
|
||||
* configuration, and the function prototypes match OpenSSL's. No code from
|
||||
* Colin Plumb's implementation has been reused; this comment merely compares
|
||||
* the properties of the two independent implementations.
|
||||
*
|
||||
* The primary goals of this implementation are portability and ease of use.
|
||||
* It is meant to be fast, but not as fast as possible. Some known
|
||||
* optimizations are not included to reduce source code size and avoid
|
||||
* compile-time configuration.
|
||||
*
|
||||
* Modified for hash-wasm by Dani Biró
|
||||
*/
|
||||
|
||||
#define WITH_BUFFER
|
||||
#include "hash-wasm.h"
|
||||
|
||||
struct MD5_CTX {
|
||||
uint32_t lo, hi;
|
||||
uint32_t a, b, c, d;
|
||||
uint8_t buffer[64];
|
||||
uint32_t block[16];
|
||||
};
|
||||
|
||||
struct MD5_CTX sctx;
|
||||
struct MD5_CTX *ctx = &sctx;
|
||||
|
||||
/*
|
||||
* The basic MD5 functions.
|
||||
*
|
||||
* F and G are optimized compared to their RFC 1321 definitions for
|
||||
* architectures that lack an AND-NOT instruction, just like in Colin Plumb's
|
||||
* implementation.
|
||||
*/
|
||||
#define F(x, y, z) ((z) ^ ((x) & ((y) ^ (z))))
|
||||
#define G(x, y, z) ((y) ^ ((z) & ((x) ^ (y))))
|
||||
#define H(x, y, z) (((x) ^ (y)) ^ (z))
|
||||
#define H2(x, y, z) ((x) ^ ((y) ^ (z)))
|
||||
#define I(x, y, z) ((y) ^ ((x) | ~(z)))
|
||||
|
||||
/*
|
||||
* The MD5 transformation for all four rounds.
|
||||
*/
|
||||
#define STEP(f, a, b, c, d, x, t, s) \
|
||||
(a) += f((b), (c), (d)) + (x) + (t); \
|
||||
(a) = (((a) << (s)) | (((a)&0xffffffff) >> (32 - (s)))); \
|
||||
(a) += (b);
|
||||
|
||||
/*
|
||||
* SET reads 4 input bytes in little-endian byte order and stores them in a
|
||||
* properly aligned word in host byte order.
|
||||
*
|
||||
* The check for little-endian architectures that tolerate unaligned memory
|
||||
* accesses is just an optimization. Nothing will break if it fails to detect
|
||||
* a suitable architecture.
|
||||
*
|
||||
* Unfortunately, this optimization may be a C strict aliasing rules violation
|
||||
* if the caller's data buffer has effective type that cannot be aliased by
|
||||
* uint32_t. In practice, this problem may occur if these MD5 routines are
|
||||
* inlined into a calling function, or with future and dangerously advanced
|
||||
* link-time optimizations. For the time being, keeping these MD5 routines in
|
||||
* their own translation unit avoids the problem.
|
||||
*/
|
||||
|
||||
#define SET(n) (*(uint32_t *)&ptr[(n)*4])
|
||||
#define GET(n) SET(n)
|
||||
|
||||
/*
|
||||
* This processes one or more 64-byte data blocks, but does NOT update the bit
|
||||
* counters. There are no alignment requirements.
|
||||
*/
|
||||
static const void *body(const void *data, uint32_t size) {
|
||||
const uint8_t *ptr;
|
||||
uint32_t a, b, c, d;
|
||||
uint32_t saved_a, saved_b, saved_c, saved_d;
|
||||
|
||||
ptr = (const uint8_t *)data;
|
||||
|
||||
a = ctx->a;
|
||||
b = ctx->b;
|
||||
c = ctx->c;
|
||||
d = ctx->d;
|
||||
|
||||
do {
|
||||
saved_a = a;
|
||||
saved_b = b;
|
||||
saved_c = c;
|
||||
saved_d = d;
|
||||
|
||||
/* Round 1 */
|
||||
STEP(F, a, b, c, d, SET(0), 0xd76aa478, 7)
|
||||
STEP(F, d, a, b, c, SET(1), 0xe8c7b756, 12)
|
||||
STEP(F, c, d, a, b, SET(2), 0x242070db, 17)
|
||||
STEP(F, b, c, d, a, SET(3), 0xc1bdceee, 22)
|
||||
STEP(F, a, b, c, d, SET(4), 0xf57c0faf, 7)
|
||||
STEP(F, d, a, b, c, SET(5), 0x4787c62a, 12)
|
||||
STEP(F, c, d, a, b, SET(6), 0xa8304613, 17)
|
||||
STEP(F, b, c, d, a, SET(7), 0xfd469501, 22)
|
||||
STEP(F, a, b, c, d, SET(8), 0x698098d8, 7)
|
||||
STEP(F, d, a, b, c, SET(9), 0x8b44f7af, 12)
|
||||
STEP(F, c, d, a, b, SET(10), 0xffff5bb1, 17)
|
||||
STEP(F, b, c, d, a, SET(11), 0x895cd7be, 22)
|
||||
STEP(F, a, b, c, d, SET(12), 0x6b901122, 7)
|
||||
STEP(F, d, a, b, c, SET(13), 0xfd987193, 12)
|
||||
STEP(F, c, d, a, b, SET(14), 0xa679438e, 17)
|
||||
STEP(F, b, c, d, a, SET(15), 0x49b40821, 22)
|
||||
|
||||
/* Round 2 */
|
||||
STEP(G, a, b, c, d, GET(1), 0xf61e2562, 5)
|
||||
STEP(G, d, a, b, c, GET(6), 0xc040b340, 9)
|
||||
STEP(G, c, d, a, b, GET(11), 0x265e5a51, 14)
|
||||
STEP(G, b, c, d, a, GET(0), 0xe9b6c7aa, 20)
|
||||
STEP(G, a, b, c, d, GET(5), 0xd62f105d, 5)
|
||||
STEP(G, d, a, b, c, GET(10), 0x02441453, 9)
|
||||
STEP(G, c, d, a, b, GET(15), 0xd8a1e681, 14)
|
||||
STEP(G, b, c, d, a, GET(4), 0xe7d3fbc8, 20)
|
||||
STEP(G, a, b, c, d, GET(9), 0x21e1cde6, 5)
|
||||
STEP(G, d, a, b, c, GET(14), 0xc33707d6, 9)
|
||||
STEP(G, c, d, a, b, GET(3), 0xf4d50d87, 14)
|
||||
STEP(G, b, c, d, a, GET(8), 0x455a14ed, 20)
|
||||
STEP(G, a, b, c, d, GET(13), 0xa9e3e905, 5)
|
||||
STEP(G, d, a, b, c, GET(2), 0xfcefa3f8, 9)
|
||||
STEP(G, c, d, a, b, GET(7), 0x676f02d9, 14)
|
||||
STEP(G, b, c, d, a, GET(12), 0x8d2a4c8a, 20)
|
||||
|
||||
/* Round 3 */
|
||||
STEP(H, a, b, c, d, GET(5), 0xfffa3942, 4)
|
||||
STEP(H2, d, a, b, c, GET(8), 0x8771f681, 11)
|
||||
STEP(H, c, d, a, b, GET(11), 0x6d9d6122, 16)
|
||||
STEP(H2, b, c, d, a, GET(14), 0xfde5380c, 23)
|
||||
STEP(H, a, b, c, d, GET(1), 0xa4beea44, 4)
|
||||
STEP(H2, d, a, b, c, GET(4), 0x4bdecfa9, 11)
|
||||
STEP(H, c, d, a, b, GET(7), 0xf6bb4b60, 16)
|
||||
STEP(H2, b, c, d, a, GET(10), 0xbebfbc70, 23)
|
||||
STEP(H, a, b, c, d, GET(13), 0x289b7ec6, 4)
|
||||
STEP(H2, d, a, b, c, GET(0), 0xeaa127fa, 11)
|
||||
STEP(H, c, d, a, b, GET(3), 0xd4ef3085, 16)
|
||||
STEP(H2, b, c, d, a, GET(6), 0x04881d05, 23)
|
||||
STEP(H, a, b, c, d, GET(9), 0xd9d4d039, 4)
|
||||
STEP(H2, d, a, b, c, GET(12), 0xe6db99e5, 11)
|
||||
STEP(H, c, d, a, b, GET(15), 0x1fa27cf8, 16)
|
||||
STEP(H2, b, c, d, a, GET(2), 0xc4ac5665, 23)
|
||||
|
||||
/* Round 4 */
|
||||
STEP(I, a, b, c, d, GET(0), 0xf4292244, 6)
|
||||
STEP(I, d, a, b, c, GET(7), 0x432aff97, 10)
|
||||
STEP(I, c, d, a, b, GET(14), 0xab9423a7, 15)
|
||||
STEP(I, b, c, d, a, GET(5), 0xfc93a039, 21)
|
||||
STEP(I, a, b, c, d, GET(12), 0x655b59c3, 6)
|
||||
STEP(I, d, a, b, c, GET(3), 0x8f0ccc92, 10)
|
||||
STEP(I, c, d, a, b, GET(10), 0xffeff47d, 15)
|
||||
STEP(I, b, c, d, a, GET(1), 0x85845dd1, 21)
|
||||
STEP(I, a, b, c, d, GET(8), 0x6fa87e4f, 6)
|
||||
STEP(I, d, a, b, c, GET(15), 0xfe2ce6e0, 10)
|
||||
STEP(I, c, d, a, b, GET(6), 0xa3014314, 15)
|
||||
STEP(I, b, c, d, a, GET(13), 0x4e0811a1, 21)
|
||||
STEP(I, a, b, c, d, GET(4), 0xf7537e82, 6)
|
||||
STEP(I, d, a, b, c, GET(11), 0xbd3af235, 10)
|
||||
STEP(I, c, d, a, b, GET(2), 0x2ad7d2bb, 15)
|
||||
STEP(I, b, c, d, a, GET(9), 0xeb86d391, 21)
|
||||
|
||||
a += saved_a;
|
||||
b += saved_b;
|
||||
c += saved_c;
|
||||
d += saved_d;
|
||||
|
||||
ptr += 64;
|
||||
} while (size -= 64);
|
||||
|
||||
ctx->a = a;
|
||||
ctx->b = b;
|
||||
ctx->c = c;
|
||||
ctx->d = d;
|
||||
|
||||
return ptr;
|
||||
}
|
||||
|
||||
WASM_EXPORT
|
||||
void Hash_Init() {
|
||||
ctx->a = 0x67452301;
|
||||
ctx->b = 0xefcdab89;
|
||||
ctx->c = 0x98badcfe;
|
||||
ctx->d = 0x10325476;
|
||||
|
||||
ctx->lo = 0;
|
||||
ctx->hi = 0;
|
||||
}
|
||||
|
||||
WASM_EXPORT
|
||||
void Hash_Update(uint32_t size) {
|
||||
const uint8_t *data = main_buffer;
|
||||
uint32_t saved_lo;
|
||||
uint32_t used, available;
|
||||
|
||||
saved_lo = ctx->lo;
|
||||
if ((ctx->lo = (saved_lo + size) & 0x1fffffff) < saved_lo) {
|
||||
ctx->hi++;
|
||||
}
|
||||
ctx->hi += size >> 29;
|
||||
|
||||
used = saved_lo & 0x3f;
|
||||
|
||||
if (used) {
|
||||
available = 64 - used;
|
||||
|
||||
if (size < available) {
|
||||
for (uint32_t i = 0; i < size; i++) {
|
||||
ctx->buffer[used + i] = data[i];
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
for (uint32_t i = 0; i < available; i++) {
|
||||
ctx->buffer[used + i] = data[i];
|
||||
}
|
||||
data = (const uint8_t *)data + available;
|
||||
size -= available;
|
||||
body(ctx->buffer, 64);
|
||||
}
|
||||
|
||||
if (size >= 64) {
|
||||
data = body(data, size & ~(uint32_t)0x3f);
|
||||
size &= 0x3f;
|
||||
}
|
||||
|
||||
for (uint32_t i = 0; i < size; i++) {
|
||||
ctx->buffer[i] = data[i];
|
||||
}
|
||||
}
|
||||
|
||||
#define OUT(dst, src) \
|
||||
(dst)[0] = (uint8_t)(src); \
|
||||
(dst)[1] = (uint8_t)((src) >> 8); \
|
||||
(dst)[2] = (uint8_t)((src) >> 16); \
|
||||
(dst)[3] = (uint8_t)((src) >> 24);
|
||||
|
||||
WASM_EXPORT
|
||||
void Hash_Final() {
|
||||
uint8_t *result = main_buffer;
|
||||
uint32_t used, available;
|
||||
|
||||
used = ctx->lo & 0x3f;
|
||||
|
||||
ctx->buffer[used++] = 0x80;
|
||||
|
||||
available = 64 - used;
|
||||
|
||||
if (available < 8) {
|
||||
for (int i = 0; i < available; i++) {
|
||||
ctx->buffer[used + i] = 0;
|
||||
}
|
||||
body(ctx->buffer, 64);
|
||||
used = 0;
|
||||
available = 64;
|
||||
}
|
||||
|
||||
for (int i = available - 9; i >= 0; i--) {
|
||||
ctx->buffer[used + i] = 0;
|
||||
}
|
||||
|
||||
ctx->lo <<= 3;
|
||||
OUT(&ctx->buffer[56], ctx->lo)
|
||||
OUT(&ctx->buffer[60], ctx->hi)
|
||||
|
||||
body(ctx->buffer, 64);
|
||||
|
||||
OUT(&result[0], ctx->a)
|
||||
OUT(&result[4], ctx->b)
|
||||
OUT(&result[8], ctx->c)
|
||||
OUT(&result[12], ctx->d)
|
||||
}
|
||||
|
||||
WASM_EXPORT
|
||||
const uint32_t STATE_SIZE = sizeof(*ctx);
|
||||
|
||||
WASM_EXPORT
|
||||
uint8_t* Hash_GetState() {
|
||||
return (uint8_t*) ctx;
|
||||
}
|
||||
|
||||
WASM_EXPORT
|
||||
void Hash_Calculate(uint32_t length) {
|
||||
Hash_Init();
|
||||
Hash_Update(length);
|
||||
Hash_Final();
|
||||
}
|
||||
+308
@@ -0,0 +1,308 @@
|
||||
/*
|
||||
* RIPE MD-160 implementation
|
||||
*
|
||||
* Copyright (C) 2006-2015, ARM Limited, All Rights Reserved
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License"); you may
|
||||
* not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*
|
||||
* This file is part of mbed TLS (https://tls.mbed.org)
|
||||
*
|
||||
* Modified for hash-wasm by Dani Biró
|
||||
*/
|
||||
|
||||
/*
|
||||
* The RIPEMD-160 algorithm was designed by RIPE in 1996
|
||||
* http://homes.esat.kuleuven.be/~bosselae/ripemd160.html
|
||||
* http://ehash.iaik.tugraz.at/wiki/RIPEMD-160
|
||||
*/
|
||||
|
||||
#define WITH_BUFFER
|
||||
#include "hash-wasm.h"
|
||||
|
||||
#define RIPEMD160_BLOCK_LENGTH 64
|
||||
#define RIPEMD160_DIGEST_LENGTH 20
|
||||
|
||||
struct RIPEMD160_CTX {
|
||||
uint32_t total[2];
|
||||
uint32_t state[5];
|
||||
uint8_t buffer[RIPEMD160_BLOCK_LENGTH];
|
||||
};
|
||||
|
||||
struct RIPEMD160_CTX sctx;
|
||||
struct RIPEMD160_CTX* ctx = &sctx;
|
||||
|
||||
|
||||
WASM_EXPORT
|
||||
void Hash_Init() {
|
||||
ctx->total[0] = 0;
|
||||
ctx->total[1] = 0;
|
||||
ctx->state[0] = 0x67452301;
|
||||
ctx->state[1] = 0xEFCDAB89;
|
||||
ctx->state[2] = 0x98BADCFE;
|
||||
ctx->state[3] = 0x10325476;
|
||||
ctx->state[4] = 0xC3D2E1F0;
|
||||
}
|
||||
|
||||
void ripemd160_process(const uint8_t data[RIPEMD160_BLOCK_LENGTH]) {
|
||||
uint32_t A, B, C, D, E, Ap, Bp, Cp, Dp, Ep, X[16];
|
||||
|
||||
#pragma clang loop unroll(full)
|
||||
for (uint8_t i = 0; i < 16; i++) {
|
||||
X[i] = ((uint32_t*)data)[i];
|
||||
}
|
||||
|
||||
A = Ap = ctx->state[0];
|
||||
B = Bp = ctx->state[1];
|
||||
C = Cp = ctx->state[2];
|
||||
D = Dp = ctx->state[3];
|
||||
E = Ep = ctx->state[4];
|
||||
|
||||
#define F1(x, y, z) (x ^ y ^ z)
|
||||
#define F2(x, y, z) ((x & y) | (~x & z))
|
||||
#define F3(x, y, z) ((x | ~y) ^ z)
|
||||
#define F4(x, y, z) ((x & z) | (y & ~z))
|
||||
#define F5(x, y, z) (x ^ (y | ~z))
|
||||
|
||||
#define S(x, n) ((x << n) | (x >> (32 - n)))
|
||||
|
||||
#define P(a, b, c, d, e, r, s, f, k) \
|
||||
a += f(b, c, d) + X[r] + k; \
|
||||
a = S(a, s) + e; \
|
||||
c = S(c, 10);
|
||||
|
||||
#define P2(a, b, c, d, e, r, s, rp, sp) \
|
||||
P(a, b, c, d, e, r, s, F, K); \
|
||||
P(a ## p, b ## p, c ## p, d ## p, e ## p, rp, sp, Fp, Kp);
|
||||
|
||||
#define F F1
|
||||
#define K 0x00000000
|
||||
#define Fp F5
|
||||
#define Kp 0x50A28BE6
|
||||
P2(A, B, C, D, E, 0, 11, 5, 8);
|
||||
P2(E, A, B, C, D, 1, 14, 14, 9);
|
||||
P2(D, E, A, B, C, 2, 15, 7, 9);
|
||||
P2(C, D, E, A, B, 3, 12, 0, 11);
|
||||
P2(B, C, D, E, A, 4, 5, 9, 13);
|
||||
P2(A, B, C, D, E, 5, 8, 2, 15);
|
||||
P2(E, A, B, C, D, 6, 7, 11, 15);
|
||||
P2(D, E, A, B, C, 7, 9, 4, 5);
|
||||
P2(C, D, E, A, B, 8, 11, 13, 7);
|
||||
P2(B, C, D, E, A, 9, 13, 6, 7);
|
||||
P2(A, B, C, D, E, 10, 14, 15, 8);
|
||||
P2(E, A, B, C, D, 11, 15, 8, 11);
|
||||
P2(D, E, A, B, C, 12, 6, 1, 14);
|
||||
P2(C, D, E, A, B, 13, 7, 10, 14);
|
||||
P2(B, C, D, E, A, 14, 9, 3, 12);
|
||||
P2(A, B, C, D, E, 15, 8, 12, 6);
|
||||
#undef F
|
||||
#undef K
|
||||
#undef Fp
|
||||
#undef Kp
|
||||
|
||||
#define F F2
|
||||
#define K 0x5A827999
|
||||
#define Fp F4
|
||||
#define Kp 0x5C4DD124
|
||||
P2(E, A, B, C, D, 7, 7, 6, 9);
|
||||
P2(D, E, A, B, C, 4, 6, 11, 13);
|
||||
P2(C, D, E, A, B, 13, 8, 3, 15);
|
||||
P2(B, C, D, E, A, 1, 13, 7, 7);
|
||||
P2(A, B, C, D, E, 10, 11, 0, 12);
|
||||
P2(E, A, B, C, D, 6, 9, 13, 8);
|
||||
P2(D, E, A, B, C, 15, 7, 5, 9);
|
||||
P2(C, D, E, A, B, 3, 15, 10, 11);
|
||||
P2(B, C, D, E, A, 12, 7, 14, 7);
|
||||
P2(A, B, C, D, E, 0, 12, 15, 7);
|
||||
P2(E, A, B, C, D, 9, 15, 8, 12);
|
||||
P2(D, E, A, B, C, 5, 9, 12, 7);
|
||||
P2(C, D, E, A, B, 2, 11, 4, 6);
|
||||
P2(B, C, D, E, A, 14, 7, 9, 15);
|
||||
P2(A, B, C, D, E, 11, 13, 1, 13);
|
||||
P2(E, A, B, C, D, 8, 12, 2, 11);
|
||||
#undef F
|
||||
#undef K
|
||||
#undef Fp
|
||||
#undef Kp
|
||||
|
||||
#define F F3
|
||||
#define K 0x6ED9EBA1
|
||||
#define Fp F3
|
||||
#define Kp 0x6D703EF3
|
||||
P2(D, E, A, B, C, 3, 11, 15, 9);
|
||||
P2(C, D, E, A, B, 10, 13, 5, 7);
|
||||
P2(B, C, D, E, A, 14, 6, 1, 15);
|
||||
P2(A, B, C, D, E, 4, 7, 3, 11);
|
||||
P2(E, A, B, C, D, 9, 14, 7, 8);
|
||||
P2(D, E, A, B, C, 15, 9, 14, 6);
|
||||
P2(C, D, E, A, B, 8, 13, 6, 6);
|
||||
P2(B, C, D, E, A, 1, 15, 9, 14);
|
||||
P2(A, B, C, D, E, 2, 14, 11, 12);
|
||||
P2(E, A, B, C, D, 7, 8, 8, 13);
|
||||
P2(D, E, A, B, C, 0, 13, 12, 5);
|
||||
P2(C, D, E, A, B, 6, 6, 2, 14);
|
||||
P2(B, C, D, E, A, 13, 5, 10, 13);
|
||||
P2(A, B, C, D, E, 11, 12, 0, 13);
|
||||
P2(E, A, B, C, D, 5, 7, 4, 7);
|
||||
P2(D, E, A, B, C, 12, 5, 13, 5);
|
||||
#undef F
|
||||
#undef K
|
||||
#undef Fp
|
||||
#undef Kp
|
||||
|
||||
#define F F4
|
||||
#define K 0x8F1BBCDC
|
||||
#define Fp F2
|
||||
#define Kp 0x7A6D76E9
|
||||
P2(C, D, E, A, B, 1, 11, 8, 15);
|
||||
P2(B, C, D, E, A, 9, 12, 6, 5);
|
||||
P2(A, B, C, D, E, 11, 14, 4, 8);
|
||||
P2(E, A, B, C, D, 10, 15, 1, 11);
|
||||
P2(D, E, A, B, C, 0, 14, 3, 14);
|
||||
P2(C, D, E, A, B, 8, 15, 11, 14);
|
||||
P2(B, C, D, E, A, 12, 9, 15, 6);
|
||||
P2(A, B, C, D, E, 4, 8, 0, 14);
|
||||
P2(E, A, B, C, D, 13, 9, 5, 6);
|
||||
P2(D, E, A, B, C, 3, 14, 12, 9);
|
||||
P2(C, D, E, A, B, 7, 5, 2, 12);
|
||||
P2(B, C, D, E, A, 15, 6, 13, 9);
|
||||
P2(A, B, C, D, E, 14, 8, 9, 12);
|
||||
P2(E, A, B, C, D, 5, 6, 7, 5);
|
||||
P2(D, E, A, B, C, 6, 5, 10, 15);
|
||||
P2(C, D, E, A, B, 2, 12, 14, 8);
|
||||
#undef F
|
||||
#undef K
|
||||
#undef Fp
|
||||
#undef Kp
|
||||
|
||||
#define F F5
|
||||
#define K 0xA953FD4E
|
||||
#define Fp F1
|
||||
#define Kp 0x00000000
|
||||
P2(B, C, D, E, A, 4, 9, 12, 8);
|
||||
P2(A, B, C, D, E, 0, 15, 15, 5);
|
||||
P2(E, A, B, C, D, 5, 5, 10, 12);
|
||||
P2(D, E, A, B, C, 9, 11, 4, 9);
|
||||
P2(C, D, E, A, B, 7, 6, 1, 12);
|
||||
P2(B, C, D, E, A, 12, 8, 5, 5);
|
||||
P2(A, B, C, D, E, 2, 13, 8, 14);
|
||||
P2(E, A, B, C, D, 10, 12, 7, 6);
|
||||
P2(D, E, A, B, C, 14, 5, 6, 8);
|
||||
P2(C, D, E, A, B, 1, 12, 2, 13);
|
||||
P2(B, C, D, E, A, 3, 13, 13, 6);
|
||||
P2(A, B, C, D, E, 8, 14, 14, 5);
|
||||
P2(E, A, B, C, D, 11, 11, 0, 15);
|
||||
P2(D, E, A, B, C, 6, 8, 3, 13);
|
||||
P2(C, D, E, A, B, 15, 5, 9, 11);
|
||||
P2(B, C, D, E, A, 13, 6, 11, 11);
|
||||
#undef F
|
||||
#undef K
|
||||
#undef Fp
|
||||
#undef Kp
|
||||
C = ctx->state[1] + C + Dp;
|
||||
ctx->state[1] = ctx->state[2] + D + Ep;
|
||||
ctx->state[2] = ctx->state[3] + E + Ap;
|
||||
ctx->state[3] = ctx->state[4] + A + Bp;
|
||||
ctx->state[4] = ctx->state[0] + B + Cp;
|
||||
ctx->state[0] = C;
|
||||
}
|
||||
|
||||
WASM_EXPORT
|
||||
void ripemd160_update(const uint8_t* input, uint32_t ilen) {
|
||||
uint32_t fill;
|
||||
uint32_t left;
|
||||
|
||||
if (ilen == 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
left = ctx->total[0] & 0x3F;
|
||||
fill = RIPEMD160_BLOCK_LENGTH - left;
|
||||
|
||||
ctx->total[0] += (uint32_t)ilen;
|
||||
ctx->total[0] &= 0xFFFFFFFF;
|
||||
|
||||
if (ctx->total[0] < (uint32_t)ilen) {
|
||||
ctx->total[1]++;
|
||||
}
|
||||
|
||||
if (left && ilen >= fill) {
|
||||
for (uint8_t i = 0; i < fill; i++) {
|
||||
ctx->buffer[left + i] = input[i];
|
||||
}
|
||||
ripemd160_process(ctx->buffer);
|
||||
input += fill;
|
||||
ilen -= fill;
|
||||
left = 0;
|
||||
}
|
||||
|
||||
while (ilen >= RIPEMD160_BLOCK_LENGTH) {
|
||||
ripemd160_process(input);
|
||||
input += RIPEMD160_BLOCK_LENGTH;
|
||||
ilen -= RIPEMD160_BLOCK_LENGTH;
|
||||
}
|
||||
|
||||
if (ilen > 0) {
|
||||
for (uint8_t i = 0; i < ilen; i++) {
|
||||
ctx->buffer[left + i] = input[i];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
WASM_EXPORT
|
||||
void Hash_Update(uint32_t ilen) {
|
||||
ripemd160_update(main_buffer, ilen);
|
||||
}
|
||||
|
||||
static const uint8_t ripemd160_padding[RIPEMD160_BLOCK_LENGTH] = {
|
||||
0x80, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
|
||||
};
|
||||
|
||||
WASM_EXPORT
|
||||
void Hash_Final() {
|
||||
uint8_t* result = main_buffer;
|
||||
uint32_t last, padn;
|
||||
uint8_t msglen[8];
|
||||
|
||||
((uint32_t*)msglen)[0] = (ctx->total[0] << 3);
|
||||
((uint32_t*)msglen)[1] = (ctx->total[0] >> 29) | (ctx->total[1] << 3);
|
||||
|
||||
last = ctx->total[0] & 0x3F;
|
||||
padn = (last < 56) ? (56 - last) : (120 - last);
|
||||
|
||||
ripemd160_update(ripemd160_padding, padn);
|
||||
ripemd160_update(msglen, 8);
|
||||
|
||||
#pragma clang loop unroll(full)
|
||||
for (int i = 0; i < 5; i++) {
|
||||
((uint32_t*)result)[i] = ctx->state[i];
|
||||
}
|
||||
}
|
||||
|
||||
WASM_EXPORT
|
||||
const uint32_t STATE_SIZE = sizeof(*ctx);
|
||||
|
||||
WASM_EXPORT
|
||||
uint8_t* Hash_GetState() {
|
||||
return (uint8_t*) ctx;
|
||||
}
|
||||
|
||||
WASM_EXPORT
|
||||
void Hash_Calculate(uint32_t length) {
|
||||
Hash_Init();
|
||||
Hash_Update(length);
|
||||
Hash_Final();
|
||||
}
|
||||
+294
@@ -0,0 +1,294 @@
|
||||
/*
|
||||
* Copyright 2009 Colin Percival
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
* 1. Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* 2. Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
|
||||
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||
* ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
|
||||
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
|
||||
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
|
||||
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
|
||||
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
|
||||
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
|
||||
* SUCH DAMAGE.
|
||||
*
|
||||
* This file was originally written by Colin Percival as part of the Tarsnap
|
||||
* online backup system.
|
||||
*
|
||||
* Modified for hash-wasm by Dani Biró
|
||||
*/
|
||||
|
||||
#include "hash-wasm.h"
|
||||
|
||||
#define BYTES_PER_PAGE 65536
|
||||
|
||||
uint8_t *B = NULL;
|
||||
uint64_t B_size = 0;
|
||||
|
||||
WASM_EXPORT
|
||||
int8_t Hash_SetMemorySize(uint32_t total_bytes) {
|
||||
uint32_t bytes_required = total_bytes - B_size;
|
||||
|
||||
if (bytes_required > 0) {
|
||||
uint32_t blocks = bytes_required / BYTES_PER_PAGE;
|
||||
if (blocks * BYTES_PER_PAGE < bytes_required) {
|
||||
blocks += 1;
|
||||
}
|
||||
|
||||
if (__builtin_wasm_memory_grow(0, blocks) == -1) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
B_size += blocks * BYTES_PER_PAGE;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
WASM_EXPORT
|
||||
uint8_t *Hash_GetBuffer() {
|
||||
if (B == NULL) {
|
||||
// start of new memory
|
||||
B = (uint8_t *)(__builtin_wasm_memory_size(0) * BYTES_PER_PAGE);
|
||||
// always preallocate 16kb to not cause problems with the other hashes
|
||||
if (Hash_SetMemorySize(512 * 1024) == -1) {
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
|
||||
return B;
|
||||
}
|
||||
|
||||
static inline uint32_t le32dec(const void *pp) {
|
||||
return ((uint32_t *)pp)[0];
|
||||
}
|
||||
|
||||
static inline void le32enc(void *pp, uint32_t x) {
|
||||
((uint32_t *)pp)[0] = x;
|
||||
}
|
||||
|
||||
/**
|
||||
* salsa20_8(B):
|
||||
* Apply the salsa20/8 core to the provided block.
|
||||
*/
|
||||
static void salsa20_8(uint32_t B[16]) {
|
||||
uint32_t x[16];
|
||||
|
||||
#pragma clang loop unroll(full)
|
||||
for (uint8_t i = 0; i < 8; i++) {
|
||||
((uint64_t *)x)[i] = ((uint64_t *)B)[i];
|
||||
}
|
||||
|
||||
#pragma clang loop unroll(full)
|
||||
for (uint8_t i = 0; i < 8; i += 2) {
|
||||
#define R(a, b) (((a) << (b)) | ((a) >> (32 - (b))))
|
||||
/* Operate on columns. */
|
||||
x[4] ^= R(x[0] + x[12], 7);
|
||||
x[8] ^= R(x[4] + x[0], 9);
|
||||
x[12] ^= R(x[8] + x[4], 13);
|
||||
x[0] ^= R(x[12] + x[8], 18);
|
||||
|
||||
x[9] ^= R(x[5] + x[1], 7);
|
||||
x[13] ^= R(x[9] + x[5], 9);
|
||||
x[1] ^= R(x[13] + x[9], 13);
|
||||
x[5] ^= R(x[1] + x[13], 18);
|
||||
|
||||
x[14] ^= R(x[10] + x[6], 7);
|
||||
x[2] ^= R(x[14] + x[10], 9);
|
||||
x[6] ^= R(x[2] + x[14], 13);
|
||||
x[10] ^= R(x[6] + x[2], 18);
|
||||
|
||||
x[3] ^= R(x[15] + x[11], 7);
|
||||
x[7] ^= R(x[3] + x[15], 9);
|
||||
x[11] ^= R(x[7] + x[3], 13);
|
||||
x[15] ^= R(x[11] + x[7], 18);
|
||||
|
||||
/* Operate on rows. */
|
||||
x[1] ^= R(x[0] + x[3], 7);
|
||||
x[2] ^= R(x[1] + x[0], 9);
|
||||
x[3] ^= R(x[2] + x[1], 13);
|
||||
x[0] ^= R(x[3] + x[2], 18);
|
||||
|
||||
x[6] ^= R(x[5] + x[4], 7);
|
||||
x[7] ^= R(x[6] + x[5], 9);
|
||||
x[4] ^= R(x[7] + x[6], 13);
|
||||
x[5] ^= R(x[4] + x[7], 18);
|
||||
|
||||
x[11] ^= R(x[10] + x[9], 7);
|
||||
x[8] ^= R(x[11] + x[10], 9);
|
||||
x[9] ^= R(x[8] + x[11], 13);
|
||||
x[10] ^= R(x[9] + x[8], 18);
|
||||
|
||||
x[12] ^= R(x[15] + x[14], 7);
|
||||
x[13] ^= R(x[12] + x[15], 9);
|
||||
x[14] ^= R(x[13] + x[12], 13);
|
||||
x[15] ^= R(x[14] + x[13], 18);
|
||||
#undef R
|
||||
}
|
||||
|
||||
#pragma clang loop unroll(full)
|
||||
for (uint8_t i = 0; i < 16; i++) {
|
||||
B[i] += x[i];
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* blockmix_salsa8(Bin, Bout, X, r):
|
||||
* Compute Bout = BlockMix_{salsa20/8, r}(Bin). The input Bin must be 128r
|
||||
* bytes in length; the output Bout must also be the same size. The
|
||||
* temporary space X must be 64 bytes.
|
||||
*/
|
||||
static void blockmix_salsa8(const uint32_t *Bin, uint32_t *Bout, uint32_t *X, int r) {
|
||||
/* 1: X <-- B_{2r - 1} */
|
||||
#pragma clang loop unroll(full)
|
||||
for (uint8_t i = 0; i < 8; i++) {
|
||||
((uint64_t *)X)[i] = ((uint64_t *)&Bin[(2 * r - 1) * 16])[i];
|
||||
}
|
||||
|
||||
/* 2: for i = 0 to 2r - 1 do */
|
||||
for (uint32_t i = 0; i < 2 * r; i += 2) {
|
||||
/* 3: X <-- H(X \xor B_i) */
|
||||
#pragma clang loop unroll(full)
|
||||
for (uint8_t j = 0; j < 8; j++) {
|
||||
((uint64_t *)X)[j] ^= ((uint64_t *)&Bin[i * 16])[j];
|
||||
}
|
||||
salsa20_8(X);
|
||||
|
||||
/* 4: Y_i <-- X */
|
||||
/* 6: B' <-- (Y_0, Y_2 ... Y_{2r-2}, Y_1, Y_3 ... Y_{2r-1}) */
|
||||
#pragma clang loop unroll(full)
|
||||
for (uint8_t j = 0; j < 8; j++) {
|
||||
((uint64_t *)&Bout[i * 8])[j] = ((uint64_t *)X)[j];
|
||||
}
|
||||
|
||||
/* 3: X <-- H(X \xor B_i) */
|
||||
#pragma clang loop unroll(full)
|
||||
for (uint8_t j = 0; j < 8; j++) {
|
||||
((uint64_t *)X)[j] ^= ((uint64_t *)&Bin[i * 16 + 16])[j];
|
||||
}
|
||||
salsa20_8(X);
|
||||
|
||||
/* 4: Y_i <-- X */
|
||||
/* 6: B' <-- (Y_0, Y_2 ... Y_{2r-2}, Y_1, Y_3 ... Y_{2r-1}) */
|
||||
#pragma clang loop unroll(full)
|
||||
for (uint8_t j = 0; j < 8; j++) {
|
||||
((uint64_t *)&Bout[i * 8 + r * 16])[j] = ((uint64_t *)X)[j];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* integerify(B, r):
|
||||
* Return the result of parsing B_{2r-1} as a little-endian integer.
|
||||
*/
|
||||
static inline uint64_t integerify(const void *B, int r) {
|
||||
const uint32_t *X = (const void *)((uintptr_t)(B) + (2 * r - 1) * 64);
|
||||
|
||||
return (((uint64_t)(X[1]) << 32) + X[0]);
|
||||
}
|
||||
|
||||
/**
|
||||
* smix(B, r, N, V, XY):
|
||||
* Compute B = SMix_r(B, N). The input B must be 128r bytes in length;
|
||||
* the temporary storage V must be 128rN bytes in length; the temporary
|
||||
* storage XY must be 256r + 64 bytes in length. The value N must be a
|
||||
* power of 2 greater than 1. The arrays B, V, and XY must be aligned to a
|
||||
* multiple of 64 bytes.
|
||||
*/
|
||||
void smix(uint8_t *B, int r, uint64_t N, void *_V, void *XY) {
|
||||
uint32_t *X = XY;
|
||||
uint32_t *Y = (void *)((uint8_t *)(XY) + 128 * r);
|
||||
uint32_t *Z = (void *)((uint8_t *)(XY) + 256 * r);
|
||||
uint32_t *V = _V;
|
||||
|
||||
/* 1: X <-- B */
|
||||
for (uint32_t k = 0; k < 32 * r; k++) {
|
||||
X[k] = le32dec(&B[4 * k]);
|
||||
}
|
||||
|
||||
/* 2: for i = 0 to N - 1 do */
|
||||
for (uint32_t i = 0; i < N; i += 2) {
|
||||
/* 3: V_i <-- X */
|
||||
|
||||
for (uint32_t j = 0; j < r; j++) {
|
||||
uint64_t *dest = &(((uint64_t *)&V[i * (32 * r)])[j * 16]);
|
||||
uint64_t *src = &(((uint64_t *)X)[j * 16]);
|
||||
#pragma clang loop unroll(full)
|
||||
for (uint8_t jj = 0; jj < 16; jj++) {
|
||||
dest[jj] = src[jj];
|
||||
}
|
||||
}
|
||||
/* 4: X <-- H(X) */
|
||||
blockmix_salsa8(X, Y, Z, r);
|
||||
|
||||
/* 3: V_i <-- X */
|
||||
for (uint32_t j = 0; j < r; j++) {
|
||||
uint64_t *dest = &(((uint64_t *)&V[(i + 1) * (32 * r)])[j * 16]);
|
||||
uint64_t *src = &(((uint64_t *)Y)[j * 16]);
|
||||
#pragma clang loop unroll(full)
|
||||
for (uint8_t jj = 0; jj < 16; jj++) {
|
||||
dest[jj] = src[jj];
|
||||
}
|
||||
}
|
||||
|
||||
/* 4: X <-- H(X) */
|
||||
blockmix_salsa8(Y, X, Z, r);
|
||||
}
|
||||
|
||||
/* 6: for i = 0 to N - 1 do */
|
||||
for (uint32_t i = 0; i < N; i += 2) {
|
||||
/* 7: j <-- Integerify(X) mod N */
|
||||
uint32_t j = integerify(X, r) & (N - 1);
|
||||
|
||||
/* 8: X <-- H(X \xor V_j) */
|
||||
for (uint32_t z = 0; z < r; z++) {
|
||||
uint64_t *dest = &(((uint64_t *)X)[z * 16]);
|
||||
uint64_t *src = &(((uint64_t *)&V[j * (32 * r)])[z * 16]);
|
||||
#pragma clang loop unroll(full)
|
||||
for (uint8_t zz = 0; zz < 16; zz++) {
|
||||
dest[zz] ^= src[zz];
|
||||
}
|
||||
}
|
||||
blockmix_salsa8(X, Y, Z, r);
|
||||
|
||||
/* 7: j <-- Integerify(X) mod N */
|
||||
j = integerify(Y, r) & (N - 1);
|
||||
|
||||
/* 8: X <-- H(X \xor V_j) */
|
||||
for (uint32_t z = 0; z < r; z++) {
|
||||
uint64_t *dest = &(((uint64_t *)Y)[z * 16]);
|
||||
uint64_t *src = &(((uint64_t *)&V[j * (32 * r)])[z * 16]);
|
||||
#pragma clang loop unroll(full)
|
||||
for (uint8_t zz = 0; zz < 16; zz++) {
|
||||
dest[zz] ^= src[zz];
|
||||
}
|
||||
}
|
||||
blockmix_salsa8(Y, X, Z, r);
|
||||
}
|
||||
|
||||
/* 10: B' <-- X */
|
||||
for (uint32_t k = 0; k < 32 * r; k++) {
|
||||
le32enc(&B[4 * k], X[k]);
|
||||
}
|
||||
}
|
||||
|
||||
WASM_EXPORT
|
||||
void scrypt(uint32_t blockSize, uint32_t costFactor, uint32_t parallelism) {
|
||||
uint8_t *V = &B[128 * blockSize * parallelism];
|
||||
uint8_t *XY = &V[128 * blockSize * costFactor];
|
||||
|
||||
for (uint32_t i = 0; i < parallelism; i++) {
|
||||
smix(&B[i * 128 * blockSize], blockSize, costFactor, V, XY);
|
||||
}
|
||||
}
|
||||
+192
@@ -0,0 +1,192 @@
|
||||
/*
|
||||
SHA-1 in C
|
||||
By Steve Reid <steve@edmweb.com>
|
||||
100% Public Domain
|
||||
|
||||
Modified for hash-wasm by Dani Biró
|
||||
*/
|
||||
|
||||
#define WITH_BUFFER
|
||||
#include "hash-wasm.h"
|
||||
|
||||
#define rol(value, bits) (((value) << (bits)) | ((value) >> (32 - (bits))))
|
||||
|
||||
/* blk0() and blk() perform the initial expand. */
|
||||
/* I got the idea of expanding during the round function from SSLeay */
|
||||
#define blk0(i) \
|
||||
(block->l[i] = (rol(block->l[i], 24) & 0xFF00FF00) | \
|
||||
(rol(block->l[i], 8) & 0x00FF00FF))
|
||||
|
||||
#define blk(i) \
|
||||
(block->l[i & 15] = rol(block->l[(i + 13) & 15] ^ \
|
||||
block->l[(i + 8) & 15] ^ \
|
||||
block->l[(i + 2) & 15] ^ \
|
||||
block->l[i & 15], \
|
||||
1))
|
||||
|
||||
struct SHA1_CTX {
|
||||
uint32_t state[5];
|
||||
uint32_t count[2];
|
||||
uint8_t buffer[64];
|
||||
};
|
||||
|
||||
struct SHA1_CTX sctx;
|
||||
struct SHA1_CTX* context = &sctx;
|
||||
|
||||
/* (R0+R1), R2, R3, R4 are the different operations used in SHA1 */
|
||||
#define R0(v, w, x, y, z, i) \
|
||||
z += ((w & (x ^ y)) ^ y) + blk0(i) + 0x5A827999 + rol(v, 5); \
|
||||
w = rol(w, 30);
|
||||
#define R1(v, w, x, y, z, i) \
|
||||
z += ((w & (x ^ y)) ^ y) + blk(i) + 0x5A827999 + rol(v, 5); \
|
||||
w = rol(w, 30);
|
||||
#define R2(v, w, x, y, z, i) \
|
||||
z += (w ^ x ^ y) + blk(i) + 0x6ED9EBA1 + rol(v, 5); \
|
||||
w = rol(w, 30);
|
||||
#define R3(v, w, x, y, z, i) \
|
||||
z += (((w | x) & y) | (w & x)) + blk(i) + 0x8F1BBCDC + rol(v, 5); \
|
||||
w = rol(w, 30);
|
||||
#define R4(v, w, x, y, z, i) \
|
||||
z += (w ^ x ^ y) + blk(i) + 0xCA62C1D6 + rol(v, 5); \
|
||||
w = rol(w, 30);
|
||||
|
||||
/* Hash a single 512-bit block. This is the core of the algorithm. */
|
||||
void SHA1Transform(uint32_t state[5], const uint8_t buffer[64]) {
|
||||
uint32_t a, b, c, d, e;
|
||||
typedef union {
|
||||
uint8_t c[64];
|
||||
uint32_t l[16];
|
||||
uint64_t ll[8];
|
||||
} CHAR64LONG16;
|
||||
|
||||
CHAR64LONG16 block[1]; /* use array to appear as a pointer */
|
||||
|
||||
#pragma clang loop unroll(full)
|
||||
for (uint8_t i = 0; i < 8; i++) {
|
||||
block->ll[i] = *(uint64_t*)&buffer[i * 8];
|
||||
}
|
||||
|
||||
/* Copy context->state[] to working vars */
|
||||
a = state[0];
|
||||
b = state[1];
|
||||
c = state[2];
|
||||
d = state[3];
|
||||
e = state[4];
|
||||
|
||||
/* 4 rounds of 20 operations each. Loop unrolled. */
|
||||
R0(a, b, c, d, e, 0); R0(e, a, b, c, d, 1); R0(d, e, a, b, c, 2); R0(c, d, e, a, b, 3);
|
||||
R0(b, c, d, e, a, 4); R0(a, b, c, d, e, 5); R0(e, a, b, c, d, 6); R0(d, e, a, b, c, 7);
|
||||
R0(c, d, e, a, b, 8); R0(b, c, d, e, a, 9); R0(a, b, c, d, e, 10); R0(e, a, b, c, d, 11);
|
||||
R0(d, e, a, b, c, 12); R0(c, d, e, a, b, 13); R0(b, c, d, e, a, 14); R0(a, b, c, d, e, 15);
|
||||
R1(e, a, b, c, d, 16); R1(d, e, a, b, c, 17); R1(c, d, e, a, b, 18); R1(b, c, d, e, a, 19);
|
||||
R2(a, b, c, d, e, 20); R2(e, a, b, c, d, 21); R2(d, e, a, b, c, 22); R2(c, d, e, a, b, 23);
|
||||
R2(b, c, d, e, a, 24); R2(a, b, c, d, e, 25); R2(e, a, b, c, d, 26); R2(d, e, a, b, c, 27);
|
||||
R2(c, d, e, a, b, 28); R2(b, c, d, e, a, 29); R2(a, b, c, d, e, 30); R2(e, a, b, c, d, 31);
|
||||
R2(d, e, a, b, c, 32); R2(c, d, e, a, b, 33); R2(b, c, d, e, a, 34); R2(a, b, c, d, e, 35);
|
||||
R2(e, a, b, c, d, 36); R2(d, e, a, b, c, 37); R2(c, d, e, a, b, 38); R2(b, c, d, e, a, 39);
|
||||
R3(a, b, c, d, e, 40); R3(e, a, b, c, d, 41); R3(d, e, a, b, c, 42); R3(c, d, e, a, b, 43);
|
||||
R3(b, c, d, e, a, 44); R3(a, b, c, d, e, 45); R3(e, a, b, c, d, 46); R3(d, e, a, b, c, 47);
|
||||
R3(c, d, e, a, b, 48); R3(b, c, d, e, a, 49); R3(a, b, c, d, e, 50); R3(e, a, b, c, d, 51);
|
||||
R3(d, e, a, b, c, 52); R3(c, d, e, a, b, 53); R3(b, c, d, e, a, 54); R3(a, b, c, d, e, 55);
|
||||
R3(e, a, b, c, d, 56); R3(d, e, a, b, c, 57); R3(c, d, e, a, b, 58); R3(b, c, d, e, a, 59);
|
||||
R4(a, b, c, d, e, 60); R4(e, a, b, c, d, 61); R4(d, e, a, b, c, 62); R4(c, d, e, a, b, 63);
|
||||
R4(b, c, d, e, a, 64); R4(a, b, c, d, e, 65); R4(e, a, b, c, d, 66); R4(d, e, a, b, c, 67);
|
||||
R4(c, d, e, a, b, 68); R4(b, c, d, e, a, 69); R4(a, b, c, d, e, 70); R4(e, a, b, c, d, 71);
|
||||
R4(d, e, a, b, c, 72); R4(c, d, e, a, b, 73); R4(b, c, d, e, a, 74); R4(a, b, c, d, e, 75);
|
||||
R4(e, a, b, c, d, 76); R4(d, e, a, b, c, 77); R4(c, d, e, a, b, 78); R4(b, c, d, e, a, 79);
|
||||
|
||||
/* Add the working vars back into context.state[] */
|
||||
state[0] += a;
|
||||
state[1] += b;
|
||||
state[2] += c;
|
||||
state[3] += d;
|
||||
state[4] += e;
|
||||
}
|
||||
|
||||
/* SHA1Init - Initialize new context */
|
||||
WASM_EXPORT
|
||||
void Hash_Init() {
|
||||
context->state[0] = 0x67452301;
|
||||
context->state[1] = 0xEFCDAB89;
|
||||
context->state[2] = 0x98BADCFE;
|
||||
context->state[3] = 0x10325476;
|
||||
context->state[4] = 0xC3D2E1F0;
|
||||
context->count[0] = context->count[1] = 0;
|
||||
}
|
||||
|
||||
void SHA1Update(const uint8_t* data, uint32_t len) {
|
||||
uint32_t i;
|
||||
|
||||
uint32_t j = context->count[0];
|
||||
if ((context->count[0] += len << 3) < j) {
|
||||
context->count[1]++;
|
||||
}
|
||||
|
||||
context->count[1] += (len >> 29);
|
||||
j = (j >> 3) & 63;
|
||||
|
||||
if ((j + len) > 63) {
|
||||
uint8_t end = i = 64 - j;
|
||||
for (uint8_t z = 0; z < end; z++) {
|
||||
context->buffer[j + z] = data[z];
|
||||
}
|
||||
SHA1Transform(context->state, context->buffer);
|
||||
for (; i + 63 < len; i += 64) {
|
||||
SHA1Transform(context->state, &data[i]);
|
||||
}
|
||||
j = 0;
|
||||
} else {
|
||||
i = 0;
|
||||
}
|
||||
|
||||
for (uint8_t z = 0; z < len - i; z++) {
|
||||
context->buffer[j + z] = data[i + z];
|
||||
}
|
||||
}
|
||||
|
||||
WASM_EXPORT
|
||||
void Hash_Update(uint32_t len) {
|
||||
SHA1Update(main_buffer, len);
|
||||
}
|
||||
|
||||
/* Add padding and return the message digest. */
|
||||
WASM_EXPORT
|
||||
void Hash_Final() {
|
||||
uint8_t* result = main_buffer;
|
||||
uint8_t finalcount[8];
|
||||
uint8_t c;
|
||||
|
||||
for (uint8_t i = 0; i < 8; i++) {
|
||||
finalcount[i] = (uint8_t)(
|
||||
(context->count[(i >= 4 ? 0 : 1)] >> ((3 - (i & 3)) * 8)) & 255);
|
||||
}
|
||||
|
||||
c = 0200;
|
||||
SHA1Update(&c, 1);
|
||||
while ((context->count[0] & 504) != 448) {
|
||||
c = 0000;
|
||||
SHA1Update(&c, 1);
|
||||
}
|
||||
|
||||
SHA1Update(finalcount, 8); /* Should cause a SHA1Transform() */
|
||||
|
||||
for (uint8_t i = 0; i < 20; i++) {
|
||||
result[i] =
|
||||
(uint8_t)((context->state[i >> 2] >> ((3 - (i & 3)) * 8)) & 255);
|
||||
}
|
||||
}
|
||||
|
||||
WASM_EXPORT
|
||||
const uint32_t STATE_SIZE = sizeof(*context);
|
||||
|
||||
WASM_EXPORT
|
||||
uint8_t* Hash_GetState() {
|
||||
return (uint8_t*) context;
|
||||
}
|
||||
|
||||
WASM_EXPORT
|
||||
void Hash_Calculate(uint32_t length) {
|
||||
Hash_Init();
|
||||
Hash_Update(length);
|
||||
Hash_Final();
|
||||
}
|
||||
+295
@@ -0,0 +1,295 @@
|
||||
/* sha256.c - an implementation of SHA-256/224 hash functions
|
||||
* based on FIPS 180-3 (Federal Information Processing Standart).
|
||||
*
|
||||
* Copyright (c) 2010, Aleksey Kravchenko <rhash.admin@gmail.com>
|
||||
*
|
||||
* Permission to use, copy, modify, and/or distribute this software for any
|
||||
* purpose with or without fee is hereby granted.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH
|
||||
* REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
|
||||
* AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT,
|
||||
* INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
|
||||
* LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
|
||||
* OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
|
||||
* PERFORMANCE OF THIS SOFTWARE.
|
||||
|
||||
* Modified for hash-wasm by Dani Biró
|
||||
*/
|
||||
|
||||
#define WITH_BUFFER
|
||||
#include "hash-wasm.h"
|
||||
|
||||
#define sha256_block_size 64
|
||||
#define sha256_hash_size 32
|
||||
#define sha224_hash_size 28
|
||||
#define ROTR32(dword, n) ((dword) >> (n) ^ ((dword) << (32 - (n))))
|
||||
#define bswap_32(x) __builtin_bswap32(x)
|
||||
|
||||
struct sha256_ctx {
|
||||
uint32_t message[16]; /* 512-bit buffer for leftovers */
|
||||
uint64_t length; /* number of processed bytes */
|
||||
uint32_t hash[8]; /* 256-bit algorithm internal hashing state */
|
||||
uint32_t digest_length; /* length of the algorithm digest in bytes */
|
||||
};
|
||||
|
||||
struct sha256_ctx sctx;
|
||||
struct sha256_ctx* ctx = &sctx;
|
||||
|
||||
/* SHA-224 and SHA-256 constants for 64 rounds. These words represent
|
||||
* the first 32 bits of the fractional parts of the cube
|
||||
* roots of the first 64 prime numbers. */
|
||||
static const uint32_t rhash_k256[64] = {
|
||||
0x428a2f98, 0x71374491, 0xb5c0fbcf, 0xe9b5dba5, 0x3956c25b, 0x59f111f1,
|
||||
0x923f82a4, 0xab1c5ed5, 0xd807aa98, 0x12835b01, 0x243185be, 0x550c7dc3,
|
||||
0x72be5d74, 0x80deb1fe, 0x9bdc06a7, 0xc19bf174, 0xe49b69c1, 0xefbe4786,
|
||||
0x0fc19dc6, 0x240ca1cc, 0x2de92c6f, 0x4a7484aa, 0x5cb0a9dc, 0x76f988da,
|
||||
0x983e5152, 0xa831c66d, 0xb00327c8, 0xbf597fc7, 0xc6e00bf3, 0xd5a79147,
|
||||
0x06ca6351, 0x14292967, 0x27b70a85, 0x2e1b2138, 0x4d2c6dfc, 0x53380d13,
|
||||
0x650a7354, 0x766a0abb, 0x81c2c92e, 0x92722c85, 0xa2bfe8a1, 0xa81a664b,
|
||||
0xc24b8b70, 0xc76c51a3, 0xd192e819, 0xd6990624, 0xf40e3585, 0x106aa070,
|
||||
0x19a4c116, 0x1e376c08, 0x2748774c, 0x34b0bcb5, 0x391c0cb3, 0x4ed8aa4a,
|
||||
0x5b9cca4f, 0x682e6ff3, 0x748f82ee, 0x78a5636f, 0x84c87814, 0x8cc70208,
|
||||
0x90befffa, 0xa4506ceb, 0xbef9a3f7, 0xc67178f2
|
||||
};
|
||||
|
||||
/* The SHA256/224 functions defined by FIPS 180-3, 4.1.2 */
|
||||
/* Optimized version of Ch(x,y,z)=((x & y) | (~x & z)) */
|
||||
#define Ch(x, y, z) ((z) ^ ((x) & ((y) ^ (z))))
|
||||
/* Optimized version of Maj(x,y,z)=((x & y) ^ (x & z) ^ (y & z)) */
|
||||
#define Maj(x, y, z) (((x) & (y)) ^ ((z) & ((x) ^ (y))))
|
||||
|
||||
#define Sigma0(x) (ROTR32((x), 2) ^ ROTR32((x), 13) ^ ROTR32((x), 22))
|
||||
#define Sigma1(x) (ROTR32((x), 6) ^ ROTR32((x), 11) ^ ROTR32((x), 25))
|
||||
#define sigma0(x) (ROTR32((x), 7) ^ ROTR32((x), 18) ^ ((x) >> 3))
|
||||
#define sigma1(x) (ROTR32((x), 17) ^ ROTR32((x), 19) ^ ((x) >> 10))
|
||||
|
||||
/* Recalculate element n-th of circular buffer W using formula
|
||||
* W[n] = sigma1(W[n - 2]) + W[n - 7] + sigma0(W[n - 15]) + W[n - 16]; */
|
||||
#define RECALCULATE_W(W, n) \
|
||||
(W[n] += \
|
||||
(sigma1(W[(n - 2) & 15]) + W[(n - 7) & 15] + sigma0(W[(n - 15) & 15])))
|
||||
|
||||
#define ROUND(a, b, c, d, e, f, g, h, k, data) \
|
||||
{ \
|
||||
uint32_t T1 = h + Sigma1(e) + Ch(e, f, g) + k + (data); \
|
||||
d += T1, h = T1 + Sigma0(a) + Maj(a, b, c); \
|
||||
}
|
||||
#define ROUND_1_16(a, b, c, d, e, f, g, h, n) \
|
||||
ROUND(a, b, c, d, e, f, g, h, rhash_k256[n], W[n] = bswap_32(block[n]))
|
||||
#define ROUND_17_64(a, b, c, d, e, f, g, h, n) \
|
||||
ROUND(a, b, c, d, e, f, g, h, k[n], RECALCULATE_W(W, n))
|
||||
|
||||
/**
|
||||
* Initialize context before calculaing hash.
|
||||
*
|
||||
*/
|
||||
void sha256_init() {
|
||||
/* Initial values. These words were obtained by taking the first 32
|
||||
* bits of the fractional parts of the square roots of the first
|
||||
* eight prime numbers. */
|
||||
static const uint32_t SHA256_H0[8] = {
|
||||
0x6a09e667, 0xbb67ae85, 0x3c6ef372, 0xa54ff53a,
|
||||
0x510e527f, 0x9b05688c, 0x1f83d9ab, 0x5be0cd19
|
||||
};
|
||||
|
||||
ctx->length = 0;
|
||||
ctx->digest_length = sha256_hash_size;
|
||||
|
||||
/* initialize algorithm state */
|
||||
|
||||
#pragma clang loop unroll(full)
|
||||
for (uint8_t i = 0; i < 8; i += 2) {
|
||||
*(uint64_t*)&ctx->hash[i] = *(uint64_t*)&SHA256_H0[i];
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Initialize context before calculaing hash.
|
||||
*
|
||||
*/
|
||||
void sha224_init() {
|
||||
/* Initial values from FIPS 180-3. These words were obtained by taking
|
||||
* bits from 33th to 64th of the fractional parts of the square
|
||||
* roots of ninth through sixteenth prime numbers. */
|
||||
static const uint32_t SHA224_H0[8] = {
|
||||
0xc1059ed8, 0x367cd507, 0x3070dd17, 0xf70e5939,
|
||||
0xffc00b31, 0x68581511, 0x64f98fa7, 0xbefa4fa4
|
||||
};
|
||||
|
||||
ctx->length = 0;
|
||||
ctx->digest_length = sha224_hash_size;
|
||||
|
||||
#pragma clang loop unroll(full)
|
||||
for (uint8_t i = 0; i < 8; i += 2) {
|
||||
*(uint64_t*)&ctx->hash[i] = *(uint64_t*)&SHA224_H0[i];
|
||||
}
|
||||
}
|
||||
|
||||
WASM_EXPORT
|
||||
void Hash_Init(uint32_t bits) {
|
||||
if (bits == 224) {
|
||||
sha224_init();
|
||||
} else {
|
||||
sha256_init();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* The core transformation. Process a 512-bit block.
|
||||
*
|
||||
* @param hash algorithm state
|
||||
* @param block the message block to process
|
||||
*/
|
||||
static void sha256_process_block(uint32_t hash[8], uint32_t block[16]) {
|
||||
uint32_t A, B, C, D, E, F, G, H;
|
||||
uint32_t W[16];
|
||||
const uint32_t* k;
|
||||
int i;
|
||||
|
||||
A = hash[0], B = hash[1], C = hash[2], D = hash[3];
|
||||
E = hash[4], F = hash[5], G = hash[6], H = hash[7];
|
||||
|
||||
/* Compute SHA using alternate Method: FIPS 180-3 6.1.3 */
|
||||
ROUND_1_16(A, B, C, D, E, F, G, H, 0);
|
||||
ROUND_1_16(H, A, B, C, D, E, F, G, 1);
|
||||
ROUND_1_16(G, H, A, B, C, D, E, F, 2);
|
||||
ROUND_1_16(F, G, H, A, B, C, D, E, 3);
|
||||
ROUND_1_16(E, F, G, H, A, B, C, D, 4);
|
||||
ROUND_1_16(D, E, F, G, H, A, B, C, 5);
|
||||
ROUND_1_16(C, D, E, F, G, H, A, B, 6);
|
||||
ROUND_1_16(B, C, D, E, F, G, H, A, 7);
|
||||
ROUND_1_16(A, B, C, D, E, F, G, H, 8);
|
||||
ROUND_1_16(H, A, B, C, D, E, F, G, 9);
|
||||
ROUND_1_16(G, H, A, B, C, D, E, F, 10);
|
||||
ROUND_1_16(F, G, H, A, B, C, D, E, 11);
|
||||
ROUND_1_16(E, F, G, H, A, B, C, D, 12);
|
||||
ROUND_1_16(D, E, F, G, H, A, B, C, 13);
|
||||
ROUND_1_16(C, D, E, F, G, H, A, B, 14);
|
||||
ROUND_1_16(B, C, D, E, F, G, H, A, 15);
|
||||
|
||||
#pragma clang loop unroll(full)
|
||||
for (i = 16, k = &rhash_k256[16]; i < 64; i += 16, k += 16) {
|
||||
ROUND_17_64(A, B, C, D, E, F, G, H, 0);
|
||||
ROUND_17_64(H, A, B, C, D, E, F, G, 1);
|
||||
ROUND_17_64(G, H, A, B, C, D, E, F, 2);
|
||||
ROUND_17_64(F, G, H, A, B, C, D, E, 3);
|
||||
ROUND_17_64(E, F, G, H, A, B, C, D, 4);
|
||||
ROUND_17_64(D, E, F, G, H, A, B, C, 5);
|
||||
ROUND_17_64(C, D, E, F, G, H, A, B, 6);
|
||||
ROUND_17_64(B, C, D, E, F, G, H, A, 7);
|
||||
ROUND_17_64(A, B, C, D, E, F, G, H, 8);
|
||||
ROUND_17_64(H, A, B, C, D, E, F, G, 9);
|
||||
ROUND_17_64(G, H, A, B, C, D, E, F, 10);
|
||||
ROUND_17_64(F, G, H, A, B, C, D, E, 11);
|
||||
ROUND_17_64(E, F, G, H, A, B, C, D, 12);
|
||||
ROUND_17_64(D, E, F, G, H, A, B, C, 13);
|
||||
ROUND_17_64(C, D, E, F, G, H, A, B, 14);
|
||||
ROUND_17_64(B, C, D, E, F, G, H, A, 15);
|
||||
}
|
||||
|
||||
hash[0] += A, hash[1] += B, hash[2] += C, hash[3] += D;
|
||||
hash[4] += E, hash[5] += F, hash[6] += G, hash[7] += H;
|
||||
}
|
||||
|
||||
/**
|
||||
* Calculate message hash.
|
||||
* Can be called repeatedly with chunks of the message to be hashed.
|
||||
*
|
||||
* @param size length of the message chunk
|
||||
*/
|
||||
WASM_EXPORT
|
||||
void Hash_Update(uint32_t size) {
|
||||
const uint8_t* msg = main_buffer;
|
||||
uint32_t index = (uint32_t)ctx->length & 63;
|
||||
ctx->length += size;
|
||||
|
||||
/* fill partial block */
|
||||
if (index) {
|
||||
uint32_t left = sha256_block_size - index;
|
||||
uint32_t end = size < left ? size : left;
|
||||
uint8_t* message8 = (uint8_t*)ctx->message;
|
||||
for (uint8_t i = 0; i < end; i++) {
|
||||
*(message8 + index + i) = msg[i];
|
||||
}
|
||||
if (size < left) return;
|
||||
|
||||
/* process partial block */
|
||||
sha256_process_block(ctx->hash, (uint32_t*)ctx->message);
|
||||
msg += left;
|
||||
size -= left;
|
||||
}
|
||||
|
||||
while (size >= sha256_block_size) {
|
||||
uint32_t* aligned_message_block = (uint32_t*)msg;
|
||||
|
||||
sha256_process_block(ctx->hash, aligned_message_block);
|
||||
msg += sha256_block_size;
|
||||
size -= sha256_block_size;
|
||||
}
|
||||
|
||||
if (size) {
|
||||
/* save leftovers */
|
||||
for (uint8_t i = 0; i < size; i++) {
|
||||
*(((uint8_t*)ctx->message) + i) = msg[i];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Store calculated hash into the given array.
|
||||
*
|
||||
*/
|
||||
WASM_EXPORT
|
||||
void Hash_Final() {
|
||||
uint32_t index = ((uint32_t)ctx->length & 63) >> 2;
|
||||
uint32_t shift = ((uint32_t)ctx->length & 3) * 8;
|
||||
|
||||
/* pad message and run for last block */
|
||||
|
||||
/* append the byte 0x80 to the message */
|
||||
ctx->message[index] &= ~(0xFFFFFFFFu << shift);
|
||||
ctx->message[index++] ^= 0x80u << shift;
|
||||
|
||||
/* if no room left in the message to store 64-bit message length */
|
||||
if (index > 14) {
|
||||
/* then fill the rest with zeros and process it */
|
||||
while (index < 16) {
|
||||
ctx->message[index++] = 0;
|
||||
}
|
||||
sha256_process_block(ctx->hash, ctx->message);
|
||||
index = 0;
|
||||
}
|
||||
|
||||
while (index < 14) {
|
||||
ctx->message[index++] = 0;
|
||||
}
|
||||
|
||||
ctx->message[14] = bswap_32((uint32_t)(ctx->length >> 29));
|
||||
ctx->message[15] = bswap_32((uint32_t)(ctx->length << 3));
|
||||
sha256_process_block(ctx->hash, ctx->message);
|
||||
|
||||
#pragma clang loop unroll(full)
|
||||
for (int32_t i = 7; i >= 0; i--) {
|
||||
ctx->hash[i] = bswap_32(ctx->hash[i]);
|
||||
}
|
||||
|
||||
for (uint8_t i = 0; i < ctx->digest_length; i++) {
|
||||
main_buffer[i] = *(((uint8_t*)ctx->hash) + i);
|
||||
}
|
||||
}
|
||||
|
||||
WASM_EXPORT
|
||||
const uint32_t STATE_SIZE = sizeof(*ctx);
|
||||
|
||||
WASM_EXPORT
|
||||
uint8_t* Hash_GetState() {
|
||||
return (uint8_t*) ctx;
|
||||
}
|
||||
|
||||
WASM_EXPORT
|
||||
void Hash_Calculate(uint32_t length, uint32_t initParam) {
|
||||
Hash_Init(initParam);
|
||||
Hash_Update(length);
|
||||
Hash_Final();
|
||||
}
|
||||
+332
@@ -0,0 +1,332 @@
|
||||
/* 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 (c) 2013, Aleksey Kravchenko <rhash.admin@gmail.com>
|
||||
*
|
||||
* Permission to use, copy, modify, and/or distribute this software for any
|
||||
* purpose with or without fee is hereby granted.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH
|
||||
* REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
|
||||
* AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT,
|
||||
* INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
|
||||
* LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
|
||||
* OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
|
||||
* PERFORMANCE OF THIS SOFTWARE.
|
||||
|
||||
* Modified for hash-wasm by Dani Biró
|
||||
*/
|
||||
|
||||
#define WITH_BUFFER
|
||||
#include "hash-wasm.h"
|
||||
|
||||
#define NumberOfRounds 24
|
||||
#define sha3_max_permutation_size 25
|
||||
#define sha3_max_rate_in_qwords 24
|
||||
|
||||
#define I64(x) x##ULL
|
||||
#define ROTL64(qword, n) ((qword) << (n) ^ ((qword) >> (64 - (n))))
|
||||
|
||||
struct SHA3_CTX {
|
||||
/* 1600 bits algorithm hashing state */
|
||||
uint64_t hash[sha3_max_permutation_size];
|
||||
/* 1536-bit buffer for leftovers */
|
||||
uint64_t message[sha3_max_rate_in_qwords];
|
||||
/* count of bytes in the message[] buffer */
|
||||
unsigned rest;
|
||||
/* size of a message block processed at once */
|
||||
unsigned block_size;
|
||||
};
|
||||
|
||||
struct SHA3_CTX sctx;
|
||||
struct SHA3_CTX* ctx = &sctx;
|
||||
|
||||
/* SHA3 (Keccak) constants for 24 rounds */
|
||||
static uint64_t keccak_round_constants[NumberOfRounds] = {
|
||||
I64(0x0000000000000001), I64(0x0000000000008082), I64(0x800000000000808A), I64(0x8000000080008000),
|
||||
I64(0x000000000000808B), I64(0x0000000080000001), I64(0x8000000080008081), I64(0x8000000000008009),
|
||||
I64(0x000000000000008A), I64(0x0000000000000088), I64(0x0000000080008009), I64(0x000000008000000A),
|
||||
I64(0x000000008000808B), I64(0x800000000000008B), I64(0x8000000000008089), I64(0x8000000000008003),
|
||||
I64(0x8000000000008002), I64(0x8000000000000080), I64(0x000000000000800A), I64(0x800000008000000A),
|
||||
I64(0x8000000080008081), I64(0x8000000000008080), I64(0x0000000080000001), I64(0x8000000080008008)
|
||||
};
|
||||
|
||||
/* Initializing a sha3 context for given number of output bits */
|
||||
WASM_EXPORT
|
||||
void Hash_Init(uint32_t bits) {
|
||||
/* NB: The Keccak capacity parameter = bits * 2 */
|
||||
uint32_t rate = 1600 - bits * 2;
|
||||
|
||||
for(int i = 0; i < sha3_max_permutation_size; i++) {
|
||||
ctx->hash[i] = 0;
|
||||
}
|
||||
|
||||
for(int i = 0; i < sha3_max_rate_in_qwords; i++) {
|
||||
ctx->message[i] = 0;
|
||||
}
|
||||
|
||||
ctx->rest = 0;
|
||||
ctx->block_size = rate / 8;
|
||||
}
|
||||
|
||||
#define XORED_A(i) A[(i)] ^ A[(i) + 5] ^ A[(i) + 10] ^ A[(i) + 15] ^ A[(i) + 20]
|
||||
#define THETA_STEP(i) \
|
||||
A[(i)] ^= D[(i)]; \
|
||||
A[(i) + 5] ^= D[(i)]; \
|
||||
A[(i) + 10] ^= D[(i)]; \
|
||||
A[(i) + 15] ^= D[(i)]; \
|
||||
A[(i) + 20] ^= D[(i)]
|
||||
|
||||
/* Keccak theta() transformation */
|
||||
static void keccak_theta(uint64_t* A) {
|
||||
uint64_t D[5];
|
||||
D[0] = ROTL64(XORED_A(1), 1) ^ XORED_A(4);
|
||||
D[1] = ROTL64(XORED_A(2), 1) ^ XORED_A(0);
|
||||
D[2] = ROTL64(XORED_A(3), 1) ^ XORED_A(1);
|
||||
D[3] = ROTL64(XORED_A(4), 1) ^ XORED_A(2);
|
||||
D[4] = ROTL64(XORED_A(0), 1) ^ XORED_A(3);
|
||||
THETA_STEP(0);
|
||||
THETA_STEP(1);
|
||||
THETA_STEP(2);
|
||||
THETA_STEP(3);
|
||||
THETA_STEP(4);
|
||||
}
|
||||
|
||||
/* Keccak pi() transformation */
|
||||
static void keccak_pi(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 */
|
||||
}
|
||||
|
||||
#define CHI_STEP(i) \
|
||||
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
|
||||
|
||||
/* Keccak chi() transformation */
|
||||
static void keccak_chi(uint64_t* A) {
|
||||
uint64_t A0, A1;
|
||||
CHI_STEP(0);
|
||||
CHI_STEP(5);
|
||||
CHI_STEP(10);
|
||||
CHI_STEP(15);
|
||||
CHI_STEP(20);
|
||||
}
|
||||
|
||||
static void sha3_permutation(uint64_t* state) {
|
||||
for (int 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 sha3_process_block(
|
||||
uint64_t hash[25], const uint64_t* block, uint32_t block_size
|
||||
) {
|
||||
/* expanded loop */
|
||||
hash[0] ^= block[0];
|
||||
hash[1] ^= block[1];
|
||||
hash[2] ^= block[2];
|
||||
hash[3] ^= block[3];
|
||||
hash[4] ^= block[4];
|
||||
hash[5] ^= block[5];
|
||||
hash[6] ^= block[6];
|
||||
hash[7] ^= block[7];
|
||||
hash[8] ^= block[8];
|
||||
/* if not sha3-512 */
|
||||
if (block_size > 72) {
|
||||
hash[9] ^= block[9];
|
||||
hash[10] ^= block[10];
|
||||
hash[11] ^= block[11];
|
||||
hash[12] ^= block[12];
|
||||
/* if not sha3-384 */
|
||||
if (block_size > 104) {
|
||||
hash[13] ^= block[13];
|
||||
hash[14] ^= block[14];
|
||||
hash[15] ^= block[15];
|
||||
hash[16] ^= block[16];
|
||||
/* if not sha3-256 */
|
||||
if (block_size > 136) {
|
||||
hash[17] ^= block[17];
|
||||
#ifdef FULL_SHA3_FAMILY_SUPPORT
|
||||
/* if not sha3-224 */
|
||||
if (block_size > 144) {
|
||||
hash[18] ^= block[18];
|
||||
hash[19] ^= block[19];
|
||||
hash[20] ^= block[20];
|
||||
hash[21] ^= block[21];
|
||||
hash[22] ^= block[22];
|
||||
hash[23] ^= block[23];
|
||||
hash[24] ^= block[24];
|
||||
}
|
||||
#endif
|
||||
}
|
||||
}
|
||||
}
|
||||
/* make a permutation of the hash */
|
||||
sha3_permutation(hash);
|
||||
}
|
||||
|
||||
#define SHA3_FINALIZED 0x80000000
|
||||
|
||||
/**
|
||||
* Calculate message hash.
|
||||
* Can be called repeatedly with chunks of the message to be hashed.
|
||||
*
|
||||
* @param msg message chunk
|
||||
* @param size length of the message chunk
|
||||
*/
|
||||
WASM_EXPORT
|
||||
void Hash_Update(uint32_t size) {
|
||||
const uint8_t* msg = main_buffer;
|
||||
uint32_t index = (uint32_t)ctx->rest;
|
||||
uint32_t block_size = (uint32_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 (index) {
|
||||
uint32_t left = block_size - index;
|
||||
uint32_t end = size < left ? size : left;
|
||||
uint8_t* msg_pointer = (uint8_t*)ctx->message + index;
|
||||
for (uint32_t i = 0; i < end; i++) {
|
||||
msg_pointer[i] = msg[i];
|
||||
}
|
||||
if (size < left) return;
|
||||
|
||||
/* process partial block */
|
||||
sha3_process_block(ctx->hash, ctx->message, block_size);
|
||||
msg += left;
|
||||
size -= left;
|
||||
}
|
||||
|
||||
while (size >= block_size) {
|
||||
uint64_t* aligned_message_block = (uint64_t*)msg;
|
||||
|
||||
sha3_process_block(ctx->hash, aligned_message_block, block_size);
|
||||
msg += block_size;
|
||||
size -= block_size;
|
||||
}
|
||||
|
||||
if (size) {
|
||||
/* save leftovers */
|
||||
uint8_t* msg_pointer = (uint8_t*)ctx->message;
|
||||
for (uint8_t i = 0; i < size; i++) {
|
||||
msg_pointer[i] = msg[i];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Store calculated hash into the given array.
|
||||
*/
|
||||
WASM_EXPORT
|
||||
void Hash_Final(uint8_t padding) {
|
||||
uint32_t digest_length = 100 - ctx->block_size / 2;
|
||||
const uint32_t block_size = ctx->block_size;
|
||||
|
||||
if (!(ctx->rest & SHA3_FINALIZED)) {
|
||||
/* clear the rest of the data queue */
|
||||
int8_t* start = (int8_t*)ctx->message + ctx->rest;
|
||||
for (int i = 0; i < block_size - ctx->rest; i++) {
|
||||
start[i] = 0;
|
||||
}
|
||||
((int8_t*)ctx->message)[ctx->rest] |= padding;
|
||||
((int8_t*)ctx->message)[block_size - 1] |= 0x80;
|
||||
|
||||
/* process final block */
|
||||
sha3_process_block(ctx->hash, ctx->message, block_size);
|
||||
ctx->rest = SHA3_FINALIZED; /* mark context as finalized */
|
||||
}
|
||||
|
||||
uint32_t* array32 = (uint32_t*)main_buffer;
|
||||
uint32_t* hash32 = (uint32_t*)ctx->hash;
|
||||
for (uint32_t i = 0; i < digest_length / 4; i++) {
|
||||
array32[i] = hash32[i];
|
||||
}
|
||||
}
|
||||
|
||||
WASM_EXPORT
|
||||
const uint32_t STATE_SIZE = sizeof(*ctx);
|
||||
|
||||
WASM_EXPORT
|
||||
uint8_t* Hash_GetState() {
|
||||
return (uint8_t*) ctx;
|
||||
}
|
||||
|
||||
WASM_EXPORT
|
||||
void Hash_Calculate(uint32_t length, uint32_t initParam, uint8_t finalParam) {
|
||||
Hash_Init(initParam);
|
||||
Hash_Update(length);
|
||||
Hash_Final(finalParam);
|
||||
}
|
||||
+308
@@ -0,0 +1,308 @@
|
||||
/* sha512.c - an implementation of SHA-384/512 hash functions
|
||||
* based on FIPS 180-3 (Federal Information Processing Standart).
|
||||
*
|
||||
* Copyright (c) 2010, Aleksey Kravchenko <rhash.admin@gmail.com>
|
||||
*
|
||||
* Permission to use, copy, modify, and/or distribute this software for any
|
||||
* purpose with or without fee is hereby granted.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH
|
||||
* REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
|
||||
* AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT,
|
||||
* INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
|
||||
* LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
|
||||
* OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
|
||||
* PERFORMANCE OF THIS SOFTWARE.
|
||||
|
||||
* Modified for hash-wasm by Dani Biró
|
||||
*/
|
||||
|
||||
#define WITH_BUFFER
|
||||
#include "hash-wasm.h"
|
||||
|
||||
#define sha512_block_size 128
|
||||
#define sha512_hash_size 64
|
||||
#define sha384_hash_size 48
|
||||
#define I64(x) x##ULL
|
||||
#define ROTR64(qword, n) ((qword) >> (n) ^ ((qword) << (64 - (n))))
|
||||
#define bswap_64(x) __builtin_bswap64(x)
|
||||
|
||||
struct sha512_ctx {
|
||||
uint64_t message[16]; /* 1024-bit buffer for leftovers */
|
||||
uint64_t length; /* number of processed bytes */
|
||||
uint64_t hash[8]; /* 512-bit algorithm internal hashing state */
|
||||
uint32_t digest_length; /* length of the algorithm digest in bytes */
|
||||
};
|
||||
|
||||
struct sha512_ctx sctx;
|
||||
struct sha512_ctx* ctx = &sctx;
|
||||
|
||||
/* SHA-384 and SHA-512 constants for 80 rounds. These qwords represent
|
||||
* the first 64 bits of the fractional parts of the cube
|
||||
* roots of the first 80 prime numbers. */
|
||||
static const uint64_t rhash_k512[80] = {
|
||||
I64(0x428a2f98d728ae22), I64(0x7137449123ef65cd), I64(0xb5c0fbcfec4d3b2f),
|
||||
I64(0xe9b5dba58189dbbc), I64(0x3956c25bf348b538), I64(0x59f111f1b605d019),
|
||||
I64(0x923f82a4af194f9b), I64(0xab1c5ed5da6d8118), I64(0xd807aa98a3030242),
|
||||
I64(0x12835b0145706fbe), I64(0x243185be4ee4b28c), I64(0x550c7dc3d5ffb4e2),
|
||||
I64(0x72be5d74f27b896f), I64(0x80deb1fe3b1696b1), I64(0x9bdc06a725c71235),
|
||||
I64(0xc19bf174cf692694), I64(0xe49b69c19ef14ad2), I64(0xefbe4786384f25e3),
|
||||
I64(0x0fc19dc68b8cd5b5), I64(0x240ca1cc77ac9c65), I64(0x2de92c6f592b0275),
|
||||
I64(0x4a7484aa6ea6e483), I64(0x5cb0a9dcbd41fbd4), I64(0x76f988da831153b5),
|
||||
I64(0x983e5152ee66dfab), I64(0xa831c66d2db43210), I64(0xb00327c898fb213f),
|
||||
I64(0xbf597fc7beef0ee4), I64(0xc6e00bf33da88fc2), I64(0xd5a79147930aa725),
|
||||
I64(0x06ca6351e003826f), I64(0x142929670a0e6e70), I64(0x27b70a8546d22ffc),
|
||||
I64(0x2e1b21385c26c926), I64(0x4d2c6dfc5ac42aed), I64(0x53380d139d95b3df),
|
||||
I64(0x650a73548baf63de), I64(0x766a0abb3c77b2a8), I64(0x81c2c92e47edaee6),
|
||||
I64(0x92722c851482353b), I64(0xa2bfe8a14cf10364), I64(0xa81a664bbc423001),
|
||||
I64(0xc24b8b70d0f89791), I64(0xc76c51a30654be30), I64(0xd192e819d6ef5218),
|
||||
I64(0xd69906245565a910), I64(0xf40e35855771202a), I64(0x106aa07032bbd1b8),
|
||||
I64(0x19a4c116b8d2d0c8), I64(0x1e376c085141ab53), I64(0x2748774cdf8eeb99),
|
||||
I64(0x34b0bcb5e19b48a8), I64(0x391c0cb3c5c95a63), I64(0x4ed8aa4ae3418acb),
|
||||
I64(0x5b9cca4f7763e373), I64(0x682e6ff3d6b2b8a3), I64(0x748f82ee5defb2fc),
|
||||
I64(0x78a5636f43172f60), I64(0x84c87814a1f0ab72), I64(0x8cc702081a6439ec),
|
||||
I64(0x90befffa23631e28), I64(0xa4506cebde82bde9), I64(0xbef9a3f7b2c67915),
|
||||
I64(0xc67178f2e372532b), I64(0xca273eceea26619c), I64(0xd186b8c721c0c207),
|
||||
I64(0xeada7dd6cde0eb1e), I64(0xf57d4f7fee6ed178), I64(0x06f067aa72176fba),
|
||||
I64(0x0a637dc5a2c898a6), I64(0x113f9804bef90dae), I64(0x1b710b35131c471b),
|
||||
I64(0x28db77f523047d84), I64(0x32caab7b40c72493), I64(0x3c9ebe0a15c9bebc),
|
||||
I64(0x431d67c49c100d4c), I64(0x4cc5d4becb3e42b6), I64(0x597f299cfc657e2a),
|
||||
I64(0x5fcb6fab3ad6faec), I64(0x6c44198c4a475817)
|
||||
};
|
||||
|
||||
/* The SHA512/384 functions defined by FIPS 180-3, 4.1.3 */
|
||||
/* Optimized version of Ch(x,y,z)=((x & y) | (~x & z)) */
|
||||
#define Ch(x, y, z) ((z) ^ ((x) & ((y) ^ (z))))
|
||||
/* Optimized version of Maj(x,y,z)=((x & y) ^ (x & z) ^ (y & z)) */
|
||||
#define Maj(x, y, z) (((x) & (y)) ^ ((z) & ((x) ^ (y))))
|
||||
|
||||
#define Sigma0(x) (ROTR64((x), 28) ^ ROTR64((x), 34) ^ ROTR64((x), 39))
|
||||
#define Sigma1(x) (ROTR64((x), 14) ^ ROTR64((x), 18) ^ ROTR64((x), 41))
|
||||
#define sigma0(x) (ROTR64((x), 1) ^ ROTR64((x), 8) ^ ((x) >> 7))
|
||||
#define sigma1(x) (ROTR64((x), 19) ^ ROTR64((x), 61) ^ ((x) >> 6))
|
||||
|
||||
/* Recalculate element n-th of circular buffer W using formula
|
||||
* W[n] = sigma1(W[n - 2]) + W[n - 7] + sigma0(W[n - 15]) + W[n - 16]; */
|
||||
#define RECALCULATE_W(W, n) \
|
||||
(W[n] += \
|
||||
(sigma1(W[(n - 2) & 15]) + W[(n - 7) & 15] + sigma0(W[(n - 15) & 15])))
|
||||
|
||||
#define ROUND(a, b, c, d, e, f, g, h, k, data) \
|
||||
{ \
|
||||
uint64_t T1 = h + Sigma1(e) + Ch(e, f, g) + k + (data); \
|
||||
d += T1, h = T1 + Sigma0(a) + Maj(a, b, c); \
|
||||
}
|
||||
#define ROUND_1_16(a, b, c, d, e, f, g, h, n) \
|
||||
ROUND(a, b, c, d, e, f, g, h, rhash_k512[n], W[n] = bswap_64(block[n]))
|
||||
#define ROUND_17_80(a, b, c, d, e, f, g, h, n) \
|
||||
ROUND(a, b, c, d, e, f, g, h, k[n], RECALCULATE_W(W, n))
|
||||
|
||||
/**
|
||||
* Initialize context before calculating hash.
|
||||
*
|
||||
*/
|
||||
void sha512_init() {
|
||||
/* Initial values. These words were obtained by taking the first 32
|
||||
* bits of the fractional parts of the square roots of the first
|
||||
* eight prime numbers. */
|
||||
static const uint64_t SHA512_H0[8] = {
|
||||
I64(0x6a09e667f3bcc908), I64(0xbb67ae8584caa73b), I64(0x3c6ef372fe94f82b),
|
||||
I64(0xa54ff53a5f1d36f1), I64(0x510e527fade682d1), I64(0x9b05688c2b3e6c1f),
|
||||
I64(0x1f83d9abfb41bd6b), I64(0x5be0cd19137e2179)
|
||||
};
|
||||
|
||||
ctx->length = 0;
|
||||
ctx->digest_length = sha512_hash_size;
|
||||
|
||||
/* initialize algorithm state */
|
||||
#pragma clang loop unroll(full)
|
||||
for (uint8_t i = 0; i < 8; i++) {
|
||||
ctx->hash[i] = SHA512_H0[i];
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Initialize context before calculaing hash.
|
||||
*
|
||||
*/
|
||||
void sha384_init() {
|
||||
/* Initial values from FIPS 180-3. These words were obtained by taking
|
||||
* the first sixty-four bits of the fractional parts of the square
|
||||
* roots of ninth through sixteenth prime numbers. */
|
||||
static const uint64_t SHA384_H0[8] = {
|
||||
I64(0xcbbb9d5dc1059ed8), I64(0x629a292a367cd507), I64(0x9159015a3070dd17),
|
||||
I64(0x152fecd8f70e5939), I64(0x67332667ffc00b31), I64(0x8eb44a8768581511),
|
||||
I64(0xdb0c2e0d64f98fa7), I64(0x47b5481dbefa4fa4)
|
||||
};
|
||||
|
||||
ctx->length = 0;
|
||||
ctx->digest_length = sha384_hash_size;
|
||||
|
||||
#pragma clang loop unroll(full)
|
||||
for (uint8_t i = 0; i < 8; i++) {
|
||||
ctx->hash[i] = SHA384_H0[i];
|
||||
}
|
||||
}
|
||||
|
||||
WASM_EXPORT
|
||||
void Hash_Init(uint32_t bits) {
|
||||
if (bits == 384) {
|
||||
sha384_init();
|
||||
} else {
|
||||
sha512_init();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* The core transformation. Process a 512-bit block.
|
||||
*
|
||||
* @param hash algorithm state
|
||||
* @param block the message block to process
|
||||
*/
|
||||
static void sha512_process_block(uint64_t hash[8], uint64_t block[16]) {
|
||||
uint64_t A, B, C, D, E, F, G, H;
|
||||
uint64_t W[16];
|
||||
const uint64_t* k;
|
||||
int i;
|
||||
|
||||
A = hash[0], B = hash[1], C = hash[2], D = hash[3];
|
||||
E = hash[4], F = hash[5], G = hash[6], H = hash[7];
|
||||
|
||||
/* Compute SHA using alternate Method: FIPS 180-3 6.1.3 */
|
||||
ROUND_1_16(A, B, C, D, E, F, G, H, 0);
|
||||
ROUND_1_16(H, A, B, C, D, E, F, G, 1);
|
||||
ROUND_1_16(G, H, A, B, C, D, E, F, 2);
|
||||
ROUND_1_16(F, G, H, A, B, C, D, E, 3);
|
||||
ROUND_1_16(E, F, G, H, A, B, C, D, 4);
|
||||
ROUND_1_16(D, E, F, G, H, A, B, C, 5);
|
||||
ROUND_1_16(C, D, E, F, G, H, A, B, 6);
|
||||
ROUND_1_16(B, C, D, E, F, G, H, A, 7);
|
||||
ROUND_1_16(A, B, C, D, E, F, G, H, 8);
|
||||
ROUND_1_16(H, A, B, C, D, E, F, G, 9);
|
||||
ROUND_1_16(G, H, A, B, C, D, E, F, 10);
|
||||
ROUND_1_16(F, G, H, A, B, C, D, E, 11);
|
||||
ROUND_1_16(E, F, G, H, A, B, C, D, 12);
|
||||
ROUND_1_16(D, E, F, G, H, A, B, C, 13);
|
||||
ROUND_1_16(C, D, E, F, G, H, A, B, 14);
|
||||
ROUND_1_16(B, C, D, E, F, G, H, A, 15);
|
||||
|
||||
#pragma clang loop unroll(full)
|
||||
for (i = 16, k = &rhash_k512[16]; i < 80; i += 16, k += 16) {
|
||||
ROUND_17_80(A, B, C, D, E, F, G, H, 0);
|
||||
ROUND_17_80(H, A, B, C, D, E, F, G, 1);
|
||||
ROUND_17_80(G, H, A, B, C, D, E, F, 2);
|
||||
ROUND_17_80(F, G, H, A, B, C, D, E, 3);
|
||||
ROUND_17_80(E, F, G, H, A, B, C, D, 4);
|
||||
ROUND_17_80(D, E, F, G, H, A, B, C, 5);
|
||||
ROUND_17_80(C, D, E, F, G, H, A, B, 6);
|
||||
ROUND_17_80(B, C, D, E, F, G, H, A, 7);
|
||||
ROUND_17_80(A, B, C, D, E, F, G, H, 8);
|
||||
ROUND_17_80(H, A, B, C, D, E, F, G, 9);
|
||||
ROUND_17_80(G, H, A, B, C, D, E, F, 10);
|
||||
ROUND_17_80(F, G, H, A, B, C, D, E, 11);
|
||||
ROUND_17_80(E, F, G, H, A, B, C, D, 12);
|
||||
ROUND_17_80(D, E, F, G, H, A, B, C, 13);
|
||||
ROUND_17_80(C, D, E, F, G, H, A, B, 14);
|
||||
ROUND_17_80(B, C, D, E, F, G, H, A, 15);
|
||||
}
|
||||
|
||||
hash[0] += A, hash[1] += B, hash[2] += C, hash[3] += D;
|
||||
hash[4] += E, hash[5] += F, hash[6] += G, hash[7] += H;
|
||||
}
|
||||
|
||||
/**
|
||||
* Calculate message hash.
|
||||
* Can be called repeatedly with chunks of the message to be hashed.
|
||||
*
|
||||
* @param size length of the message chunk
|
||||
*/
|
||||
WASM_EXPORT
|
||||
void Hash_Update(uint32_t size) {
|
||||
const uint8_t* msg = main_buffer;
|
||||
uint32_t index = (uint32_t)ctx->length & 127;
|
||||
ctx->length += size;
|
||||
|
||||
/* fill partial block */
|
||||
if (index) {
|
||||
uint32_t left = sha512_block_size - index;
|
||||
uint32_t end = size < left ? size : left;
|
||||
uint8_t* message8 = (uint8_t*)ctx->message;
|
||||
for (uint8_t i = 0; i < end; i++) {
|
||||
*(message8 + index + i) = msg[i];
|
||||
}
|
||||
if (size < left) return;
|
||||
|
||||
/* process partial block */
|
||||
sha512_process_block(ctx->hash, ctx->message);
|
||||
msg += left;
|
||||
size -= left;
|
||||
}
|
||||
|
||||
while (size >= sha512_block_size) {
|
||||
uint64_t* aligned_message_block = (uint64_t*)msg;
|
||||
|
||||
sha512_process_block(ctx->hash, aligned_message_block);
|
||||
msg += sha512_block_size;
|
||||
size -= sha512_block_size;
|
||||
}
|
||||
|
||||
if (size) {
|
||||
/* save leftovers */
|
||||
for (uint8_t i = 0; i < size; i++) {
|
||||
*(((uint8_t*)ctx->message) + i) = msg[i];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Store calculated hash into the given array.
|
||||
*/
|
||||
WASM_EXPORT
|
||||
void Hash_Final() {
|
||||
uint32_t index = ((uint32_t)ctx->length & 127) >> 3;
|
||||
uint32_t shift = ((uint32_t)ctx->length & 7) * 8;
|
||||
|
||||
/* pad message and process the last block */
|
||||
|
||||
/* append the byte 0x80 to the message */
|
||||
ctx->message[index] &= ~(I64(0xFFFFFFFFFFFFFFFF) << shift);
|
||||
ctx->message[index++] ^= I64(0x80) << shift;
|
||||
|
||||
/* if no room left in the message to store 128-bit message length */
|
||||
if (index >= 15) {
|
||||
if (index == 15) ctx->message[index] = 0;
|
||||
sha512_process_block(ctx->hash, ctx->message);
|
||||
index = 0;
|
||||
}
|
||||
|
||||
while (index < 15) {
|
||||
ctx->message[index++] = 0;
|
||||
}
|
||||
|
||||
ctx->message[15] = bswap_64(ctx->length << 3);
|
||||
sha512_process_block(ctx->hash, ctx->message);
|
||||
|
||||
#pragma clang loop unroll(full)
|
||||
for (int32_t i = 7; i >= 0; i--) {
|
||||
ctx->hash[i] = bswap_64(ctx->hash[i]);
|
||||
}
|
||||
|
||||
for (uint8_t i = 0; i < ctx->digest_length; i++) {
|
||||
main_buffer[i] = *(((uint8_t*)ctx->hash) + i);
|
||||
}
|
||||
}
|
||||
|
||||
WASM_EXPORT
|
||||
const uint32_t STATE_SIZE = sizeof(*ctx);
|
||||
|
||||
WASM_EXPORT
|
||||
uint8_t* Hash_GetState() {
|
||||
return (uint8_t*) ctx;
|
||||
}
|
||||
|
||||
WASM_EXPORT
|
||||
void Hash_Calculate(uint32_t length, uint32_t initParam) {
|
||||
Hash_Init(initParam);
|
||||
Hash_Update(length);
|
||||
Hash_Final();
|
||||
}
|
||||
+231
@@ -0,0 +1,231 @@
|
||||
/*******************************************************************************
|
||||
* SM3 function implementation
|
||||
* Copyright 2016 Yanbo Li dreamfly281@gmail.com, goldboar@163.com
|
||||
* MIT License
|
||||
*
|
||||
* Modified for hash-wasm by Dani Biró
|
||||
*/
|
||||
|
||||
|
||||
#define WITH_BUFFER
|
||||
#include "hash-wasm.h"
|
||||
|
||||
#define SM3_DIGEST_LEN 32
|
||||
|
||||
#define u8 uint8_t
|
||||
#define u32 uint32_t
|
||||
|
||||
struct sm3_ctx {
|
||||
u32 total[2];
|
||||
u32 state[8];
|
||||
u8 buffer[64];
|
||||
};
|
||||
|
||||
#define S(x,n) ((x << n) | (x >> (32 - n)))
|
||||
|
||||
#define P0(x) (x ^ S(x, 9) ^ S(x,17))
|
||||
#define P1(x) (x ^ S(x,15) ^ S(x,23))
|
||||
|
||||
#define PW(t) \
|
||||
( \
|
||||
temp = W[t - 16] ^ W[t - 9] ^ (S(W[t - 3], 15)), \
|
||||
P1(temp) ^ W[t - 6] ^ (S(W[t - 13], 7)) \
|
||||
)
|
||||
|
||||
#define FF1(x,y,z) (x ^ y ^ z)
|
||||
#define FF2(x,y,z) ((x & y) | (x & z) | (y & z))
|
||||
|
||||
#define GG1(x,y,z) (x ^ y ^ z)
|
||||
#define GG2(x,y,z) ((x & y) | ((~x) & z))
|
||||
|
||||
#define T1 0x79cc4519
|
||||
#define T2 0x7a879d8a
|
||||
|
||||
#define bswap_32(x) __builtin_bswap32(x)
|
||||
|
||||
void sm3_init(struct sm3_ctx *ctx) {
|
||||
ctx->total[0] = 0;
|
||||
ctx->total[1] = 0;
|
||||
|
||||
ctx->state[0] = 0x7380166f;
|
||||
ctx->state[1] = 0x4914b2b9;
|
||||
ctx->state[2] = 0x172442d7;
|
||||
ctx->state[3] = 0xda8a0600;
|
||||
ctx->state[4] = 0xa96f30bc;
|
||||
ctx->state[5] = 0x163138aa;
|
||||
ctx->state[6] = 0xe38dee4d;
|
||||
ctx->state[7] = 0xb0fb0e4e;
|
||||
}
|
||||
|
||||
static void sm3_process(struct sm3_ctx *ctx, const u8 data[64]) {
|
||||
u32 temp, W[68], WP[64], A, B, C, D, E, F, G, H, SS1, SS2, TT1, TT2;
|
||||
int j, k;
|
||||
|
||||
#pragma clang loop unroll(full)
|
||||
for (int i = 0; i < 16; i++) {
|
||||
W[i] = bswap_32(((u32 *) data)[i]);
|
||||
}
|
||||
|
||||
W[16] = PW(16);
|
||||
W[17] = PW(17);
|
||||
W[18] = PW(18);
|
||||
W[19] = PW(19);
|
||||
|
||||
A = ctx->state[0];
|
||||
B = ctx->state[1];
|
||||
C = ctx->state[2];
|
||||
D = ctx->state[3];
|
||||
E = ctx->state[4];
|
||||
F = ctx->state[5];
|
||||
G = ctx->state[6];
|
||||
H = ctx->state[7];
|
||||
|
||||
// #pragma clang loop unroll(full)
|
||||
for (int i = 0; i < 16; i++) {
|
||||
WP[i] = W[i] ^ W[i+4];
|
||||
|
||||
SS1 = S(A, 12) + E + S(T1, i);
|
||||
SS1 = S(SS1, 7);
|
||||
SS2 = SS1 ^ S(A, 12);
|
||||
TT1 = FF1(A, B, C) + D + SS2 + WP[i];
|
||||
TT2 = GG1(E, F, G) + H + SS1 + W[i];
|
||||
D = C;
|
||||
C = S(B,9);
|
||||
B = A;
|
||||
A = TT1;
|
||||
H = G;
|
||||
G = S(F,19);
|
||||
F = E;
|
||||
E = P0(TT2);
|
||||
}
|
||||
|
||||
// #pragma clang loop unroll(full)
|
||||
for (int i = 16; i < 64; i++) {
|
||||
k = i + 4;
|
||||
W[k] = PW(k);
|
||||
WP[i] = W[i] ^ W[i + 4];
|
||||
|
||||
j = i % 32;
|
||||
|
||||
SS1 = S(A, 12) + E + S(T2, j);
|
||||
SS1 = S(SS1, 7);
|
||||
SS2 = SS1 ^ S(A, 12);
|
||||
TT1 = FF2(A, B, C) + D + SS2 + WP[i];
|
||||
TT2 = GG2(E, F, G) + H + SS1 + W[i];
|
||||
D = C;
|
||||
C = S(B, 9);
|
||||
B = A;
|
||||
A = TT1;
|
||||
H = G;
|
||||
G = S(F, 19);
|
||||
F = E;
|
||||
E = P0(TT2);
|
||||
}
|
||||
|
||||
ctx->state[0] ^= A;
|
||||
ctx->state[1] ^= B;
|
||||
ctx->state[2] ^= C;
|
||||
ctx->state[3] ^= D;
|
||||
ctx->state[4] ^= E;
|
||||
ctx->state[5] ^= F;
|
||||
ctx->state[6] ^= G;
|
||||
ctx->state[7] ^= H;
|
||||
}
|
||||
|
||||
static void sm3_update(struct sm3_ctx *ctx, const u8 *msg, u32 len) {
|
||||
u32 left, fill;
|
||||
|
||||
if (!len) {
|
||||
return;
|
||||
}
|
||||
|
||||
left = ctx->total[0] & 0x3F;
|
||||
fill = 64 - left;
|
||||
|
||||
ctx->total[0] += len;
|
||||
ctx->total[0] &= 0xFFFFFFFF;
|
||||
|
||||
if (ctx->total[0] < len) {
|
||||
ctx->total[1]++;
|
||||
}
|
||||
|
||||
if (left && (len >= fill)) {
|
||||
memcpy((void *)(ctx->buffer + left), (void *)msg, fill);
|
||||
sm3_process(ctx, ctx->buffer);
|
||||
len -= fill;
|
||||
msg += fill;
|
||||
left = 0;
|
||||
}
|
||||
|
||||
while (len >= 64) {
|
||||
sm3_process(ctx, msg);
|
||||
len -= 64;
|
||||
msg += 64;
|
||||
}
|
||||
|
||||
if (len) {
|
||||
memcpy((void *)(ctx->buffer + left), (void *)msg, len);
|
||||
}
|
||||
}
|
||||
|
||||
static u8 sm3_padding[64] = {
|
||||
0x80, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
|
||||
};
|
||||
|
||||
static void sm3_finish(struct sm3_ctx *ctx, u8 digest[32]) {
|
||||
u32 last, padn;
|
||||
u32 high, low;
|
||||
u8 msglen[8];
|
||||
|
||||
high = (ctx->total[0] >> 29)
|
||||
| (ctx->total[1] << 3);
|
||||
low = (ctx->total[0] << 3);
|
||||
|
||||
((u32 *)msglen)[0] = bswap_32(high);
|
||||
((u32 *)msglen)[1] = bswap_32(low);
|
||||
|
||||
last = ctx->total[0] & 0x3F;
|
||||
padn = (last < 56 ) ? (56 - last) : (120 - last);
|
||||
|
||||
sm3_update(ctx, sm3_padding, padn);
|
||||
sm3_update(ctx, msglen, 8);
|
||||
|
||||
for (int i = 0; i < 8; i++) {
|
||||
((u32 *)digest)[i] = bswap_32(ctx->state[i]);
|
||||
}
|
||||
}
|
||||
|
||||
struct sm3_ctx ctx;
|
||||
|
||||
WASM_EXPORT
|
||||
void Hash_Init() {
|
||||
sm3_init(&ctx);
|
||||
}
|
||||
|
||||
WASM_EXPORT
|
||||
void Hash_Update(uint32_t size) {
|
||||
sm3_update(&ctx, main_buffer, size);
|
||||
}
|
||||
|
||||
WASM_EXPORT
|
||||
void Hash_Final() {
|
||||
sm3_finish(&ctx, main_buffer);
|
||||
}
|
||||
|
||||
WASM_EXPORT
|
||||
const uint32_t STATE_SIZE = sizeof(ctx);
|
||||
|
||||
WASM_EXPORT
|
||||
uint8_t* Hash_GetState() {
|
||||
return (uint8_t*) &ctx;
|
||||
}
|
||||
|
||||
WASM_EXPORT
|
||||
void Hash_Calculate(uint32_t length) {
|
||||
Hash_Init();
|
||||
Hash_Update(length);
|
||||
Hash_Final();
|
||||
}
|
||||
+243
@@ -0,0 +1,243 @@
|
||||
/**
|
||||
* Whirlpool hash in C
|
||||
*
|
||||
* Copyright (c) 2017 Project Nayuki. (MIT License)
|
||||
* https://www.nayuki.io/page/fast-whirlpool-hash-in-x86-assembly
|
||||
*
|
||||
* 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,
|
||||
* subject to the following conditions:
|
||||
* - The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
* - The Software is provided "as is", without warranty of any kind, express or
|
||||
* implied, including but not limited to the warranties of merchantability,
|
||||
* fitness for a particular purpose and noninfringement. In no event shall the
|
||||
* authors or copyright holders be liable for any claim, damages or other
|
||||
* liability, whether in an action of contract, tort or otherwise, arising from,
|
||||
* out of or in connection with the Software or the use or other dealings in the
|
||||
* Software.
|
||||
*
|
||||
* Modified for hash-wasm by Dani Biró
|
||||
*/
|
||||
|
||||
#define WITH_BUFFER
|
||||
#include "hash-wasm.h"
|
||||
|
||||
#define BLOCK_LEN 64 // In bytes
|
||||
#define STATE_LEN 64 // In bytes
|
||||
|
||||
struct Whirlpool_CTX {
|
||||
uint8_t buffer[BLOCK_LEN]; /* buffer of data to hash */
|
||||
uint8_t hash[STATE_LEN]; /* the hashing state */
|
||||
uint32_t rem;
|
||||
uint64_t totalBytes;
|
||||
};
|
||||
|
||||
static struct Whirlpool_CTX sctx;
|
||||
|
||||
static void whirlpool_round(uint64_t block[static 8], const uint64_t key[static 8]);
|
||||
|
||||
static uint64_t ROUND_CONSTANTS[32] = {
|
||||
UINT64_C(0x4F01B887E8C62318), UINT64_C(0x52916F79F5D2A636), UINT64_C(0x357B0CA38E9BBC60), UINT64_C(0x57FE4B2EC2D7E01D),
|
||||
UINT64_C(0xDA4AF09FE5377715), UINT64_C(0x856BA0B10A29C958), UINT64_C(0x67053ECBF4105DBD), UINT64_C(0xD8957DA78B4127E4),
|
||||
UINT64_C(0x9E4717DD667CEEFB), UINT64_C(0x33835AAD07BF2DCA), UINT64_C(0xD94919C871AA0263), UINT64_C(0xB032269A885BE3F2),
|
||||
UINT64_C(0x4834CDBE80D50FE9), UINT64_C(0xAE1A68205F907AFF), UINT64_C(0x1273F164229354B4), UINT64_C(0x3D8DA1DBECC30840),
|
||||
UINT64_C(0x1BD682762BCF0097), UINT64_C(0xEF30F345506AAFB5), UINT64_C(0xC02FBA65EAA2553F), UINT64_C(0x8A0675924DFD1CDE),
|
||||
UINT64_C(0x96A8D4621F0EE6B2), UINT64_C(0x4C3972845925C5F9), UINT64_C(0x61E2A5D18C38785E), UINT64_C(0x04FCC7431E9C21B3),
|
||||
UINT64_C(0x247EDFFA0D6D9951), UINT64_C(0xEBB74E8F11CEAB3B), UINT64_C(0xD32C13B9F794813C), UINT64_C(0xA97F445603C46EE7),
|
||||
UINT64_C(0x6C9D0BDC53C1BB2A), UINT64_C(0xE11489AC46F67431), UINT64_C(0xEDD0B67009693A16), UINT64_C(0x86F85C28A49842CC),
|
||||
};
|
||||
|
||||
// Temporary state that doesn't need to be preserved between calls to _compress()
|
||||
uint64_t tempState[8];
|
||||
uint64_t tempBlock[8];
|
||||
uint64_t rcon[8] = {0};
|
||||
|
||||
void whirlpool_compress(uint8_t state[static 64], const uint8_t block[static 64]) {
|
||||
const int NUM_ROUNDS = 10; // Any number from 0 to 32 is allowed
|
||||
|
||||
// Initialization
|
||||
#pragma clang loop unroll(full)
|
||||
for (int i = 0; i < 8; i++) {
|
||||
int j = i << 3;
|
||||
uint64_t x = *(uint64_t*)(state + j);
|
||||
uint64_t y = *(uint64_t*)(block + j);
|
||||
tempState[i] = x;
|
||||
tempBlock[i] = x ^ y;
|
||||
}
|
||||
|
||||
// Hashing rounds
|
||||
#pragma clang loop unroll(full)
|
||||
for (int i = 0; i < NUM_ROUNDS; i++) {
|
||||
rcon[0] = ROUND_CONSTANTS[i];
|
||||
whirlpool_round(tempState, rcon);
|
||||
whirlpool_round(tempBlock, tempState);
|
||||
}
|
||||
|
||||
// Final combining
|
||||
#pragma clang loop unroll(full)
|
||||
for (uint8_t i = 0; i < 8; i++) {
|
||||
((uint64_t*)state)[i] ^= ((uint64_t*)block)[i] ^ tempBlock[i];
|
||||
}
|
||||
}
|
||||
|
||||
// The combined effect of gamma (SubBytes) and theta (MixRows)
|
||||
static uint64_t MAGIC_TABLE[256] = {
|
||||
UINT64_C(0xD83078C018601818), UINT64_C(0x2646AF05238C2323), UINT64_C(0xB891F97EC63FC6C6), UINT64_C(0xFBCD6F13E887E8E8), UINT64_C(0xCB13A14C87268787), UINT64_C(0x116D62A9B8DAB8B8), UINT64_C(0x0902050801040101), UINT64_C(0x0D9E6E424F214F4F),
|
||||
UINT64_C(0x9B6CEEAD36D83636), UINT64_C(0xFF510459A6A2A6A6), UINT64_C(0x0CB9BDDED26FD2D2), UINT64_C(0x0EF706FBF5F3F5F5), UINT64_C(0x96F280EF79F97979), UINT64_C(0x30DECE5F6FA16F6F), UINT64_C(0x6D3FEFFC917E9191), UINT64_C(0xF8A407AA52555252),
|
||||
UINT64_C(0x47C0FD27609D6060), UINT64_C(0x35657689BCCABCBC), UINT64_C(0x372BCDAC9B569B9B), UINT64_C(0x8A018C048E028E8E), UINT64_C(0xD25B1571A3B6A3A3), UINT64_C(0x6C183C600C300C0C), UINT64_C(0x84F68AFF7BF17B7B), UINT64_C(0x806AE1B535D43535),
|
||||
UINT64_C(0xF53A69E81D741D1D), UINT64_C(0xB3DD4753E0A7E0E0), UINT64_C(0x21B3ACF6D77BD7D7), UINT64_C(0x9C99ED5EC22FC2C2), UINT64_C(0x435C966D2EB82E2E), UINT64_C(0x29967A624B314B4B), UINT64_C(0x5DE121A3FEDFFEFE), UINT64_C(0xD5AE168257415757),
|
||||
UINT64_C(0xBD2A41A815541515), UINT64_C(0xE8EEB69F77C17777), UINT64_C(0x926EEBA537DC3737), UINT64_C(0x9ED7567BE5B3E5E5), UINT64_C(0x1323D98C9F469F9F), UINT64_C(0x23FD17D3F0E7F0F0), UINT64_C(0x20947F6A4A354A4A), UINT64_C(0x44A9959EDA4FDADA),
|
||||
UINT64_C(0xA2B025FA587D5858), UINT64_C(0xCF8FCA06C903C9C9), UINT64_C(0x7C528D5529A42929), UINT64_C(0x5A1422500A280A0A), UINT64_C(0x507F4FE1B1FEB1B1), UINT64_C(0xC95D1A69A0BAA0A0), UINT64_C(0x14D6DA7F6BB16B6B), UINT64_C(0xD917AB5C852E8585),
|
||||
UINT64_C(0x3C677381BDCEBDBD), UINT64_C(0x8FBA34D25D695D5D), UINT64_C(0x9020508010401010), UINT64_C(0x07F503F3F4F7F4F4), UINT64_C(0xDD8BC016CB0BCBCB), UINT64_C(0xD37CC6ED3EF83E3E), UINT64_C(0x2D0A112805140505), UINT64_C(0x78CEE61F67816767),
|
||||
UINT64_C(0x97D55373E4B7E4E4), UINT64_C(0x024EBB25279C2727), UINT64_C(0x7382583241194141), UINT64_C(0xA70B9D2C8B168B8B), UINT64_C(0xF6530151A7A6A7A7), UINT64_C(0xB2FA94CF7DE97D7D), UINT64_C(0x4937FBDC956E9595), UINT64_C(0x56AD9F8ED847D8D8),
|
||||
UINT64_C(0x70EB308BFBCBFBFB), UINT64_C(0xCDC17123EE9FEEEE), UINT64_C(0xBBF891C77CED7C7C), UINT64_C(0x71CCE31766856666), UINT64_C(0x7BA78EA6DD53DDDD), UINT64_C(0xAF2E4BB8175C1717), UINT64_C(0x458E460247014747), UINT64_C(0x1A21DC849E429E9E),
|
||||
UINT64_C(0xD489C51ECA0FCACA), UINT64_C(0x585A99752DB42D2D), UINT64_C(0x2E637991BFC6BFBF), UINT64_C(0x3F0E1B38071C0707), UINT64_C(0xAC472301AD8EADAD), UINT64_C(0xB0B42FEA5A755A5A), UINT64_C(0xEF1BB56C83368383), UINT64_C(0xB666FF8533CC3333),
|
||||
UINT64_C(0x5CC6F23F63916363), UINT64_C(0x12040A1002080202), UINT64_C(0x93493839AA92AAAA), UINT64_C(0xDEE2A8AF71D97171), UINT64_C(0xC68DCF0EC807C8C8), UINT64_C(0xD1327DC819641919), UINT64_C(0x3B92707249394949), UINT64_C(0x5FAF9A86D943D9D9),
|
||||
UINT64_C(0x31F91DC3F2EFF2F2), UINT64_C(0xA8DB484BE3ABE3E3), UINT64_C(0xB9B62AE25B715B5B), UINT64_C(0xBC0D9234881A8888), UINT64_C(0x3E29C8A49A529A9A), UINT64_C(0x0B4CBE2D26982626), UINT64_C(0xBF64FA8D32C83232), UINT64_C(0x597D4AE9B0FAB0B0),
|
||||
UINT64_C(0xF2CF6A1BE983E9E9), UINT64_C(0x771E33780F3C0F0F), UINT64_C(0x33B7A6E6D573D5D5), UINT64_C(0xF41DBA74803A8080), UINT64_C(0x27617C99BEC2BEBE), UINT64_C(0xEB87DE26CD13CDCD), UINT64_C(0x8968E4BD34D03434), UINT64_C(0x3290757A483D4848),
|
||||
UINT64_C(0x54E324ABFFDBFFFF), UINT64_C(0x8DF48FF77AF57A7A), UINT64_C(0x643DEAF4907A9090), UINT64_C(0x9DBE3EC25F615F5F), UINT64_C(0x3D40A01D20802020), UINT64_C(0x0FD0D56768BD6868), UINT64_C(0xCA3472D01A681A1A), UINT64_C(0xB7412C19AE82AEAE),
|
||||
UINT64_C(0x7D755EC9B4EAB4B4), UINT64_C(0xCEA8199A544D5454), UINT64_C(0x7F3BE5EC93769393), UINT64_C(0x2F44AA0D22882222), UINT64_C(0x63C8E907648D6464), UINT64_C(0x2AFF12DBF1E3F1F1), UINT64_C(0xCCE6A2BF73D17373), UINT64_C(0x82245A9012481212),
|
||||
UINT64_C(0x7A805D3A401D4040), UINT64_C(0x4810284008200808), UINT64_C(0x959BE856C32BC3C3), UINT64_C(0xDFC57B33EC97ECEC), UINT64_C(0x4DAB9096DB4BDBDB), UINT64_C(0xC05F1F61A1BEA1A1), UINT64_C(0x9107831C8D0E8D8D), UINT64_C(0xC87AC9F53DF43D3D),
|
||||
UINT64_C(0x5B33F1CC97669797), UINT64_C(0x0000000000000000), UINT64_C(0xF983D436CF1BCFCF), UINT64_C(0x6E5687452BAC2B2B), UINT64_C(0xE1ECB39776C57676), UINT64_C(0xE619B06482328282), UINT64_C(0x28B1A9FED67FD6D6), UINT64_C(0xC33677D81B6C1B1B),
|
||||
UINT64_C(0x74775BC1B5EEB5B5), UINT64_C(0xBE432911AF86AFAF), UINT64_C(0x1DD4DF776AB56A6A), UINT64_C(0xEAA00DBA505D5050), UINT64_C(0x578A4C1245094545), UINT64_C(0x38FB18CBF3EBF3F3), UINT64_C(0xAD60F09D30C03030), UINT64_C(0xC4C3742BEF9BEFEF),
|
||||
UINT64_C(0xDA7EC3E53FFC3F3F), UINT64_C(0xC7AA1C9255495555), UINT64_C(0xDB591079A2B2A2A2), UINT64_C(0xE9C96503EA8FEAEA), UINT64_C(0x6ACAEC0F65896565), UINT64_C(0x036968B9BAD2BABA), UINT64_C(0x4A5E93652FBC2F2F), UINT64_C(0x8E9DE74EC027C0C0),
|
||||
UINT64_C(0x60A181BEDE5FDEDE), UINT64_C(0xFC386CE01C701C1C), UINT64_C(0x46E72EBBFDD3FDFD), UINT64_C(0x1F9A64524D294D4D), UINT64_C(0x7639E0E492729292), UINT64_C(0xFAEABC8F75C97575), UINT64_C(0x360C1E3006180606), UINT64_C(0xAE0998248A128A8A),
|
||||
UINT64_C(0x4B7940F9B2F2B2B2), UINT64_C(0x85D15963E6BFE6E6), UINT64_C(0x7E1C36700E380E0E), UINT64_C(0xE73E63F81F7C1F1F), UINT64_C(0x55C4F73762956262), UINT64_C(0x3AB5A3EED477D4D4), UINT64_C(0x814D3229A89AA8A8), UINT64_C(0x5231F4C496629696),
|
||||
UINT64_C(0x62EF3A9BF9C3F9F9), UINT64_C(0xA397F666C533C5C5), UINT64_C(0x104AB13525942525), UINT64_C(0xABB220F259795959), UINT64_C(0xD015AE54842A8484), UINT64_C(0xC5E4A7B772D57272), UINT64_C(0xEC72DDD539E43939), UINT64_C(0x1698615A4C2D4C4C),
|
||||
UINT64_C(0x94BC3BCA5E655E5E), UINT64_C(0x9FF085E778FD7878), UINT64_C(0xE570D8DD38E03838), UINT64_C(0x980586148C0A8C8C), UINT64_C(0x17BFB2C6D163D1D1), UINT64_C(0xE4570B41A5AEA5A5), UINT64_C(0xA1D94D43E2AFE2E2), UINT64_C(0x4EC2F82F61996161),
|
||||
UINT64_C(0x427B45F1B3F6B3B3), UINT64_C(0x3442A51521842121), UINT64_C(0x0825D6949C4A9C9C), UINT64_C(0xEE3C66F01E781E1E), UINT64_C(0x6186522243114343), UINT64_C(0xB193FC76C73BC7C7), UINT64_C(0x4FE52BB3FCD7FCFC), UINT64_C(0x2408142004100404),
|
||||
UINT64_C(0xE3A208B251595151), UINT64_C(0x252FC7BC995E9999), UINT64_C(0x22DAC44F6DA96D6D), UINT64_C(0x651A39680D340D0D), UINT64_C(0x79E93583FACFFAFA), UINT64_C(0x69A384B6DF5BDFDF), UINT64_C(0xA9FC9BD77EE57E7E), UINT64_C(0x1948B43D24902424),
|
||||
UINT64_C(0xFE76D7C53BEC3B3B), UINT64_C(0x9A4B3D31AB96ABAB), UINT64_C(0xF081D13ECE1FCECE), UINT64_C(0x9922558811441111), UINT64_C(0x8303890C8F068F8F), UINT64_C(0x049C6B4A4E254E4E), UINT64_C(0x667351D1B7E6B7B7), UINT64_C(0xE0CB600BEB8BEBEB),
|
||||
UINT64_C(0xC178CCFD3CF03C3C), UINT64_C(0xFD1FBF7C813E8181), UINT64_C(0x4035FED4946A9494), UINT64_C(0x1CF30CEBF7FBF7F7), UINT64_C(0x186F67A1B9DEB9B9), UINT64_C(0x8B265F98134C1313), UINT64_C(0x51589C7D2CB02C2C), UINT64_C(0x05BBB8D6D36BD3D3),
|
||||
UINT64_C(0x8CD35C6BE7BBE7E7), UINT64_C(0x39DCCB576EA56E6E), UINT64_C(0xAA95F36EC437C4C4), UINT64_C(0x1B060F18030C0303), UINT64_C(0xDCAC138A56455656), UINT64_C(0x5E88491A440D4444), UINT64_C(0xA0FE9EDF7FE17F7F), UINT64_C(0x884F3721A99EA9A9),
|
||||
UINT64_C(0x6754824D2AA82A2A), UINT64_C(0x0A6B6DB1BBD6BBBB), UINT64_C(0x879FE246C123C1C1), UINT64_C(0xF1A602A253515353), UINT64_C(0x72A58BAEDC57DCDC), UINT64_C(0x531627580B2C0B0B), UINT64_C(0x0127D39C9D4E9D9D), UINT64_C(0x2BD8C1476CAD6C6C),
|
||||
UINT64_C(0xA462F59531C43131), UINT64_C(0xF3E8B98774CD7474), UINT64_C(0x15F109E3F6FFF6F6), UINT64_C(0x4C8C430A46054646), UINT64_C(0xA5452609AC8AACAC), UINT64_C(0xB50F973C891E8989), UINT64_C(0xB42844A014501414), UINT64_C(0xBADF425BE1A3E1E1),
|
||||
UINT64_C(0xA62C4EB016581616), UINT64_C(0xF774D2CD3AE83A3A), UINT64_C(0x06D2D06F69B96969), UINT64_C(0x41122D4809240909), UINT64_C(0xD7E0ADA770DD7070), UINT64_C(0x6F7154D9B6E2B6B6), UINT64_C(0x1EBDB7CED067D0D0), UINT64_C(0xD6C77E3BED93EDED),
|
||||
UINT64_C(0xE285DB2ECC17CCCC), UINT64_C(0x6884572A42154242), UINT64_C(0x2C2DC2B4985A9898), UINT64_C(0xED550E49A4AAA4A4), UINT64_C(0x7550885D28A02828), UINT64_C(0x86B831DA5C6D5C5C), UINT64_C(0x6BED3F93F8C7F8F8), UINT64_C(0xC211A44486228686),
|
||||
};
|
||||
|
||||
static void whirlpool_round(uint64_t block[static 8], const uint64_t key[static 8]) {
|
||||
uint64_t a = block[0];
|
||||
uint64_t b = block[1];
|
||||
uint64_t c = block[2];
|
||||
uint64_t d = block[3];
|
||||
uint64_t e = block[4];
|
||||
uint64_t f = block[5];
|
||||
uint64_t g = block[6];
|
||||
uint64_t h = block[7];
|
||||
|
||||
uint64_t r;
|
||||
#define ROTR64(x, n) (((0U + (x)) << (64 - (n))) | ((x) >> (n))) // Assumes that x is uint64_t and 0 < n < 64
|
||||
#define DOROW(i, s, t, u, v, w, x, y, z) \
|
||||
r = MAGIC_TABLE[(uint8_t)s]; r = ROTR64(r, 8); \
|
||||
r ^= MAGIC_TABLE[(uint8_t)(t >> 8)]; r = ROTR64(r, 8); \
|
||||
r ^= MAGIC_TABLE[(uint8_t)(u >> 16)]; r = ROTR64(r, 8); \
|
||||
r ^= MAGIC_TABLE[(uint8_t)(v >> 24)]; r = ROTR64(r, 8); \
|
||||
r ^= MAGIC_TABLE[(uint8_t)(w >> 32)]; r = ROTR64(r, 8); \
|
||||
r ^= MAGIC_TABLE[(uint8_t)(x >> 40)]; r = ROTR64(r, 8); \
|
||||
r ^= MAGIC_TABLE[(uint8_t)(y >> 48)]; r = ROTR64(r, 8); \
|
||||
r ^= MAGIC_TABLE[(uint8_t)(z >> 56)]; r = ROTR64(r, 8); \
|
||||
block[i] = r ^ key[i];
|
||||
|
||||
DOROW(0, a, h, g, f, e, d, c, b)
|
||||
DOROW(1, b, a, h, g, f, e, d, c)
|
||||
DOROW(2, c, b, a, h, g, f, e, d)
|
||||
DOROW(3, d, c, b, a, h, g, f, e)
|
||||
DOROW(4, e, d, c, b, a, h, g, f)
|
||||
DOROW(5, f, e, d, c, b, a, h, g)
|
||||
DOROW(6, g, f, e, d, c, b, a, h)
|
||||
DOROW(7, h, g, f, e, d, c, b, a)
|
||||
}
|
||||
|
||||
WASM_EXPORT
|
||||
void Hash_Init() {
|
||||
for (uint32_t i = 0; i < 64; i+=8) {
|
||||
*(uint64_t*)(sctx.hash + i) = 0;
|
||||
}
|
||||
sctx.totalBytes = 0;
|
||||
sctx.rem = 0;
|
||||
}
|
||||
|
||||
inline uint32_t min(uint32_t a, uint32_t b) {
|
||||
if (a < b) return a;
|
||||
return b;
|
||||
}
|
||||
|
||||
WASM_EXPORT
|
||||
void Hash_Update(uint32_t len) {
|
||||
sctx.totalBytes += len;
|
||||
uint32_t read = 0;
|
||||
if (sctx.rem > 0) {
|
||||
uint32_t end = min(64, sctx.rem + len);
|
||||
for (uint8_t z = sctx.rem; z < end; z++) {
|
||||
sctx.buffer[z] = main_buffer[read++];
|
||||
}
|
||||
if (end == 64) {
|
||||
whirlpool_compress(sctx.hash, sctx.buffer);
|
||||
sctx.rem = 0;
|
||||
} else {
|
||||
sctx.rem = end;
|
||||
}
|
||||
}
|
||||
|
||||
while (len - read >= 64) {
|
||||
whirlpool_compress(sctx.hash, &main_buffer[read]);
|
||||
read += 64;
|
||||
}
|
||||
|
||||
if (len - read > 0) {
|
||||
sctx.rem = len - read;
|
||||
for (uint8_t z = 0; z < sctx.rem; z++) {
|
||||
sctx.buffer[z] = main_buffer[read + z];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
WASM_EXPORT
|
||||
void Hash_Final() {
|
||||
const int LENGTH_SIZE = 32;
|
||||
|
||||
uint8_t temp[64] = {0};
|
||||
for (uint8_t i = 0; i < sctx.rem; i++) {
|
||||
temp[i] = sctx.buffer[i];
|
||||
}
|
||||
temp[sctx.rem] = 0x80;
|
||||
sctx.rem++;
|
||||
|
||||
if (BLOCK_LEN - sctx.rem < LENGTH_SIZE) {
|
||||
whirlpool_compress(sctx.hash, temp);
|
||||
for (uint32_t i = 0; i < 32; i+=8) {
|
||||
*(uint64_t*)(temp + i) = 0;
|
||||
}
|
||||
}
|
||||
|
||||
temp[BLOCK_LEN - 1] = (uint8_t)((sctx.totalBytes & 0x1FU) << 3);
|
||||
sctx.totalBytes >>= 5;
|
||||
for (int i = 1; i < LENGTH_SIZE; i++, sctx.totalBytes >>= 8) {
|
||||
temp[BLOCK_LEN - 1 - i] = (uint8_t)(sctx.totalBytes & 0xFFU);
|
||||
}
|
||||
whirlpool_compress(sctx.hash, temp);
|
||||
|
||||
for (uint32_t i = 0; i < 64; i+=8) {
|
||||
*(uint64_t*)(main_buffer + i) = *(uint64_t*)(sctx.hash + i);
|
||||
}
|
||||
}
|
||||
|
||||
WASM_EXPORT
|
||||
const uint32_t STATE_SIZE = sizeof(sctx);
|
||||
|
||||
WASM_EXPORT
|
||||
uint8_t* Hash_GetState() {
|
||||
return (uint8_t*) &sctx;
|
||||
}
|
||||
|
||||
WASM_EXPORT
|
||||
void Hash_Calculate(uint32_t length) {
|
||||
Hash_Init();
|
||||
Hash_Update(length);
|
||||
Hash_Final();
|
||||
}
|
||||
+1003
File diff suppressed because it is too large
Load Diff
+871
@@ -0,0 +1,871 @@
|
||||
/*
|
||||
* xxHash - Extremely Fast Hash algorithm
|
||||
* Header File
|
||||
* Copyright (C) 2012-2020 Yann Collet
|
||||
*
|
||||
* BSD 2-Clause License (https://www.opensource.org/licenses/bsd-license.php)
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are
|
||||
* met:
|
||||
*
|
||||
* * Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* * Redistributions in binary form must reproduce the above
|
||||
* copyright notice, this list of conditions and the following disclaimer
|
||||
* in the documentation and/or other materials provided with the
|
||||
* distribution.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*
|
||||
* You can contact the author at:
|
||||
* - xxHash homepage: https://www.xxhash.com
|
||||
* - xxHash source repository: https://github.com/Cyan4973/xxHash
|
||||
*
|
||||
* Modified for hash-wasm by Dani Biró
|
||||
*/
|
||||
|
||||
#define WITH_BUFFER
|
||||
#include <stddef.h>
|
||||
|
||||
#include "hash-wasm.h"
|
||||
|
||||
typedef uint8_t xxh_u8;
|
||||
typedef uint32_t XXH32_hash_t;
|
||||
typedef XXH32_hash_t xxh_u32;
|
||||
typedef uint64_t XXH64_hash_t;
|
||||
typedef XXH64_hash_t xxh_u64;
|
||||
typedef struct {
|
||||
XXH64_hash_t low64; /*!< `value & 0xFFFFFFFFFFFFFFFF` */
|
||||
XXH64_hash_t high64; /*!< `value >> 64` */
|
||||
} XXH128_hash_t;
|
||||
|
||||
#define XXH_RESTRICT restrict
|
||||
#define XXH_NO_INLINE static
|
||||
#define XXH_likely(x) __builtin_expect(x, 1)
|
||||
#define XXH_unlikely(x) __builtin_expect(x, 0)
|
||||
#define XXH_swap64 __builtin_bswap64
|
||||
#define XXH_swap32 __builtin_bswap32
|
||||
#define XXH_rotl32 __builtin_rotateleft32
|
||||
#define XXH_rotl64 __builtin_rotateleft64
|
||||
#define XXH_FORCE_INLINE inline static
|
||||
|
||||
#define XXH_ALIGN(n) alignas(n)
|
||||
#define XXH_ALIGN_MEMBER(align, type) XXH_ALIGN(align) type
|
||||
#define XXH3_INTERNALBUFFER_SIZE 256
|
||||
#define XXH3_SECRET_DEFAULT_SIZE 192
|
||||
#define XXH3_SECRET_SIZE_MIN 136
|
||||
#define XXH_SECRET_DEFAULT_SIZE 192 /* minimum XXH3_SECRET_SIZE_MIN */
|
||||
#define XXH_STRIPE_LEN 64
|
||||
#define XXH_SECRET_CONSUME_RATE \
|
||||
8 /* nb of secret bytes consumed at each accumulation */
|
||||
#define XXH_ASSERT(c) ((void)0)
|
||||
#define XXH_STATIC_ASSERT(c) \
|
||||
do { \
|
||||
enum { XXH_sa = 1 / (int)(!!(c)) }; \
|
||||
} while (0)
|
||||
#define XXH_ACC_ALIGN 64
|
||||
#define XXH_ACC_NB (XXH_STRIPE_LEN / sizeof(xxh_u64))
|
||||
#define XXH_SECRET_MERGEACCS_START 11
|
||||
#define XXH3_MIDSIZE_MAX 240
|
||||
#define XXH_SECRET_LASTACC_START 7
|
||||
#define XXH3_MIDSIZE_STARTOFFSET 3
|
||||
#define XXH3_MIDSIZE_LASTOFFSET 17
|
||||
#define XXH_SEC_ALIGN 64
|
||||
|
||||
XXH_FORCE_INLINE xxh_u64 XXH_mult32to64(xxh_u64 x, xxh_u64 y) {
|
||||
return (x & 0xFFFFFFFF) * (y & 0xFFFFFFFF);
|
||||
}
|
||||
|
||||
XXH_FORCE_INLINE xxh_u32 XXH_read32(const void* memPtr) {
|
||||
return *(const xxh_u32*)memPtr;
|
||||
}
|
||||
|
||||
XXH_FORCE_INLINE xxh_u32 XXH_readLE32(const void* ptr) {
|
||||
return XXH_read32(ptr);
|
||||
}
|
||||
|
||||
XXH_FORCE_INLINE xxh_u64 XXH_read64(const void* memPtr) {
|
||||
return *(const xxh_u64*)memPtr;
|
||||
}
|
||||
|
||||
XXH_FORCE_INLINE xxh_u64 XXH_readLE64(const void* ptr) {
|
||||
return XXH_read64(ptr);
|
||||
}
|
||||
|
||||
XXH_FORCE_INLINE void XXH_writeLE64(void* dst, xxh_u64 v64) {
|
||||
memcpy64(dst, &v64);
|
||||
}
|
||||
|
||||
XXH_FORCE_INLINE xxh_u64 XXH_xorshift64(xxh_u64 v64, int shift) {
|
||||
XXH_ASSERT(0 <= shift && shift < 64);
|
||||
return v64 ^ (v64 >> shift);
|
||||
}
|
||||
|
||||
/*
|
||||
* This is a stronger avalanche,
|
||||
* inspired by Pelle Evensen's rrmxmx
|
||||
* preferable when input has not been previously mixed
|
||||
*/
|
||||
XXH_FORCE_INLINE XXH64_hash_t XXH3_rrmxmx(xxh_u64 h64, xxh_u64 len) {
|
||||
/* this mix is inspired by Pelle Evensen's rrmxmx */
|
||||
h64 ^= XXH_rotl64(h64, 49) ^ XXH_rotl64(h64, 24);
|
||||
h64 *= 0x9FB21C651E98DF25ULL;
|
||||
h64 ^= (h64 >> 35) + len;
|
||||
h64 *= 0x9FB21C651E98DF25ULL;
|
||||
return XXH_xorshift64(h64, 28);
|
||||
}
|
||||
|
||||
XXH_FORCE_INLINE XXH128_hash_t XXH_mult64to128(xxh_u64 lhs, xxh_u64 rhs) {
|
||||
/* First calculate all of the cross products. */
|
||||
xxh_u64 const lo_lo = XXH_mult32to64(lhs & 0xFFFFFFFF, rhs & 0xFFFFFFFF);
|
||||
xxh_u64 const hi_lo = XXH_mult32to64(lhs >> 32, rhs & 0xFFFFFFFF);
|
||||
xxh_u64 const lo_hi = XXH_mult32to64(lhs & 0xFFFFFFFF, rhs >> 32);
|
||||
xxh_u64 const hi_hi = XXH_mult32to64(lhs >> 32, rhs >> 32);
|
||||
|
||||
/* Now add the products together. These will never overflow. */
|
||||
xxh_u64 const cross = (lo_lo >> 32) + (hi_lo & 0xFFFFFFFF) + lo_hi;
|
||||
xxh_u64 const upper = (hi_lo >> 32) + (cross >> 32) + hi_hi;
|
||||
xxh_u64 const lower = (cross << 32) | (lo_lo & 0xFFFFFFFF);
|
||||
|
||||
XXH128_hash_t r128;
|
||||
r128.low64 = lower;
|
||||
r128.high64 = upper;
|
||||
return r128;
|
||||
}
|
||||
|
||||
#define XXH_PREFETCH(ptr) (void)(ptr) /* disabled */
|
||||
typedef XXH64_hash_t (*XXH3_hashLong64_f)(
|
||||
const void* XXH_RESTRICT, size_t,
|
||||
XXH64_hash_t,
|
||||
const xxh_u8* XXH_RESTRICT, size_t
|
||||
);
|
||||
#define XXH_PREFETCH_DIST 320
|
||||
|
||||
#define XXH_PRIME32_1 0x9E3779B1U /*!< 0b10011110001101110111100110110001 */
|
||||
#define XXH_PRIME32_2 0x85EBCA77U /*!< 0b10000101111010111100101001110111 */
|
||||
#define XXH_PRIME32_3 0xC2B2AE3DU /*!< 0b11000010101100101010111000111101 */
|
||||
#define XXH_PRIME32_4 0x27D4EB2FU /*!< 0b00100111110101001110101100101111 */
|
||||
#define XXH_PRIME32_5 0x165667B1U /*!< 0b00010110010101100110011110110001 */
|
||||
|
||||
#define XXH_PRIME64_1 0x9E3779B185EBCA87ULL
|
||||
#define XXH_PRIME64_2 0xC2B2AE3D27D4EB4FULL
|
||||
#define XXH_PRIME64_3 0x165667B19E3779F9ULL
|
||||
#define XXH_PRIME64_4 0x85EBCA77C2B2AE63ULL
|
||||
#define XXH_PRIME64_5 0x27D4EB2F165667C5ULL
|
||||
|
||||
XXH_ALIGN(64)
|
||||
static const xxh_u8 XXH3_kSecret[XXH_SECRET_DEFAULT_SIZE] = {
|
||||
0xb8, 0xfe, 0x6c, 0x39, 0x23, 0xa4, 0x4b, 0xbe, 0x7c, 0x01, 0x81, 0x2c,
|
||||
0xf7, 0x21, 0xad, 0x1c, 0xde, 0xd4, 0x6d, 0xe9, 0x83, 0x90, 0x97, 0xdb,
|
||||
0x72, 0x40, 0xa4, 0xa4, 0xb7, 0xb3, 0x67, 0x1f, 0xcb, 0x79, 0xe6, 0x4e,
|
||||
0xcc, 0xc0, 0xe5, 0x78, 0x82, 0x5a, 0xd0, 0x7d, 0xcc, 0xff, 0x72, 0x21,
|
||||
0xb8, 0x08, 0x46, 0x74, 0xf7, 0x43, 0x24, 0x8e, 0xe0, 0x35, 0x90, 0xe6,
|
||||
0x81, 0x3a, 0x26, 0x4c, 0x3c, 0x28, 0x52, 0xbb, 0x91, 0xc3, 0x00, 0xcb,
|
||||
0x88, 0xd0, 0x65, 0x8b, 0x1b, 0x53, 0x2e, 0xa3, 0x71, 0x64, 0x48, 0x97,
|
||||
0xa2, 0x0d, 0xf9, 0x4e, 0x38, 0x19, 0xef, 0x46, 0xa9, 0xde, 0xac, 0xd8,
|
||||
0xa8, 0xfa, 0x76, 0x3f, 0xe3, 0x9c, 0x34, 0x3f, 0xf9, 0xdc, 0xbb, 0xc7,
|
||||
0xc7, 0x0b, 0x4f, 0x1d, 0x8a, 0x51, 0xe0, 0x4b, 0xcd, 0xb4, 0x59, 0x31,
|
||||
0xc8, 0x9f, 0x7e, 0xc9, 0xd9, 0x78, 0x73, 0x64, 0xea, 0xc5, 0xac, 0x83,
|
||||
0x34, 0xd3, 0xeb, 0xc3, 0xc5, 0x81, 0xa0, 0xff, 0xfa, 0x13, 0x63, 0xeb,
|
||||
0x17, 0x0d, 0xdd, 0x51, 0xb7, 0xf0, 0xda, 0x49, 0xd3, 0x16, 0x55, 0x26,
|
||||
0x29, 0xd4, 0x68, 0x9e, 0x2b, 0x16, 0xbe, 0x58, 0x7d, 0x47, 0xa1, 0xfc,
|
||||
0x8f, 0xf8, 0xb8, 0xd1, 0x7a, 0xd0, 0x31, 0xce, 0x45, 0xcb, 0x3a, 0x8f,
|
||||
0x95, 0x16, 0x04, 0x28, 0xaf, 0xd7, 0xfb, 0xca, 0xbb, 0x4b, 0x40, 0x7e,
|
||||
};
|
||||
|
||||
#define XXH3_INIT_ACC \
|
||||
{ \
|
||||
XXH_PRIME32_3, XXH_PRIME64_1, XXH_PRIME64_2, XXH_PRIME64_3, XXH_PRIME64_4, \
|
||||
XXH_PRIME32_2, XXH_PRIME64_5, XXH_PRIME32_1 \
|
||||
}
|
||||
|
||||
struct XXH3_state_s {
|
||||
XXH_ALIGN_MEMBER(64, XXH64_hash_t acc[8]);
|
||||
/*!< The 8 accumulators. Similar to `vN` in @ref XXH32_state_s::v1 and @ref
|
||||
* XXH64_state_s */
|
||||
XXH_ALIGN_MEMBER(64, unsigned char customSecret[XXH3_SECRET_DEFAULT_SIZE]);
|
||||
/*!< Used to store a custom secret generated from a seed. */
|
||||
XXH_ALIGN_MEMBER(64, unsigned char buffer[XXH3_INTERNALBUFFER_SIZE]);
|
||||
/*!< The internal buffer. @see XXH32_state_s::mem32 */
|
||||
XXH32_hash_t bufferedSize;
|
||||
/*!< The amount of memory in @ref buffer, @see XXH32_state_s::memsize */
|
||||
XXH32_hash_t reserved32;
|
||||
/*!< Reserved field. Needed for padding on 64-bit. */
|
||||
size_t nbStripesSoFar;
|
||||
/*!< Number or stripes processed. */
|
||||
XXH64_hash_t totalLen;
|
||||
/*!< Total length hashed. 64-bit even on 32-bit targets. */
|
||||
size_t nbStripesPerBlock;
|
||||
/*!< Number of stripes per block. */
|
||||
size_t secretLimit;
|
||||
/*!< Size of @ref customSecret or @ref extSecret */
|
||||
XXH64_hash_t seed;
|
||||
/*!< Seed for _withSeed variants. Must be zero otherwise, @see
|
||||
* XXH3_INITSTATE() */
|
||||
XXH64_hash_t reserved64;
|
||||
/*!< Reserved field. */
|
||||
const unsigned char* extSecret;
|
||||
/*!< Reference to an external secret for the _withSecret variants, NULL
|
||||
* for other variants. */
|
||||
/* note: there may be some padding at the end due to alignment on 64 bytes */
|
||||
}; /* typedef'd to XXH3_state_t */
|
||||
|
||||
typedef struct XXH3_state_s XXH3_state_t;
|
||||
|
||||
static xxh_u64 XXH64_avalanche(xxh_u64 h64) {
|
||||
h64 ^= h64 >> 33;
|
||||
h64 *= XXH_PRIME64_2;
|
||||
h64 ^= h64 >> 29;
|
||||
h64 *= XXH_PRIME64_3;
|
||||
h64 ^= h64 >> 32;
|
||||
return h64;
|
||||
}
|
||||
|
||||
static xxh_u64 XXH3_mul128_fold64(xxh_u64 lhs, xxh_u64 rhs) {
|
||||
XXH128_hash_t product = XXH_mult64to128(lhs, rhs);
|
||||
return product.low64 ^ product.high64;
|
||||
}
|
||||
|
||||
inline xxh_u64 XXH3_mix16B(
|
||||
const xxh_u8* XXH_RESTRICT input,
|
||||
const xxh_u8* XXH_RESTRICT secret,
|
||||
xxh_u64 seed64
|
||||
) {
|
||||
xxh_u64 const input_lo = XXH_readLE64(input);
|
||||
xxh_u64 const input_hi = XXH_readLE64(input + 8);
|
||||
return XXH3_mul128_fold64(input_lo ^ (XXH_readLE64(secret) + seed64),
|
||||
input_hi ^ (XXH_readLE64(secret + 8) - seed64));
|
||||
}
|
||||
|
||||
static void XXH3_reset_internal(
|
||||
XXH3_state_t* statePtr, XXH64_hash_t seed,
|
||||
const void* secret, size_t secretSize
|
||||
) {
|
||||
size_t const initStart = offsetof(XXH3_state_t, bufferedSize);
|
||||
size_t const initLength = offsetof(XXH3_state_t, nbStripesPerBlock) - initStart;
|
||||
XXH_ASSERT(offsetof(XXH3_state_t, nbStripesPerBlock) > initStart);
|
||||
XXH_ASSERT(statePtr != NULL);
|
||||
/* set members from bufferedSize to nbStripesPerBlock (excluded) to 0 */
|
||||
memset((char*)statePtr + initStart, 0, initLength);
|
||||
statePtr->acc[0] = XXH_PRIME32_3;
|
||||
statePtr->acc[1] = XXH_PRIME64_1;
|
||||
statePtr->acc[2] = XXH_PRIME64_2;
|
||||
statePtr->acc[3] = XXH_PRIME64_3;
|
||||
statePtr->acc[4] = XXH_PRIME64_4;
|
||||
statePtr->acc[5] = XXH_PRIME32_2;
|
||||
statePtr->acc[6] = XXH_PRIME64_5;
|
||||
statePtr->acc[7] = XXH_PRIME32_1;
|
||||
statePtr->seed = seed;
|
||||
statePtr->extSecret = (const unsigned char*)secret;
|
||||
XXH_ASSERT(secretSize >= XXH3_SECRET_SIZE_MIN);
|
||||
statePtr->secretLimit = secretSize - XXH_STRIPE_LEN;
|
||||
statePtr->nbStripesPerBlock = statePtr->secretLimit / XXH_SECRET_CONSUME_RATE;
|
||||
}
|
||||
|
||||
static void XXH3_64bits_reset(XXH3_state_t* statePtr) {
|
||||
XXH3_reset_internal(statePtr, 0, XXH3_kSecret, XXH_SECRET_DEFAULT_SIZE);
|
||||
}
|
||||
|
||||
static void XXH3_initCustomSecret_scalar(
|
||||
void* XXH_RESTRICT customSecret, xxh_u64 seed64
|
||||
) {
|
||||
const xxh_u8* kSecretPtr = XXH3_kSecret;
|
||||
XXH_STATIC_ASSERT((XXH_SECRET_DEFAULT_SIZE & 15) == 0);
|
||||
|
||||
XXH_ASSERT(kSecretPtr == XXH3_kSecret);
|
||||
|
||||
int const nbRounds = XXH_SECRET_DEFAULT_SIZE / 16;
|
||||
for (int i = 0; i < nbRounds; i++) {
|
||||
/*
|
||||
* The asm hack causes Clang to assume that kSecretPtr aliases with
|
||||
* customSecret, and on aarch64, this prevented LDP from merging two
|
||||
* loads together for free. Putting the loads together before the stores
|
||||
* properly generates LDP.
|
||||
*/
|
||||
xxh_u64 lo = XXH_readLE64(kSecretPtr + 16 * i) + seed64;
|
||||
xxh_u64 hi = XXH_readLE64(kSecretPtr + 16 * i + 8) - seed64;
|
||||
XXH_writeLE64((xxh_u8*)customSecret + 16 * i, lo);
|
||||
XXH_writeLE64((xxh_u8*)customSecret + 16 * i + 8, hi);
|
||||
}
|
||||
}
|
||||
|
||||
static void XXH3_64bits_reset_withSeed(
|
||||
XXH3_state_t* statePtr, XXH64_hash_t seed
|
||||
) {
|
||||
if (seed == 0) return XXH3_64bits_reset(statePtr);
|
||||
if (seed != statePtr->seed)
|
||||
XXH3_initCustomSecret_scalar(statePtr->customSecret, seed);
|
||||
XXH3_reset_internal(statePtr, seed, NULL, XXH_SECRET_DEFAULT_SIZE);
|
||||
}
|
||||
|
||||
void XXH3_accumulate_512_scalar(
|
||||
void* XXH_RESTRICT acc,
|
||||
const void* XXH_RESTRICT input,
|
||||
const void* XXH_RESTRICT secret
|
||||
) {
|
||||
XXH_ALIGN(XXH_ACC_ALIGN)
|
||||
xxh_u64* const xacc = (xxh_u64*)acc; /* presumed aligned */
|
||||
const xxh_u8* const xinput = (const xxh_u8*)input; /* no alignment restriction */
|
||||
const xxh_u8* const xsecret = (const xxh_u8*)secret; /* no alignment restriction */
|
||||
XXH_ASSERT(((size_t)acc & (XXH_ACC_ALIGN - 1)) == 0);
|
||||
for (size_t i = 0; i < XXH_ACC_NB; i++) {
|
||||
xxh_u64 const data_val = XXH_readLE64(xinput + 8 * i);
|
||||
xxh_u64 const data_key = data_val ^ XXH_readLE64(xsecret + i * 8);
|
||||
xacc[i ^ 1] += data_val; /* swap adjacent lanes */
|
||||
xacc[i] += XXH_mult32to64(data_key & 0xFFFFFFFF, data_key >> 32);
|
||||
}
|
||||
}
|
||||
|
||||
void XXH3_scrambleAcc_scalar(
|
||||
void* XXH_RESTRICT acc, const void* XXH_RESTRICT secret
|
||||
) {
|
||||
XXH_ALIGN(XXH_ACC_ALIGN)
|
||||
xxh_u64* const xacc = (xxh_u64*)acc; /* presumed aligned */
|
||||
const xxh_u8* const xsecret = (const xxh_u8*)secret; /* no alignment restriction */
|
||||
XXH_ASSERT((((size_t)acc) & (XXH_ACC_ALIGN - 1)) == 0);
|
||||
for (size_t i = 0; i < XXH_ACC_NB; i++) {
|
||||
xxh_u64 const key64 = XXH_readLE64(xsecret + 8 * i);
|
||||
xxh_u64 acc64 = xacc[i];
|
||||
acc64 = XXH_xorshift64(acc64, 47);
|
||||
acc64 ^= key64;
|
||||
acc64 *= XXH_PRIME32_1;
|
||||
xacc[i] = acc64;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* XXH3_accumulate()
|
||||
* Loops over XXH3_accumulate_512().
|
||||
* Assumption: nbStripes will not overflow the secret size
|
||||
*/
|
||||
void XXH3_accumulate(
|
||||
xxh_u64* XXH_RESTRICT acc,
|
||||
const xxh_u8* XXH_RESTRICT input,
|
||||
const xxh_u8* XXH_RESTRICT secret, size_t nbStripes
|
||||
) {
|
||||
for (size_t n = 0; n < nbStripes; n++) {
|
||||
const xxh_u8* const in = input + n * XXH_STRIPE_LEN;
|
||||
XXH_PREFETCH(in + XXH_PREFETCH_DIST);
|
||||
XXH3_accumulate_512_scalar(acc, in, secret + n * XXH_SECRET_CONSUME_RATE);
|
||||
}
|
||||
}
|
||||
|
||||
/* Note : when XXH3_consumeStripes() is invoked,
|
||||
* there must be a guarantee that at least one more byte must be consumed from
|
||||
* input
|
||||
* so that the function can blindly consume all stripes using the "normal"
|
||||
* secret segment */
|
||||
void XXH3_consumeStripes(
|
||||
xxh_u64* XXH_RESTRICT acc,
|
||||
size_t* XXH_RESTRICT nbStripesSoFarPtr,
|
||||
size_t nbStripesPerBlock,
|
||||
const xxh_u8* XXH_RESTRICT input,
|
||||
size_t nbStripes,
|
||||
const xxh_u8* XXH_RESTRICT secret,
|
||||
size_t secretLimit
|
||||
) {
|
||||
XXH_ASSERT(nbStripes <= nbStripesPerBlock); /* can handle max 1 scramble per invocation */
|
||||
XXH_ASSERT(*nbStripesSoFarPtr < nbStripesPerBlock);
|
||||
if (nbStripesPerBlock - *nbStripesSoFarPtr <= nbStripes) {
|
||||
/* need a scrambling operation */
|
||||
size_t const nbStripesToEndofBlock = nbStripesPerBlock - *nbStripesSoFarPtr;
|
||||
size_t const nbStripesAfterBlock = nbStripes - nbStripesToEndofBlock;
|
||||
XXH3_accumulate(
|
||||
acc,
|
||||
input,
|
||||
secret + nbStripesSoFarPtr[0] * XXH_SECRET_CONSUME_RATE,
|
||||
nbStripesToEndofBlock
|
||||
);
|
||||
XXH3_scrambleAcc_scalar(acc, secret + secretLimit);
|
||||
XXH3_accumulate(
|
||||
acc,
|
||||
input + nbStripesToEndofBlock * XXH_STRIPE_LEN,
|
||||
secret,
|
||||
nbStripesAfterBlock
|
||||
);
|
||||
*nbStripesSoFarPtr = nbStripesAfterBlock;
|
||||
} else {
|
||||
XXH3_accumulate(
|
||||
acc,
|
||||
input,
|
||||
secret + nbStripesSoFarPtr[0] * XXH_SECRET_CONSUME_RATE,
|
||||
nbStripes
|
||||
);
|
||||
*nbStripesSoFarPtr += nbStripes;
|
||||
}
|
||||
}
|
||||
|
||||
void XXH3_update(XXH3_state_t* state, const xxh_u8* input, size_t len) {
|
||||
{
|
||||
const xxh_u8* const bEnd = input + len;
|
||||
const unsigned char* const secret =
|
||||
(state->extSecret == NULL) ? state->customSecret : state->extSecret;
|
||||
|
||||
state->totalLen += len;
|
||||
XXH_ASSERT(state->bufferedSize <= XXH3_INTERNALBUFFER_SIZE);
|
||||
|
||||
if (state->bufferedSize + len <= XXH3_INTERNALBUFFER_SIZE) { /* fill in tmp buffer */
|
||||
memcpy2(state->buffer + state->bufferedSize, input, len);
|
||||
state->bufferedSize += (XXH32_hash_t)len;
|
||||
return;
|
||||
}
|
||||
/* total input is now > XXH3_INTERNALBUFFER_SIZE */
|
||||
|
||||
#define XXH3_INTERNALBUFFER_STRIPES (XXH3_INTERNALBUFFER_SIZE / XXH_STRIPE_LEN)
|
||||
XXH_STATIC_ASSERT(XXH3_INTERNALBUFFER_SIZE % XXH_STRIPE_LEN ==
|
||||
0); /* clean multiple */
|
||||
|
||||
/*
|
||||
* Internal buffer is partially filled (always, except at beginning)
|
||||
* Complete it, then consume it.
|
||||
*/
|
||||
if (state->bufferedSize) {
|
||||
size_t const loadSize = XXH3_INTERNALBUFFER_SIZE - state->bufferedSize;
|
||||
memcpy2(state->buffer + state->bufferedSize, input, loadSize);
|
||||
input += loadSize;
|
||||
XXH3_consumeStripes(state->acc, &state->nbStripesSoFar,
|
||||
state->nbStripesPerBlock, state->buffer,
|
||||
XXH3_INTERNALBUFFER_STRIPES, secret,
|
||||
state->secretLimit);
|
||||
state->bufferedSize = 0;
|
||||
}
|
||||
XXH_ASSERT(input < bEnd);
|
||||
|
||||
/* Consume input by a multiple of internal buffer size */
|
||||
if (input + XXH3_INTERNALBUFFER_SIZE < bEnd) {
|
||||
const xxh_u8* const limit = bEnd - XXH3_INTERNALBUFFER_SIZE;
|
||||
do {
|
||||
XXH3_consumeStripes(
|
||||
state->acc, &state->nbStripesSoFar, state->nbStripesPerBlock, input,
|
||||
XXH3_INTERNALBUFFER_STRIPES, secret, state->secretLimit);
|
||||
input += XXH3_INTERNALBUFFER_SIZE;
|
||||
} while (input < limit);
|
||||
/* for last partial stripe */
|
||||
memcpy64(state->buffer + sizeof(state->buffer) - XXH_STRIPE_LEN,
|
||||
input - XXH_STRIPE_LEN);
|
||||
}
|
||||
XXH_ASSERT(input < bEnd);
|
||||
|
||||
/* Some remaining input (always) : buffer it */
|
||||
memcpy2(state->buffer, input, (size_t)(bEnd - input));
|
||||
state->bufferedSize = (XXH32_hash_t)(bEnd - input);
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
void XXH3_64bits_update(XXH3_state_t* state, const void* input, size_t len) {
|
||||
XXH3_update(state, (const xxh_u8*)input, len);
|
||||
}
|
||||
|
||||
xxh_u64 XXH3_mix2Accs(
|
||||
const xxh_u64* XXH_RESTRICT acc,
|
||||
const xxh_u8* XXH_RESTRICT secret
|
||||
) {
|
||||
return XXH3_mul128_fold64(
|
||||
acc[0] ^ XXH_readLE64(secret),
|
||||
acc[1] ^ XXH_readLE64(secret + 8)
|
||||
);
|
||||
}
|
||||
|
||||
/*
|
||||
* This is a fast avalanche stage,
|
||||
* suitable when input bits are already partially mixed
|
||||
*/
|
||||
XXH64_hash_t XXH3_avalanche(xxh_u64 h64) {
|
||||
h64 = XXH_xorshift64(h64, 37);
|
||||
h64 *= 0x165667919E3779F9ULL;
|
||||
h64 = XXH_xorshift64(h64, 32);
|
||||
return h64;
|
||||
}
|
||||
|
||||
XXH64_hash_t XXH3_mergeAccs(
|
||||
const xxh_u64* XXH_RESTRICT acc,
|
||||
const xxh_u8* XXH_RESTRICT secret,
|
||||
xxh_u64 start
|
||||
) {
|
||||
xxh_u64 result64 = start;
|
||||
|
||||
for (size_t i = 0; i < 4; i++) {
|
||||
result64 += XXH3_mix2Accs(acc + 2 * i, secret + 16 * i);
|
||||
}
|
||||
|
||||
return XXH3_avalanche(result64);
|
||||
}
|
||||
|
||||
void XXH3_digest_long(
|
||||
XXH64_hash_t* acc,
|
||||
const XXH3_state_t* state,
|
||||
const unsigned char* secret
|
||||
) {
|
||||
/*
|
||||
* Digest on a local copy. This way, the state remains unaltered, and it can
|
||||
* continue ingesting more input afterwards.
|
||||
*/
|
||||
memcpy2(acc, state->acc, sizeof(state->acc));
|
||||
if (state->bufferedSize >= XXH_STRIPE_LEN) {
|
||||
size_t const nbStripes = (state->bufferedSize - 1) / XXH_STRIPE_LEN;
|
||||
size_t nbStripesSoFar = state->nbStripesSoFar;
|
||||
XXH3_consumeStripes(acc, &nbStripesSoFar, state->nbStripesPerBlock,
|
||||
state->buffer, nbStripes, secret, state->secretLimit);
|
||||
/* last stripe */
|
||||
XXH3_accumulate_512_scalar(
|
||||
acc, state->buffer + state->bufferedSize - XXH_STRIPE_LEN,
|
||||
secret + state->secretLimit - XXH_SECRET_LASTACC_START);
|
||||
} else { /* bufferedSize < XXH_STRIPE_LEN */
|
||||
xxh_u8 lastStripe[XXH_STRIPE_LEN];
|
||||
size_t const catchupSize = XXH_STRIPE_LEN - state->bufferedSize;
|
||||
XXH_ASSERT(state->bufferedSize > 0); /* there is always some input buffered */
|
||||
memcpy2(lastStripe, state->buffer + sizeof(state->buffer) - catchupSize, catchupSize);
|
||||
memcpy2(lastStripe + catchupSize, state->buffer, state->bufferedSize);
|
||||
XXH3_accumulate_512_scalar(
|
||||
acc,
|
||||
lastStripe,
|
||||
secret + state->secretLimit - XXH_SECRET_LASTACC_START
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
XXH64_hash_t XXH3_len_9to16_64b(
|
||||
const xxh_u8* input,
|
||||
size_t len,
|
||||
const xxh_u8* secret,
|
||||
XXH64_hash_t seed
|
||||
) {
|
||||
XXH_ASSERT(input != NULL);
|
||||
XXH_ASSERT(secret != NULL);
|
||||
XXH_ASSERT(8 <= len && len <= 16);
|
||||
xxh_u64 const bitflip1 =
|
||||
(XXH_readLE64(secret + 24) ^ XXH_readLE64(secret + 32)) + seed;
|
||||
xxh_u64 const bitflip2 =
|
||||
(XXH_readLE64(secret + 40) ^ XXH_readLE64(secret + 48)) - seed;
|
||||
xxh_u64 const input_lo = XXH_readLE64(input) ^ bitflip1;
|
||||
xxh_u64 const input_hi = XXH_readLE64(input + len - 8) ^ bitflip2;
|
||||
xxh_u64 const acc =
|
||||
len + XXH_swap64(input_lo) + input_hi + XXH3_mul128_fold64(input_lo, input_hi);
|
||||
return XXH3_avalanche(acc);
|
||||
}
|
||||
|
||||
XXH64_hash_t XXH3_len_4to8_64b(
|
||||
const xxh_u8* input,
|
||||
size_t len,
|
||||
const xxh_u8* secret,
|
||||
XXH64_hash_t seed
|
||||
) {
|
||||
XXH_ASSERT(input != NULL);
|
||||
XXH_ASSERT(secret != NULL);
|
||||
XXH_ASSERT(4 <= len && len <= 8);
|
||||
seed ^= (xxh_u64)XXH_swap32((xxh_u32)seed) << 32;
|
||||
xxh_u32 const input1 = XXH_readLE32(input);
|
||||
xxh_u32 const input2 = XXH_readLE32(input + len - 4);
|
||||
xxh_u64 const bitflip =
|
||||
(XXH_readLE64(secret + 8) ^ XXH_readLE64(secret + 16)) - seed;
|
||||
xxh_u64 const input64 = input2 + (((xxh_u64)input1) << 32);
|
||||
xxh_u64 const keyed = input64 ^ bitflip;
|
||||
return XXH3_rrmxmx(keyed, len);
|
||||
}
|
||||
|
||||
XXH64_hash_t XXH3_len_1to3_64b(
|
||||
const xxh_u8* input,
|
||||
size_t len,
|
||||
const xxh_u8* secret,
|
||||
XXH64_hash_t seed
|
||||
) {
|
||||
XXH_ASSERT(input != NULL);
|
||||
XXH_ASSERT(1 <= len && len <= 3);
|
||||
XXH_ASSERT(secret != NULL);
|
||||
/*
|
||||
* len = 1: combined = { input[0], 0x01, input[0], input[0] }
|
||||
* len = 2: combined = { input[1], 0x02, input[0], input[1] }
|
||||
* len = 3: combined = { input[2], 0x03, input[0], input[1] }
|
||||
*/
|
||||
xxh_u8 const c1 = input[0];
|
||||
xxh_u8 const c2 = input[len >> 1];
|
||||
xxh_u8 const c3 = input[len - 1];
|
||||
xxh_u32 const combined = ((xxh_u32)c1 << 16) | ((xxh_u32)c2 << 24) |
|
||||
((xxh_u32)c3 << 0) | ((xxh_u32)len << 8);
|
||||
xxh_u64 const bitflip =
|
||||
(XXH_readLE32(secret) ^ XXH_readLE32(secret + 4)) + seed;
|
||||
xxh_u64 const keyed = (xxh_u64)combined ^ bitflip;
|
||||
return XXH64_avalanche(keyed);
|
||||
}
|
||||
|
||||
XXH64_hash_t XXH3_len_0to16_64b(
|
||||
const xxh_u8* input,
|
||||
size_t len,
|
||||
const xxh_u8* secret,
|
||||
XXH64_hash_t seed
|
||||
) {
|
||||
XXH_ASSERT(len <= 16);
|
||||
if (XXH_likely(len > 8))
|
||||
return XXH3_len_9to16_64b(input, len, secret, seed);
|
||||
if (XXH_likely(len >= 4))
|
||||
return XXH3_len_4to8_64b(input, len, secret, seed);
|
||||
if (len) return XXH3_len_1to3_64b(input, len, secret, seed);
|
||||
return XXH64_avalanche(
|
||||
seed ^ (XXH_readLE64(secret + 56) ^ XXH_readLE64(secret + 64)));
|
||||
}
|
||||
|
||||
/* For mid range keys, XXH3 uses a Mum-hash variant. */
|
||||
XXH64_hash_t XXH3_len_17to128_64b(
|
||||
const xxh_u8* XXH_RESTRICT input,
|
||||
size_t len,
|
||||
const xxh_u8* XXH_RESTRICT secret,
|
||||
size_t secretSize,
|
||||
XXH64_hash_t seed
|
||||
) {
|
||||
XXH_ASSERT(secretSize >= XXH3_SECRET_SIZE_MIN);
|
||||
(void)secretSize;
|
||||
XXH_ASSERT(16 < len && len <= 128);
|
||||
|
||||
xxh_u64 acc = len * XXH_PRIME64_1;
|
||||
if (len > 32) {
|
||||
if (len > 64) {
|
||||
if (len > 96) {
|
||||
acc += XXH3_mix16B(input + 48, secret + 96, seed);
|
||||
acc += XXH3_mix16B(input + len - 64, secret + 112, seed);
|
||||
}
|
||||
acc += XXH3_mix16B(input + 32, secret + 64, seed);
|
||||
acc += XXH3_mix16B(input + len - 48, secret + 80, seed);
|
||||
}
|
||||
acc += XXH3_mix16B(input + 16, secret + 32, seed);
|
||||
acc += XXH3_mix16B(input + len - 32, secret + 48, seed);
|
||||
}
|
||||
acc += XXH3_mix16B(input + 0, secret + 0, seed);
|
||||
acc += XXH3_mix16B(input + len - 16, secret + 16, seed);
|
||||
|
||||
return XXH3_avalanche(acc);
|
||||
}
|
||||
|
||||
XXH64_hash_t XXH3_len_129to240_64b(
|
||||
const xxh_u8* XXH_RESTRICT input, size_t len,
|
||||
const xxh_u8* XXH_RESTRICT secret,
|
||||
size_t secretSize, XXH64_hash_t seed
|
||||
) {
|
||||
XXH_ASSERT(secretSize >= XXH3_SECRET_SIZE_MIN);
|
||||
(void)secretSize;
|
||||
XXH_ASSERT(128 < len && len <= XXH3_MIDSIZE_MAX);
|
||||
|
||||
{
|
||||
xxh_u64 acc = len * XXH_PRIME64_1;
|
||||
int const nbRounds = (int)len / 16;
|
||||
int i;
|
||||
for (i = 0; i < 8; i++) {
|
||||
acc += XXH3_mix16B(input + (16 * i), secret + (16 * i), seed);
|
||||
}
|
||||
acc = XXH3_avalanche(acc);
|
||||
XXH_ASSERT(nbRounds >= 8);
|
||||
for (i = 8; i < nbRounds; i++) {
|
||||
acc +=
|
||||
XXH3_mix16B(input + (16 * i),
|
||||
secret + (16 * (i - 8)) + XXH3_MIDSIZE_STARTOFFSET, seed);
|
||||
}
|
||||
/* last bytes */
|
||||
acc += XXH3_mix16B(input + len - 16,
|
||||
secret + XXH3_SECRET_SIZE_MIN - XXH3_MIDSIZE_LASTOFFSET,
|
||||
seed);
|
||||
return XXH3_avalanche(acc);
|
||||
}
|
||||
}
|
||||
|
||||
void XXH3_hashLong_internal_loop(
|
||||
xxh_u64* XXH_RESTRICT acc,
|
||||
const xxh_u8* XXH_RESTRICT input, size_t len,
|
||||
const xxh_u8* XXH_RESTRICT secret,
|
||||
size_t secretSize
|
||||
) {
|
||||
size_t const nbStripesPerBlock =
|
||||
(secretSize - XXH_STRIPE_LEN) / XXH_SECRET_CONSUME_RATE;
|
||||
size_t const block_len = XXH_STRIPE_LEN * nbStripesPerBlock;
|
||||
size_t const nb_blocks = (len - 1) / block_len;
|
||||
|
||||
size_t n;
|
||||
|
||||
XXH_ASSERT(secretSize >= XXH3_SECRET_SIZE_MIN);
|
||||
|
||||
for (n = 0; n < nb_blocks; n++) {
|
||||
XXH3_accumulate(acc, input + n * block_len, secret, nbStripesPerBlock);
|
||||
XXH3_scrambleAcc_scalar(acc, secret + secretSize - XXH_STRIPE_LEN);
|
||||
}
|
||||
|
||||
/* last partial block */
|
||||
XXH_ASSERT(len > XXH_STRIPE_LEN);
|
||||
{
|
||||
size_t const nbStripes =
|
||||
((len - 1) - (block_len * nb_blocks)) / XXH_STRIPE_LEN;
|
||||
XXH_ASSERT(nbStripes <= (secretSize / XXH_SECRET_CONSUME_RATE));
|
||||
XXH3_accumulate(acc, input + nb_blocks * block_len, secret, nbStripes);
|
||||
|
||||
/* last stripe */
|
||||
{
|
||||
const xxh_u8* const p = input + len - XXH_STRIPE_LEN;
|
||||
#define XXH_SECRET_LASTACC_START \
|
||||
7 /* not aligned on 8, last secret is different from acc & scrambler */
|
||||
XXH3_accumulate_512_scalar(
|
||||
acc, p,
|
||||
secret + secretSize - XXH_STRIPE_LEN - XXH_SECRET_LASTACC_START);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
XXH64_hash_t XXH3_hashLong_64b_internal(
|
||||
const void* XXH_RESTRICT input,
|
||||
size_t len,
|
||||
const void* XXH_RESTRICT secret,
|
||||
size_t secretSize
|
||||
) {
|
||||
XXH_ALIGN(XXH_ACC_ALIGN) xxh_u64 acc[XXH_ACC_NB] = XXH3_INIT_ACC;
|
||||
|
||||
XXH3_hashLong_internal_loop(acc, (const xxh_u8*)input, len,
|
||||
(const xxh_u8*)secret, secretSize);
|
||||
|
||||
/* converge into final hash */
|
||||
XXH_STATIC_ASSERT(sizeof(acc) == 64);
|
||||
/* do not align on 8, so that the secret is different from the accumulator */
|
||||
XXH_ASSERT(secretSize >= sizeof(acc) + XXH_SECRET_MERGEACCS_START);
|
||||
return XXH3_mergeAccs(acc, (const xxh_u8*)secret + XXH_SECRET_MERGEACCS_START, (xxh_u64)len * XXH_PRIME64_1);
|
||||
}
|
||||
|
||||
XXH64_hash_t XXH3_hashLong_64b_withSeed_internal(
|
||||
const void* input, size_t len, XXH64_hash_t seed
|
||||
) {
|
||||
if (seed == 0)
|
||||
return XXH3_hashLong_64b_internal(
|
||||
input, len, XXH3_kSecret, sizeof(XXH3_kSecret)
|
||||
);
|
||||
{
|
||||
XXH_ALIGN(XXH_SEC_ALIGN) xxh_u8 secret[XXH_SECRET_DEFAULT_SIZE];
|
||||
XXH3_initCustomSecret_scalar(secret, seed);
|
||||
return XXH3_hashLong_64b_internal(input, len, secret, sizeof(secret));
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* It's important for performance that XXH3_hashLong is not inlined.
|
||||
*/
|
||||
XXH64_hash_t XXH3_hashLong_64b_withSeed(
|
||||
const void* input, size_t len,
|
||||
XXH64_hash_t seed, const xxh_u8* secret,
|
||||
size_t secretLen
|
||||
) {
|
||||
return XXH3_hashLong_64b_withSeed_internal(input, len, seed);
|
||||
}
|
||||
|
||||
XXH64_hash_t XXH3_hashLong_64b_withSecret(
|
||||
const void* XXH_RESTRICT input,
|
||||
size_t len, XXH64_hash_t seed64,
|
||||
const xxh_u8* XXH_RESTRICT secret,
|
||||
size_t secretLen
|
||||
) {
|
||||
return XXH3_hashLong_64b_internal(input, len, secret, secretLen);
|
||||
}
|
||||
|
||||
XXH64_hash_t XXH3_64bits_internal(
|
||||
const void* XXH_RESTRICT input, size_t len,
|
||||
XXH64_hash_t seed64,
|
||||
const void* XXH_RESTRICT secret,
|
||||
size_t secretLen,
|
||||
XXH3_hashLong64_f f_hashLong
|
||||
) {
|
||||
XXH_ASSERT(secretLen >= XXH3_SECRET_SIZE_MIN);
|
||||
/*
|
||||
* If an action is to be taken if `secretLen` condition is not respected,
|
||||
* it should be done here.
|
||||
* For now, it's a contract pre-condition.
|
||||
* Adding a check and a branch here would cost performance at every hash.
|
||||
* Also, note that function signature doesn't offer room to return an error.
|
||||
*/
|
||||
if (len <= 16)
|
||||
return XXH3_len_0to16_64b(
|
||||
(const xxh_u8*)input, len, (const xxh_u8*)secret, seed64
|
||||
);
|
||||
if (len <= 128)
|
||||
return XXH3_len_17to128_64b(
|
||||
(const xxh_u8*)input, len, (const xxh_u8*)secret, secretLen, seed64
|
||||
);
|
||||
if (len <= XXH3_MIDSIZE_MAX)
|
||||
return XXH3_len_129to240_64b(
|
||||
(const xxh_u8*)input, len, (const xxh_u8*)secret, secretLen, seed64
|
||||
);
|
||||
return f_hashLong(input, len, seed64, (const xxh_u8*)secret, secretLen);
|
||||
}
|
||||
|
||||
XXH64_hash_t XXH3_64bits_withSeed(
|
||||
const void* input, size_t len, XXH64_hash_t seed
|
||||
) {
|
||||
return XXH3_64bits_internal(input, len, seed, XXH3_kSecret, sizeof(XXH3_kSecret), XXH3_hashLong_64b_withSeed);
|
||||
}
|
||||
|
||||
XXH64_hash_t XXH3_64bits_withSecret(
|
||||
const void* input, size_t len, const void* secret, size_t secretSize
|
||||
) {
|
||||
return XXH3_64bits_internal(input, len, 0, secret, secretSize, XXH3_hashLong_64b_withSecret);
|
||||
}
|
||||
|
||||
XXH64_hash_t XXH3_64bits_digest(const XXH3_state_t* state) {
|
||||
const unsigned char* const secret =
|
||||
(state->extSecret == NULL) ? state->customSecret : state->extSecret;
|
||||
if (state->totalLen > XXH3_MIDSIZE_MAX) {
|
||||
XXH_ALIGN(XXH_ACC_ALIGN) XXH64_hash_t acc[XXH_ACC_NB];
|
||||
XXH3_digest_long(acc, state, secret);
|
||||
return XXH3_mergeAccs(
|
||||
acc, secret + XXH_SECRET_MERGEACCS_START,
|
||||
(xxh_u64)state->totalLen * XXH_PRIME64_1
|
||||
);
|
||||
}
|
||||
/* totalLen <= XXH3_MIDSIZE_MAX: digesting a short input */
|
||||
if (state->seed)
|
||||
return XXH3_64bits_withSeed(
|
||||
state->buffer, (size_t)state->totalLen, state->seed
|
||||
);
|
||||
return XXH3_64bits_withSecret(
|
||||
state->buffer, (size_t)(state->totalLen),
|
||||
secret, state->secretLimit + XXH_STRIPE_LEN
|
||||
);
|
||||
}
|
||||
|
||||
static struct XXH3_state_s sctx;
|
||||
XXH3_state_t* state = &sctx;
|
||||
|
||||
WASM_EXPORT
|
||||
void Hash_Init() {
|
||||
// seed is at the memory object
|
||||
uint64_t seed = *((uint64_t*)main_buffer);
|
||||
XXH3_64bits_reset_withSeed(state, seed);
|
||||
}
|
||||
|
||||
WASM_EXPORT
|
||||
void Hash_Update(uint32_t length) {
|
||||
const void* input = main_buffer;
|
||||
XXH3_64bits_update(state, input, length);
|
||||
}
|
||||
|
||||
WASM_EXPORT
|
||||
void Hash_Final() {
|
||||
XXH64_hash_t const result = XXH_swap64(XXH3_64bits_digest(state));
|
||||
memcpy64(main_buffer, &result);
|
||||
}
|
||||
|
||||
WASM_EXPORT
|
||||
const uint32_t STATE_SIZE = sizeof(sctx);
|
||||
|
||||
WASM_EXPORT
|
||||
uint8_t* Hash_GetState() {
|
||||
return (uint8_t*)&sctx;
|
||||
}
|
||||
|
||||
WASM_EXPORT
|
||||
void Hash_Calculate() {
|
||||
return;
|
||||
}
|
||||
+183
@@ -0,0 +1,183 @@
|
||||
// //////////////////////////////////////////////////////////
|
||||
// xxhash32.h
|
||||
// Copyright (c) 2016 Stephan Brumme. All rights reserved.
|
||||
// see http://create.stephan-brumme.com/disclaimer.html
|
||||
//
|
||||
// XXHash (32 bit), based on Yann Collet's descriptions, see
|
||||
// http://cyan4973.github.io/xxHash/
|
||||
//
|
||||
// Modified for hash-wasm by Dani Biró
|
||||
//
|
||||
|
||||
#define WITH_BUFFER
|
||||
#include "hash-wasm.h"
|
||||
#define bswap32 __builtin_bswap32
|
||||
|
||||
static const uint32_t Prime1 = 2654435761U;
|
||||
static const uint32_t Prime2 = 2246822519U;
|
||||
static const uint32_t Prime3 = 3266489917U;
|
||||
static const uint32_t Prime4 = 668265263U;
|
||||
static const uint32_t Prime5 = 374761393U;
|
||||
|
||||
// temporarily store up to 15 bytes between multiple add() calls
|
||||
#define MAX_BUFFER_SIZE (15 + 1)
|
||||
|
||||
// internal state and temporary buffer
|
||||
struct XXHash32_CTX {
|
||||
uint32_t state[4]; // state[2] == seed if totalLength < MAX_BUFFER_SIZE
|
||||
unsigned char buffer[MAX_BUFFER_SIZE];
|
||||
unsigned int bufferSize;
|
||||
uint64_t totalLength;
|
||||
};
|
||||
|
||||
static struct XXHash32_CTX sctx;
|
||||
|
||||
// rotate bits, should compile to a single CPU instruction (ROL)
|
||||
static inline uint32_t rotateLeft(uint32_t x, unsigned char bits) {
|
||||
return (x << bits) | (x >> (32 - bits));
|
||||
}
|
||||
|
||||
// process a block of 4x4 bytes, this is the main part of the XXHash32
|
||||
// algorithm
|
||||
static inline void process(
|
||||
const void* data, uint32_t* state0, uint32_t* state1,
|
||||
uint32_t* state2, uint32_t* state3
|
||||
) {
|
||||
const uint32_t* block = (const uint32_t*)data;
|
||||
*state0 = rotateLeft(*state0 + block[0] * Prime2, 13) * Prime1;
|
||||
*state1 = rotateLeft(*state1 + block[1] * Prime2, 13) * Prime1;
|
||||
*state2 = rotateLeft(*state2 + block[2] * Prime2, 13) * Prime1;
|
||||
*state3 = rotateLeft(*state3 + block[3] * Prime2, 13) * Prime1;
|
||||
}
|
||||
|
||||
// create new XXHash (32 bit)
|
||||
/** @param seed your seed value, even zero is a valid seed and e.g. used by LZ4
|
||||
* **/
|
||||
WASM_EXPORT
|
||||
void Hash_Init(uint32_t seed) {
|
||||
sctx.state[0] = seed + Prime1 + Prime2;
|
||||
sctx.state[1] = seed + Prime2;
|
||||
sctx.state[2] = seed;
|
||||
sctx.state[3] = seed - Prime1;
|
||||
sctx.bufferSize = 0;
|
||||
sctx.totalLength = 0;
|
||||
}
|
||||
|
||||
// add a chunk of bytes
|
||||
/** @param length number of bytes
|
||||
@return false if parameters are invalid / zero **/
|
||||
|
||||
WASM_EXPORT
|
||||
void Hash_Update(uint32_t length) {
|
||||
const void* input = main_buffer;
|
||||
|
||||
// no data ?
|
||||
if (!input || length == 0) return;
|
||||
|
||||
sctx.totalLength += length;
|
||||
// byte-wise access
|
||||
const unsigned char* data = (const unsigned char*)input;
|
||||
|
||||
// unprocessed old data plus new data still fit in temporary buffer ?
|
||||
if (sctx.bufferSize + length < MAX_BUFFER_SIZE) {
|
||||
// just add new data
|
||||
while (length-- > 0) {
|
||||
sctx.buffer[sctx.bufferSize++] = *data++;
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
// point beyond last byte
|
||||
const unsigned char* stop = data + length;
|
||||
const unsigned char* stopBlock = stop - MAX_BUFFER_SIZE;
|
||||
|
||||
// some data left from previous update ?
|
||||
if (sctx.bufferSize > 0) {
|
||||
// make sure temporary buffer is full (16 bytes)
|
||||
while (sctx.bufferSize < MAX_BUFFER_SIZE) {
|
||||
sctx.buffer[sctx.bufferSize++] = *data++;
|
||||
}
|
||||
|
||||
// process these 16 bytes (4x4)
|
||||
process(sctx.buffer, &sctx.state[0], &sctx.state[1], &sctx.state[2], &sctx.state[3]);
|
||||
}
|
||||
|
||||
// copying state to local variables helps optimizer A LOT
|
||||
uint32_t s0 = sctx.state[0], s1 = sctx.state[1], s2 = sctx.state[2], s3 = sctx.state[3];
|
||||
// 16 bytes at once
|
||||
while (data <= stopBlock) {
|
||||
// local variables s0..s3 instead of state[0]..state[3] are much faster
|
||||
process(data, &s0, &s1, &s2, &s3);
|
||||
data += 16;
|
||||
}
|
||||
// copy back
|
||||
sctx.state[0] = s0;
|
||||
sctx.state[1] = s1;
|
||||
sctx.state[2] = s2;
|
||||
sctx.state[3] = s3;
|
||||
|
||||
// copy remainder to temporary buffer
|
||||
sctx.bufferSize = stop - data;
|
||||
for (unsigned int i = 0; i < sctx.bufferSize; i++) {
|
||||
sctx.buffer[i] = data[i];
|
||||
}
|
||||
}
|
||||
|
||||
// get current hash
|
||||
/** @return 32 bit XXHash **/
|
||||
WASM_EXPORT
|
||||
void Hash_Final() {
|
||||
uint32_t result = (uint32_t)sctx.totalLength;
|
||||
|
||||
// fold 128 bit state into one single 32 bit value
|
||||
if (sctx.totalLength >= MAX_BUFFER_SIZE) {
|
||||
result += rotateLeft(sctx.state[0], 1) +
|
||||
rotateLeft(sctx.state[1], 7) +
|
||||
rotateLeft(sctx.state[2], 12) +
|
||||
rotateLeft(sctx.state[3], 18);
|
||||
} else {
|
||||
// internal state wasn't set in add(), therefore original seed is still
|
||||
// stored in state2
|
||||
result += sctx.state[2] + Prime5;
|
||||
}
|
||||
|
||||
// process remaining bytes in temporary buffer
|
||||
const unsigned char* data = sctx.buffer;
|
||||
// point beyond last byte
|
||||
const unsigned char* stop = data + sctx.bufferSize;
|
||||
|
||||
// at least 4 bytes left ? => eat 4 bytes per step
|
||||
for (; data + 4 <= stop; data += 4) {
|
||||
result = rotateLeft(result + *(uint32_t*)data * Prime3, 17) * Prime4;
|
||||
}
|
||||
|
||||
// take care of remaining 0..3 bytes, eat 1 byte per step
|
||||
while (data != stop) {
|
||||
result = rotateLeft(result + (*data++) * Prime5, 11) * Prime1;
|
||||
}
|
||||
|
||||
// mix bits
|
||||
result ^= result >> 15;
|
||||
result *= Prime2;
|
||||
result ^= result >> 13;
|
||||
result *= Prime3;
|
||||
result ^= result >> 16;
|
||||
|
||||
result = bswap32(result);
|
||||
memcpy32(main_buffer, &result);
|
||||
}
|
||||
|
||||
WASM_EXPORT
|
||||
const uint32_t STATE_SIZE = sizeof(sctx);
|
||||
|
||||
WASM_EXPORT
|
||||
uint8_t* Hash_GetState() {
|
||||
return (uint8_t*) &sctx;
|
||||
}
|
||||
|
||||
WASM_EXPORT
|
||||
void Hash_Calculate(uint32_t length, uint32_t initParam) {
|
||||
Hash_Init(initParam);
|
||||
Hash_Update(length);
|
||||
Hash_Final();
|
||||
}
|
||||
+199
@@ -0,0 +1,199 @@
|
||||
// //////////////////////////////////////////////////////////
|
||||
// xxhash64.h
|
||||
// Copyright (c) 2016 Stephan Brumme. All rights reserved.
|
||||
// see http://create.stephan-brumme.com/disclaimer.html
|
||||
//
|
||||
// XXHash (64 bit), based on Yann Collet's descriptions, see
|
||||
// http://cyan4973.github.io/xxHash/
|
||||
//
|
||||
// Modified for hash-wasm by Dani Biró
|
||||
//
|
||||
|
||||
#define WITH_BUFFER
|
||||
#include "hash-wasm.h"
|
||||
#define bswap64 __builtin_bswap64
|
||||
|
||||
const uint64_t Prime1 = 11400714785074694791ULL;
|
||||
const uint64_t Prime2 = 14029467366897019727ULL;
|
||||
const uint64_t Prime3 = 1609587929392839161ULL;
|
||||
const uint64_t Prime4 = 9650029242287828579ULL;
|
||||
const uint64_t Prime5 = 2870177450012600261ULL;
|
||||
|
||||
// temporarily store up to 31 bytes between multiple add() calls
|
||||
#define MAX_BUFFER_SIZE (31 + 1)
|
||||
|
||||
struct XXHash64_CTX {
|
||||
uint64_t state[4];
|
||||
unsigned char buffer[MAX_BUFFER_SIZE];
|
||||
unsigned int bufferSize;
|
||||
uint64_t totalLength;
|
||||
};
|
||||
|
||||
static struct XXHash64_CTX sctx;
|
||||
|
||||
// rotate bits, should compile to a single CPU instruction (ROL)
|
||||
static inline uint64_t rotateLeft(uint64_t x, unsigned char bits) {
|
||||
return (x << bits) | (x >> (64 - bits));
|
||||
}
|
||||
|
||||
// process a single 64 bit value
|
||||
static inline uint64_t processSingle(uint64_t previous, uint64_t input) {
|
||||
return rotateLeft(previous + input * Prime2, 31) * Prime1;
|
||||
}
|
||||
|
||||
// process a block of 4x4 bytes, this is the main part of the XXHash32
|
||||
// algorithm
|
||||
static inline void process(
|
||||
const void* data, uint64_t* state0, uint64_t* state1,
|
||||
uint64_t* state2, uint64_t* state3
|
||||
) {
|
||||
const uint64_t* block = (const uint64_t*)data;
|
||||
*state0 = processSingle(*state0, block[0]);
|
||||
*state1 = processSingle(*state1, block[1]);
|
||||
*state2 = processSingle(*state2, block[2]);
|
||||
*state3 = processSingle(*state3, block[3]);
|
||||
}
|
||||
|
||||
WASM_EXPORT
|
||||
void Hash_Init() {
|
||||
// seed is at the memory object
|
||||
uint64_t seed = *((uint64_t*)main_buffer);
|
||||
|
||||
sctx.state[0] = seed + Prime1 + Prime2;
|
||||
sctx.state[1] = seed + Prime2;
|
||||
sctx.state[2] = seed;
|
||||
sctx.state[3] = seed - Prime1;
|
||||
sctx.bufferSize = 0;
|
||||
sctx.totalLength = 0;
|
||||
}
|
||||
|
||||
// add a chunk of bytes
|
||||
/** @param length number of bytes
|
||||
@return false if parameters are invalid / zero **/
|
||||
|
||||
WASM_EXPORT
|
||||
void Hash_Update(uint32_t length) {
|
||||
const void* input = main_buffer;
|
||||
|
||||
// no data ?
|
||||
if (length == 0) return;
|
||||
|
||||
sctx.totalLength += length;
|
||||
// byte-wise access
|
||||
const unsigned char* data = (const unsigned char*)input;
|
||||
|
||||
// unprocessed old data plus new data still fit in temporary buffer ?
|
||||
if (sctx.bufferSize + length < MAX_BUFFER_SIZE) {
|
||||
// just add new data
|
||||
while (length-- > 0) {
|
||||
sctx.buffer[sctx.bufferSize++] = *data++;
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
// point beyond last byte
|
||||
const unsigned char* stop = data + length;
|
||||
const unsigned char* stopBlock = stop - MAX_BUFFER_SIZE;
|
||||
|
||||
// some data left from previous update ?
|
||||
if (sctx.bufferSize > 0) {
|
||||
// make sure temporary buffer is full (16 bytes)
|
||||
while (sctx.bufferSize < MAX_BUFFER_SIZE) {
|
||||
sctx.buffer[sctx.bufferSize++] = *data++;
|
||||
}
|
||||
|
||||
// process these 32 bytes (4x8)
|
||||
process(sctx.buffer, &sctx.state[0], &sctx.state[1], &sctx.state[2], &sctx.state[3]);
|
||||
}
|
||||
|
||||
// copying state to local variables helps optimizer A LOT
|
||||
uint64_t s0 = sctx.state[0], s1 = sctx.state[1], s2 = sctx.state[2], s3 = sctx.state[3];
|
||||
// 32 bytes at once
|
||||
while (data <= stopBlock) {
|
||||
// local variables s0..s3 instead of state[0]..state[3] are much faster
|
||||
process(data, &s0, &s1, &s2, &s3);
|
||||
data += 32;
|
||||
}
|
||||
// copy back
|
||||
sctx.state[0] = s0;
|
||||
sctx.state[1] = s1;
|
||||
sctx.state[2] = s2;
|
||||
sctx.state[3] = s3;
|
||||
|
||||
// copy remainder to temporary buffer
|
||||
sctx.bufferSize = stop - data;
|
||||
for (unsigned int i = 0; i < sctx.bufferSize; i++) {
|
||||
sctx.buffer[i] = data[i];
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
/// get current hash
|
||||
WASM_EXPORT
|
||||
void Hash_Final() {
|
||||
// fold 256 bit state into one single 64 bit value
|
||||
uint64_t result;
|
||||
if (sctx.totalLength >= MAX_BUFFER_SIZE) {
|
||||
result = rotateLeft(sctx.state[0], 1) + rotateLeft(sctx.state[1], 7) +
|
||||
rotateLeft(sctx.state[2], 12) + rotateLeft(sctx.state[3], 18);
|
||||
result = (result ^ processSingle(0, sctx.state[0])) * Prime1 + Prime4;
|
||||
result = (result ^ processSingle(0, sctx.state[1])) * Prime1 + Prime4;
|
||||
result = (result ^ processSingle(0, sctx.state[2])) * Prime1 + Prime4;
|
||||
result = (result ^ processSingle(0, sctx.state[3])) * Prime1 + Prime4;
|
||||
} else {
|
||||
// internal state wasn't set in add(), therefore original seed is still
|
||||
// stored in state2
|
||||
result = sctx.state[2] + Prime5;
|
||||
}
|
||||
|
||||
result += sctx.totalLength;
|
||||
|
||||
// process remaining bytes in temporary buffer
|
||||
const unsigned char* data = sctx.buffer;
|
||||
// point beyond last byte
|
||||
const unsigned char* stop = data + sctx.bufferSize;
|
||||
|
||||
// at least 8 bytes left ? => eat 8 bytes per step
|
||||
for (; data + 8 <= stop; data += 8) {
|
||||
result =
|
||||
rotateLeft(result ^ processSingle(0, *(uint64_t*)data), 27) * Prime1 +
|
||||
Prime4;
|
||||
}
|
||||
|
||||
// 4 bytes left ? => eat those
|
||||
if (data + 4 <= stop) {
|
||||
result =
|
||||
rotateLeft(result ^ (*(uint32_t*)data) * Prime1, 23) * Prime2 + Prime3;
|
||||
data += 4;
|
||||
}
|
||||
|
||||
// take care of remaining 0..3 bytes, eat 1 byte per step
|
||||
while (data != stop) {
|
||||
result = rotateLeft(result ^ (*data++) * Prime5, 11) * Prime1;
|
||||
}
|
||||
|
||||
// mix bits
|
||||
result ^= result >> 33;
|
||||
result *= Prime2;
|
||||
result ^= result >> 29;
|
||||
result *= Prime3;
|
||||
result ^= result >> 32;
|
||||
|
||||
result = bswap64(result);
|
||||
memcpy64(main_buffer, &result);
|
||||
}
|
||||
|
||||
WASM_EXPORT
|
||||
const uint32_t STATE_SIZE = sizeof(sctx);
|
||||
|
||||
WASM_EXPORT
|
||||
uint8_t* Hash_GetState() {
|
||||
return (uint8_t*) &sctx;
|
||||
}
|
||||
|
||||
WASM_EXPORT
|
||||
void Hash_Calculate() {
|
||||
return;
|
||||
}
|
||||
Reference in New Issue
Block a user