feat: Replace aCAPTCHA with official ALTCHA (altcha.org) Proof-of-Work web component widget

This commit is contained in:
Richard
2026-07-31 11:39:29 +02:00
parent 6ef74c7cf4
commit 83c782c469
417 changed files with 100543 additions and 317 deletions
+143
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
File diff suppressed because it is too large Load Diff
+871
View File
@@ -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
View File
@@ -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
View File
@@ -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;
}