diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..d71b894 --- /dev/null +++ b/Makefile @@ -0,0 +1,24 @@ +# Makefile +# 19-Nov-11 Markku-Juhani O. Saarinen + +BINARY = sha3test +OBJS = sha3.o main.o +DIST = tiny_sha3 + +CC = gcc +CFLAGS = -Wall -O3 +LIBS = +LDFLAGS = +INCLUDES = + +$(BINARY): $(OBJS) + $(CC) $(LDFLAGS) -o $(BINARY) $(OBJS) $(LIBS) + +.c.o: + $(CC) $(CFLAGS) $(INCLUDES) -c $< -o $@ + +clean: + rm -rf $(DIST).tgz $(OBJS) $(BINARY) *~ + +dist: clean + cd ..; tar cfvz $(DIST)/$(DIST).tgz $(DIST)/* diff --git a/README.md b/README.md index d28850e..3c8f2d1 100644 --- a/README.md +++ b/README.md @@ -1,2 +1,53 @@ # tiny_sha3 Very small, readable implementation of the SHA3 hash function. +Updated 03-Sep-15: + +Made the implementation portable. The API is now pretty much the +same that OpenSSL uses. Public domain. + + +### Updated 07-Aug-15: + +Now that SHA3 spec is out, I've updated the package to match with the +new padding rules. There is literally one line difference between +Keccak 3.0 and SHA-3 implementations: + +``` + temp[inlen++] = 0x06; // XXX Padding Changed from Keccak 3.0 +``` + +The 0x06 constant there used to be 0x01. But this of course totally +breaks compatibility and test vectors had to be revised. + +SHA-3 Spec: http://nvlpubs.nist.gov/nistpubs/FIPS/NIST.FIPS.202.pdf + +Cheers, +- markku + + +### Original README.TXT from 2011: + +Hi. + +The SHA-3 competition is nearing it's end and I would personally like +to support Keccak as the winner. I have a PhD in hash function cryptanalysis +so don't take my word for it, go ahead and look into the code ! + +Since I couldn't find a *compact* and/or *readable* implementation of Keccak +anywhere, here's one I cooked up as a service to the curious. + +This implementation is intended for study of the algorithm, not for +production use. + +The code works correctly on 64-bit little-endian platforms with gcc. +Like your Linux box. The main.c module contains self-tests for all +officially supported hash sizes. + +If you're looking for production code, the official multi-megabyte package +covers everyting you could possibly need and too much much more: +http://keccak.noekeon.org/ + +Cheers, +- Markku 19-Nov-11 + +Dr. Markku-Juhani O. Saarinen diff --git a/main.c b/main.c new file mode 100644 index 0000000..dd4f6f9 --- /dev/null +++ b/main.c @@ -0,0 +1,108 @@ +// main.c +// 19-Nov-11 Markku-Juhani O. Saarinen + +#include +#include +#include "sha3.h" + +// read a hex string, return byte length or -1 on error. + +static int test_hexdigit(char ch) +{ + if (ch >= '0' && ch <= '9') + return ch - '0'; + if (ch >= 'A' && ch <= 'F') + return ch - 'A' + 10; + if (ch >= 'a' && ch <= 'f') + return ch - 'a' + 10; + return -1; +} + +static int test_readhex(uint8_t *buf, const char *str, int maxbytes) +{ + int i, h, l; + + for (i = 0; i < maxbytes; i++) { + h = test_hexdigit(str[2 * i]); + if (h < 0) + return i; + l = test_hexdigit(str[2 * i + 1]); + if (l < 0) + return i; + buf[i] = (h << 4) + l; + } + + return i; +} + +// returns zero on success, nonzero + stderr messages on failure + +int test_sha3() +{ + // message / digest pairs, lifted from ShortMsgKAT_SHA3-xxx.txt files + // in the official package: https://github.com/gvanas/KeccakCodePackage + + const char *testvec[][2] = { + { // SHA3-224, corner case with 0-length message + "", + "6B4E03423667DBB73B6E15454F0EB1ABD4597F9A1B078E3F5B5A6BC7" + }, + { // SHA3-256, short message + "9F2FCC7C90DE090D6B87CD7E9718C1EA6CB21118FC2D5DE9F97E5DB6AC1E9C10", + "2F1A5F7159E34EA19CDDC70EBF9B81F1A66DB40615D7EAD3CC1F1B954D82A3AF" + }, + { // SHA3-384, exact block size + "E35780EB9799AD4C77535D4DDB683CF33EF367715327CF4C4A58ED9CBDCDD486" + "F669F80189D549A9364FA82A51A52654EC721BB3AAB95DCEB4A86A6AFA93826D" + "B923517E928F33E3FBA850D45660EF83B9876ACCAFA2A9987A254B137C6E140A" + "21691E1069413848", + "D1C0FA85C8D183BEFF99AD9D752B263E286B477F79F0710B0103170173978133" + "44B99DAF3BB7B1BC5E8D722BAC85943A" + }, + { // SHA3-512, multiblock message + "3A3A819C48EFDE2AD914FBF00E18AB6BC4F14513AB27D0C178A188B61431E7F5" + "623CB66B23346775D386B50E982C493ADBBFC54B9A3CD383382336A1A0B2150A" + "15358F336D03AE18F666C7573D55C4FD181C29E6CCFDE63EA35F0ADF5885CFC0" + "A3D84A2B2E4DD24496DB789E663170CEF74798AA1BBCD4574EA0BBA40489D764" + "B2F83AADC66B148B4A0CD95246C127D5871C4F11418690A5DDF01246A0C80A43" + "C70088B6183639DCFDA4125BD113A8F49EE23ED306FAAC576C3FB0C1E256671D" + "817FC2534A52F5B439F72E424DE376F4C565CCA82307DD9EF76DA5B7C4EB7E08" + "5172E328807C02D011FFBF33785378D79DC266F6A5BE6BB0E4A92ECEEBAEB1", + "6E8B8BD195BDD560689AF2348BDC74AB7CD05ED8B9A57711E9BE71E9726FDA45" + "91FEE12205EDACAF82FFBBAF16DFF9E702A708862080166C2FF6BA379BC7FFC2" + } + }; + + int i, fails, msg_len, sha_len; + uint8_t sha[64], buf[64], msg[256]; + + fails = 0; + for (i = 0; i < 4; i++) { + + memset(sha, 0, sizeof(sha)); + memset(buf, 0, sizeof(buf)); + memset(msg, 0, sizeof(msg)); + + msg_len = test_readhex(msg, testvec[i][0], sizeof(msg)); + sha_len = test_readhex(sha, testvec[i][1], sizeof(sha)); + + sha3(msg, msg_len, buf, sha_len); + + if (memcmp(sha, buf, sha_len) != 0) { + fails++; + fprintf(stderr, "[%d] SHA3-%d, len %d test FAILED.\n", + i, sha_len * 8, msg_len); + } + } + + return fails; +} + +// main +int main(int argc, char **argv) +{ + if (test_sha3() == 0) + printf("SHA-3 Self-Test OK!\n"); + + return 0; +} diff --git a/sha3.c b/sha3.c new file mode 100644 index 0000000..3e14c9e --- /dev/null +++ b/sha3.c @@ -0,0 +1,165 @@ +// sha3.c +// 19-Nov-11 Markku-Juhani O. Saarinen + +// Revised 07-Aug-15 to match with official release of FIPS PUB 202 "SHA3" +// Revised 03-Sep-15 for portability + OpenSSL - style API + +#include "sha3.h" + +// update the state with given number of rounds + +static void sha3_keccakf(uint64_t st[25], int rounds) +{ + // constants + const uint64_t keccakf_rndc[24] = { + 0x0000000000000001, 0x0000000000008082, 0x800000000000808a, + 0x8000000080008000, 0x000000000000808b, 0x0000000080000001, + 0x8000000080008081, 0x8000000000008009, 0x000000000000008a, + 0x0000000000000088, 0x0000000080008009, 0x000000008000000a, + 0x000000008000808b, 0x800000000000008b, 0x8000000000008089, + 0x8000000000008003, 0x8000000000008002, 0x8000000000000080, + 0x000000000000800a, 0x800000008000000a, 0x8000000080008081, + 0x8000000000008080, 0x0000000080000001, 0x8000000080008008 + }; + const int keccakf_rotc[24] = { + 1, 3, 6, 10, 15, 21, 28, 36, 45, 55, 2, 14, + 27, 41, 56, 8, 25, 43, 62, 18, 39, 61, 20, 44 + }; + const int keccakf_piln[24] = { + 10, 7, 11, 17, 18, 3, 5, 16, 8, 21, 24, 4, + 15, 23, 19, 13, 12, 2, 20, 14, 22, 9, 6, 1 + }; + + // variables + int i, j, r; + uint64_t t, bc[5]; + +#if __BYTE_ORDER__ != __ORDER_LITTLE_ENDIAN__ + uint8_t *v; + + // endianess conversion. this is redundant on little-endian targets + for (i = 0; i < 25; i++) { + v = (uint8_t *) &st[i]; + st[i] = ((uint64_t) v[0]) | (((uint64_t) v[1]) << 8) | + (((uint64_t) v[2]) << 16) | (((uint64_t) v[3]) << 24) | + (((uint64_t) v[4]) << 32) | (((uint64_t) v[5]) << 40) | + (((uint64_t) v[6]) << 48) | (((uint64_t) v[7]) << 56); + } +#endif + + // actual iteration + for (r = 0; r < rounds; r++) { + + // Theta + for (i = 0; i < 5; i++) + bc[i] = st[i] ^ st[i + 5] ^ st[i + 10] ^ st[i + 15] ^ st[i + 20]; + + for (i = 0; i < 5; i++) { + t = bc[(i + 4) % 5] ^ ROTL64(bc[(i + 1) % 5], 1); + for (j = 0; j < 25; j += 5) + st[j + i] ^= t; + } + + // Rho Pi + t = st[1]; + for (i = 0; i < 24; i++) { + j = keccakf_piln[i]; + bc[0] = st[j]; + st[j] = ROTL64(t, keccakf_rotc[i]); + t = bc[0]; + } + + // Chi + for (j = 0; j < 25; j += 5) { + for (i = 0; i < 5; i++) + bc[i] = st[j + i]; + for (i = 0; i < 5; i++) + st[j + i] ^= (~bc[(i + 1) % 5]) & bc[(i + 2) % 5]; + } + + // Iota + st[0] ^= keccakf_rndc[r]; + } + +#if __BYTE_ORDER__ != __ORDER_LITTLE_ENDIAN__ + // endianess conversion. this is redundant on little-endian targets + for (i = 0; i < 25; i++) { + v = (uint8_t *) &st[i]; + t = st[i]; + v[0] = t & 0xFF; + v[1] = (t >> 8) & 0xFF; + v[2] = (t >> 16) & 0xFF; + v[3] = (t >> 24) & 0xFF; + v[4] = (t >> 32) & 0xFF; + v[5] = (t >> 40) & 0xFF; + v[6] = (t >> 48) & 0xFF; + v[7] = (t >> 56) & 0xFF; + } +#endif +} + +// Initialize the context for SHA3 + +int sha3_init(sha3_ctx_t *c, int mdlen) +{ + int i; + + for (i = 0; i < 25; i++) + c->st.q[i] = 0; + c->mdlen = mdlen; + c->rsiz = 200 - 2 * mdlen; + c->pt = 0; + + return 1; +} + +// update state with more data + +int sha3_update(sha3_ctx_t *c, const void *data, size_t len) +{ + size_t i; + int j; + + j = c->pt; + for (i = 0; i < len; i++) { + c->st.b[j++] ^= ((const uint8_t *) data)[i]; + if (j >= c->rsiz) { + sha3_keccakf(c->st.q, SHA3_ROUNDS); + j = 0; + } + } + c->pt = j; + + return 1; +} + +// finalize and output a hash + +int sha3_final(void *md, sha3_ctx_t *c) +{ + int i; + + c->st.b[c->pt] ^= 0x06; + c->st.b[c->rsiz - 1] ^= 0x80; + sha3_keccakf(c->st.q, SHA3_ROUNDS); + + for (i = 0; i < c->mdlen; i++) { + ((uint8_t *) md)[i] = c->st.b[i]; + } + + return 1; +} + +// compute a SHA-3 hash (md) of given byte length from "in" + +unsigned char *sha3(const uint8_t *in, size_t inlen, uint8_t *md, int mdlen) +{ + sha3_ctx_t sha3; + + sha3_init(&sha3, mdlen); + sha3_update(&sha3, in, inlen); + sha3_final(md, &sha3); + + return md; +} + diff --git a/sha3.h b/sha3.h new file mode 100644 index 0000000..21e8a4a --- /dev/null +++ b/sha3.h @@ -0,0 +1,36 @@ +// sha3.h +// 19-Nov-11 Markku-Juhani O. Saarinen + +#ifndef SHA3_H +#define SHA3_H + +#include +#include + +#ifndef SHA3_ROUNDS +#define SHA3_ROUNDS 24 +#endif + +#ifndef ROTL64 +#define ROTL64(x, y) (((x) << (y)) | ((x) >> (64 - (y)))) +#endif + +// state context +typedef struct { + union { // state: + uint8_t b[200]; // 8-bit bytes + uint64_t q[25]; // 64-bit words + } st; + int pt, rsiz, mdlen; // these don't overflow +} sha3_ctx_t; + +// OpenSSL - like interfece +int sha3_init(sha3_ctx_t *c, int mdlen); // mdlen = hash output in bytes +int sha3_update(sha3_ctx_t *c, const void *data, size_t len); +int sha3_final(void *md, sha3_ctx_t *c); // digest goes to md + +// compute a sha3 hash (md) of given byte length from "in" +unsigned char *sha3(const uint8_t *in, size_t inlen, uint8_t *md, int mdlen); + +#endif +