Files
scriptc/packages/runtime/test/test_sha_kernels.c
T

206 lines
7.9 KiB
C

/* SHA-1, SHA-256, SHA-384 and SHA-512 block kernels (scr_lib.c), built by
* sha-kernels.test.ts. The unit is included directly so the test can run
* each kernel the CPU supports and the portable blocks over the same
* inputs: known vectors, every length from 0 to 1100 bytes, and 1 MiB,
* each fed whole and in 1-, 7-, 64- and 100-byte updates. The SHA-1 and
* SHA-256 kernels use the SHA extensions; the SHA-512 kernels use AVX2 or
* AVX-512VL on x86-64 and the SHA-512 extension on arm64. On CPUs without
* them only the portable blocks run. */
#include "../src/scr_lib.c"
#include <stdio.h>
#include <string.h>
static long total = 0, failed = 0;
static void check(bool ok, const char *what, size_t len, size_t chunk) {
total++;
if (!ok) {
failed++;
fprintf(stderr, "FAIL: %s length %zu chunk %zu\n", what, len, chunk);
}
}
static void hex(const unsigned char *d, size_t n, char *out) {
for (size_t i = 0; i < n; i++) sprintf(out + 2 * i, "%02x", d[i]);
}
static void sha256_of(const unsigned char *data, size_t len, size_t chunk, unsigned char out[32]) {
ScrSha256Ctx ctx;
scr_sha256_init(&ctx);
for (size_t at = 0; at < len;) {
size_t take = chunk == 0 || chunk > len - at ? len - at : chunk;
scr_sha256_update(&ctx, data + at, take);
at += take;
}
scr_sha256_final(&ctx, out);
}
static void sha1_of(const unsigned char *data, size_t len, size_t chunk, unsigned char out[32]) {
ScrSha1Ctx ctx;
scr_sha1_init(&ctx);
for (size_t at = 0; at < len;) {
size_t take = chunk == 0 || chunk > len - at ? len - at : chunk;
scr_sha1_update(&ctx, data + at, take);
at += take;
}
scr_sha1_final(&ctx, out);
}
static void sha512_of(const unsigned char *data, size_t len, size_t chunk, bool sha384,
unsigned char out[64]) {
ScrSha512Ctx ctx;
scr_sha512_init(&ctx, sha384);
for (size_t at = 0; at < len;) {
size_t take = chunk == 0 || chunk > len - at ? len - at : chunk;
scr_sha512_update(&ctx, data + at, take);
at += take;
}
scr_sha512_final(&ctx, out, sha384);
}
/* FIPS 180-4 example vectors for SHA-384 and SHA-512. */
static void vectors512(const char *path) {
unsigned char d[64];
char text[129];
static const char two_block[] =
"abcdefghbcdefghicdefghijdefghijkefghijklfghijklmghijklmnhijklmnoijklmnopjklmnopqklmnopqrlmnopqrsmnopqrstnopqrstu";
sha512_of((const unsigned char *)"abc", 3, 0, false, d);
hex(d, 64, text);
check(strcmp(text, "ddaf35a193617abacc417349ae20413112e6fa4e89a97ea20a9eeee64b55d39a2192992a274fc1a836ba3c23a3feebbd454d4423643ce80e2a9ac94fa54ca49f") == 0, path, 3, 0);
sha512_of((const unsigned char *)"", 0, 0, false, d);
hex(d, 64, text);
check(strcmp(text, "cf83e1357eefb8bdf1542850d66d8007d620e4050b5715dc83f4a921d36ce9ce47d0d13c5d85f2b0ff8318d2877eec2f63b931bd47417a81a538327af927da3e") == 0, path, 0, 0);
sha512_of((const unsigned char *)two_block, sizeof two_block - 1, 0, false, d);
hex(d, 64, text);
check(strcmp(text, "8e959b75dae313da8cf4f72814fc143f8f7779c6eb9f7fa17299aeadb6889018501d289e4900f7e4331b99dec4b5433ac7d329eeb6dd26545e96e55b874be909") == 0, path, sizeof two_block - 1, 0);
sha512_of((const unsigned char *)"abc", 3, 0, true, d);
hex(d, 48, text);
check(strcmp(text, "cb00753f45a35e8bb5a03d699ac65007272c32ab0eded1631a8b605a43ff5bed8086072ba1e7cc2358baeca134c825a7") == 0, path, 3, 0);
sha512_of((const unsigned char *)two_block, sizeof two_block - 1, 0, true, d);
hex(d, 48, text);
check(strcmp(text, "09330c33f71147e83d192fc782cd1b4753111b173b3b05d22fa08086e3b0f712fcc7c71a557e2db966c3e9fa91746039") == 0, path, sizeof two_block - 1, 0);
static unsigned char million[1000000];
memset(million, 'a', sizeof million);
sha512_of(million, sizeof million, 0, false, d);
hex(d, 64, text);
check(strcmp(text, "e718483d0ce769644e2e42c7bc15b4638e1f98b13b2044285632a803afa973ebde0ff244877ea60a4cb0432ce577c31beb009c5c2c49aa2e4eadb217ad8cc09b") == 0, path, sizeof million, 0);
sha512_of(million, sizeof million, 0, true, d);
hex(d, 48, text);
check(strcmp(text, "9d0e1809716474cb086e834e310a4a1ced149e9c00f248527972cec5704c2a5b07b8b3dc38ecc4ebae97ddd87f3d8985") == 0, path, sizeof million, 0);
}
/* Selects the SHA-512 kernel: 0 the portable blocks, otherwise the CPU's
* kernel. Returns false when the CPU has none. */
static bool select512(int kernel) {
#ifdef SCR_SHA_NI
scr_sha512_x86_state = 0;
int best = scr_sha512_x86();
if (kernel > best - 1) return false;
scr_sha512_x86_state = kernel == 0 ? 1 : kernel + 1;
return true;
#elif defined(SCR_SHA512_ARM)
scr_sha512_arm_state = 0;
bool available = scr_sha512_arm();
if (kernel > (available ? 1 : 0)) return false;
scr_sha512_arm_state = kernel == 0 ? 2 : 1;
return true;
#else
return kernel == 0;
#endif
}
static const char *const kernel512_names[] = {"portable", "avx2-or-arm", "avx512"};
static void vectors(const char *path) {
unsigned char d[32];
char text[65];
sha256_of((const unsigned char *)"abc", 3, 0, d);
hex(d, 32, text);
check(strcmp(text, "ba7816bf8f01cfea414140de5dae2223b00361a396177a9cb410ff61f20015ad") == 0, path, 3, 0);
sha256_of((const unsigned char *)"", 0, 0, d);
hex(d, 32, text);
check(strcmp(text, "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855") == 0, path, 0, 0);
sha1_of((const unsigned char *)"abc", 3, 0, d);
hex(d, 20, text);
check(strcmp(text, "a9993e364706816aba3e25717850c26c9cd0d89d") == 0, path, 3, 0);
static unsigned char million[1000000];
memset(million, 'a', sizeof million);
sha256_of(million, sizeof million, 0, d);
hex(d, 32, text);
check(strcmp(text, "cdc76e5c9914fb9281a1c7e284d73e67f1809a48a497200e046d39ccc7112cd0") == 0, path, sizeof million, 0);
sha1_of(million, sizeof million, 0, d);
hex(d, 20, text);
check(strcmp(text, "34aa973cd4c4daa4f61eeb2bdbad27316534016f") == 0, path, sizeof million, 0);
}
int main(void) {
enum { BIG = 1 << 20 };
unsigned char *data = malloc(BIG);
uint32_t x = 0x9e3779b9u;
for (size_t i = 0; i < BIG; i++) {
x ^= x << 13; x ^= x >> 17; x ^= x << 5;
data[i] = (unsigned char)x;
}
static const size_t chunks[] = {0, 1, 7, 64, 100};
bool ni = false;
#ifdef SCR_SHA_NI
ni = scr_sha_ni();
#endif
vectors("portable-or-ni");
for (size_t len = 0; len <= 1100 + 1; len++) {
size_t n = len == 1101 ? BIG : len;
for (size_t c = 0; c < sizeof chunks / sizeof chunks[0]; c++) {
#ifdef SCR_SHA_NI
if (!ni) continue;
unsigned char a[32], b[32];
scr_sha_ni_state = 1;
sha256_of(data, n, chunks[c], a);
scr_sha_ni_state = 2;
sha256_of(data, n, chunks[c], b);
check(memcmp(a, b, 32) == 0, "sha256 ni vs portable", n, chunks[c]);
scr_sha_ni_state = 1;
sha1_of(data, n, chunks[c], a);
scr_sha_ni_state = 2;
sha1_of(data, n, chunks[c], b);
check(memcmp(a, b, 20) == 0, "sha1 ni vs portable", n, chunks[c]);
#endif
}
}
#ifdef SCR_SHA_NI
if (ni) {
scr_sha_ni_state = 2;
vectors("portable");
scr_sha_ni_state = 1;
vectors("ni");
}
#endif
/* SHA-512 and SHA-384: every kernel against the portable blocks, and
* the known vectors through each. */
int kernels512 = 0;
for (int k = 0; k < 3 && select512(k); k++) {
kernels512 = k + 1;
vectors512(kernel512_names[k]);
}
for (int k = 1; k < kernels512; k++) {
for (size_t len = 0; len <= 1100 + 1; len++) {
size_t n = len == 1101 ? BIG : len;
for (size_t c = 0; c < sizeof chunks / sizeof chunks[0]; c++) {
for (int sha384 = 0; sha384 < 2; sha384++) {
unsigned char a[64], b[64];
select512(k);
sha512_of(data, n, chunks[c], sha384, a);
select512(0);
sha512_of(data, n, chunks[c], sha384, b);
check(memcmp(a, b, sha384 ? 48 : 64) == 0, kernel512_names[k], n, chunks[c]);
}
}
}
}
free(data);
fprintf(stderr, "sha-ni %s\n", ni ? "available" : "absent");
fprintf(stderr, "sha-512 kernels %d\n", kernels512);
fprintf(stderr, "%ld/%ld cases passed\n", total - failed, total);
return failed != 0;
}