From df5c98064770cbffa648eb509cd9e7d311db45c4 Mon Sep 17 00:00:00 2001 From: Chris Howe Date: Fri, 28 Jun 2019 17:47:16 -0500 Subject: [PATCH 1/2] removed old file Tests passing with hazmat gf256 code. TODO: make propagate the 32-byte secret limitation out from interpolate to avoid the impedence mismatch copy of y-values. removed non-hazmat gf256 code --- slip39/Makefile | 31 +- slip39/README.md | 18 +- slip39/gf256.c | 85 --- slip39/gf256.h | 12 - slip39/gf256_interpolate.c | 85 --- slip39/gf256_interpolate.h | 37 -- slip39/hazmat.c | 405 ++++++++++++ slip39/hazmat.h | 43 ++ slip39/slip39.h | 95 +-- slip39/slip39_shamir.c | 10 +- ...gf256_interpolate.c => test_interpolate.c} | 59 +- slip39/test_slip39_shamir.c | 3 +- wordlist.c | 588 ------------------ 13 files changed, 580 insertions(+), 891 deletions(-) delete mode 100644 slip39/gf256.c delete mode 100644 slip39/gf256.h delete mode 100644 slip39/gf256_interpolate.c delete mode 100644 slip39/gf256_interpolate.h create mode 100644 slip39/hazmat.c create mode 100644 slip39/hazmat.h rename slip39/{test_gf256_interpolate.c => test_interpolate.c} (72%) delete mode 100644 wordlist.c diff --git a/slip39/Makefile b/slip39/Makefile index 8d71562..fa2aac8 100644 --- a/slip39/Makefile +++ b/slip39/Makefile @@ -3,13 +3,16 @@ CFLAGS += -g -O2 -m64 -std=c99 -pedantic \ -Werror=format-security -Wstrict-prototypes -Wmissing-prototypes \ -D_FORTIFY_SOURCE=2 -fPIC -fno-strict-overflow -SRCS = gf256.c gf256_interpolate.c slip39_encrypt.c slip39_rs1024.c \ - slip39_shamir.c slip39_wordlist.c slip39_mnemonics.c +SRCS = slip39_encrypt.c slip39_rs1024.c \ + slip39_shamir.c slip39_wordlist.c slip39_mnemonics.c hazmat.c OBJS := ${SRCS:.c=.o} all: libslip39.a +libslip39.so: libslip39.so + $(CC) -shared $(CFLAGS) $^ -o $@ + libslip39.a: ../randombytes/librandombytes.a $(OBJS) $(AR) -rcs $@ $^ @@ -21,23 +24,17 @@ slip39_tests.c: vectors_to_tests.js vectors.json slip39_tests.o: slip39_tests.c -slip39_tests.out: slip39_tests.o gf256.o gf256_interpolate.o slip39_wordlist.o slip39_rs1024.o \ +slip39_tests.out: slip39_tests.o hazmat.o slip39_wordlist.o slip39_rs1024.o \ slip39_shamir.o slip39_mnemonics.o test_random.o slip39_encrypt.o $(CC) -o $@ $(CFLAGS) $(LDFLAGS) $^ -l crypto $(MEMCHECK) ./$@ -gf256%.o: gf256&%.c gf256.h gf256%.h - $(CC) -o $@ $(CFLAGS) $(LDFLAGS) $< - -test_gf256.o: test_gf256.c gf256.h - -test_gf256.out: test_gf256.o gf256.o - $(CC) -o $@ $(CFLAGS) $(LDFLAGS) $^ $(LOADLIBES) $(LDLIBS) - $(MEMCHECK) ./$@ +#gf256%.o: gf256&%.c hazmat.h gf256%.h +# $(CC) -o $@ $(CFLAGS) $(LDFLAGS) $< -test_gf256_interpolate.o: test_gf256_interpolate.c +test_interpolate.o: test_interpolate.c -test_gf256_interpolate.out: gf256_interpolate.o gf256.o test_gf256_interpolate.o +test_interpolate.out: hazmat.o test_interpolate.o $(CC) -o $@ $(CFLAGS) $(LDFLAGS) $^ $(LOADLIBES) $(LDLIBS) $(MEMCHECK) ./$@ @@ -55,21 +52,21 @@ test_slip39_shamir.o: test_slip39_shamir.c slip39.h slip39_shamir.o: slip39_shamir.c slip39.h -test_slip39_shamir.out: test_slip39_shamir.o slip39_shamir.o gf256.o gf256_interpolate.o test_random.o +test_slip39_shamir.out: test_slip39_shamir.o slip39_shamir.o hazmat.o test_random.o gcc $^ -o $@ -l crypto ./$@ slip39_encrypt.o: slip39_encrypt.c slip39.h -test_slip39_encrypt.out: test_slip39_encrypt.o slip39_encrypt.o +test_slip39_encrypt.out: test_slip39_encrypt.o slip39_encrypt.o gcc $^ -o $@ -l crypto ./$@ test_generate_combine.o: test_generate_combine.c -test_generate_combine.out: test_generate_combine.o gf256.o gf256_interpolate.o slip39_wordlist.o \ +test_generate_combine.out: test_generate_combine.o hazmat.o slip39_wordlist.o \ slip39_rs1024.o slip39_shamir.o slip39_mnemonics.o slip39_encrypt.o ../randombytes/librandombytes.a $(CC) -o $@ $(CFLAGS) $(LDFLAGS) $^ $(LOADLIBES) $(LDLIBS) -l crypto $(MEMCHECK) ./$@ @@ -77,7 +74,7 @@ test_generate_combine.out: test_generate_combine.o gf256.o gf256_interpolate.o s slip39: slip39_cli.c libslip39.a ../randombytes/librandombytes.a $(CC) -o $@ $(CFLAGS) $(LDFLAGS) $^ $(LOADLIBES) $(LDLIBS) -l crypto -check: test_gf256.out test_gf256_interpolate.out test_slip39_wordlist.out \ +check: test_interpolate.out test_slip39_wordlist.out \ test_slip39_shamir.out test_slip39_encrypt.out test_generate_combine.out slip39_tests.out .PHONY: check check_slip39 diff --git a/slip39/README.md b/slip39/README.md index e068f4a..ca36eae 100644 --- a/slip39/README.md +++ b/slip39/README.md @@ -8,25 +8,21 @@ standard for doing so: Along with the proposed specification, they also have provided a [python reference implementation](https://github.com/trezor/python-shamir-mnemonic) -and a set of +and a set of [sest vectors](https://github.com/trezor/python-shamir-mnemonic/blob/master/vectors.json). This branch intends to provide an implementation of the specification in C. Note that SLIP39 differs from standard implementations of Shamir Sharing in a couple of ways - it adds some -digest checking that allows you to give you some assurance that the result is correct at +digest checking that allows you to give you some assurance that the result is correct at the expense of a few bits of security, it has a two-level grouping scheme, etc. At its heart, it ends up making more use of polynomical interpolation than other implementations do. The file vectors_to_tests.js contains some javascript that uses the published test vectors to produce C code that can be used to verify that the code implements the spec. -gf256.c provides a naive, table-lookup implementation of gf256 operations. - -gf256_interpolate.c provides an implementation for interpolating a polynomial going -through n arbitray points at an arbitrary x-coordinate. The y-coordinates are represented -as arrays of gf256 values, and while this implementation does calculations byte-by-byte, -it is probably feasible to adapt this implementation to using the bit-slicing approach -used in hazmat.c to give an implementation with fewer side-channel attacks. +hazmat.c provides a side-channel attack resistant implementation of gf256 operations +32 elements at a time, with a couple of additional functions dealing with lagrange +polynomials and polynomial interpolation. test_random.c implements some code to act as filler for random number generation when testing. It is clearly not designed to be used in any real life application. @@ -41,12 +37,12 @@ imbedding a digest into the shares which requires a sha256. Again this implement on openssl for sha256. slip39_wordlists.c implements functions for converting byte buffers to wordlists and -and the encoding and decoding of slip39 words into 10-bit integers. the toWords and +and the encoding and decoding of slip39 words into 10-bit integers. the toWords and fromWords functions do/check the appropriate left padding of bits described in slip39. slip39_wordlist_english.h contains the actual word list used. There are various and sundry test files that test key parts of the implementation. You can -build and run them all by building the make target 'check'. +build and run them all by building the make target 'check'. There is also a quick and dirty command line. You can build it with the make target 'slip39'. Here is a sample of running it to generate a share set and then combine some of those shares diff --git a/slip39/gf256.c b/slip39/gf256.c deleted file mode 100644 index 4421958..0000000 --- a/slip39/gf256.c +++ /dev/null @@ -1,85 +0,0 @@ -#include "gf256.h" - -////////////////////////////////////////////////// -// gf256 operations -// -// Addition and subtraction oar the same thing. -// GF256 | Unsigned Char Representation -// ----------+-------------------------------- -// a + b | a ^ b -// a - b | a ^ b -// -// for a,b !=0 -// a * b | exp[ (log[a] + log[b]) % 256 ] -// a / b | exp[ (log[a] - log[b]) % 256 ] - -static uint8_t initialized = 0; -static uint8_t gf256_exp_table[255]; -static uint8_t gf256_log_table[256]; -void precompute_gf256_exp_log_tables(void); - -void precompute_gf256_exp_log_tables(void) { - int i; - unsigned int poly = 1; - - gf256_log_table[0] = 0; // should never look this up! - - for(i=0;i<255;++i) { - gf256_exp_table[i] = poly; - gf256_log_table[poly] = i; - - poly = (poly<<1) ^ poly; - if(poly & 0x100) { - poly = poly ^ 0x11B; - } - } - initialized = 1; -} - -uint8_t gf256_log(uint8_t a) { - if(!initialized) { - precompute_gf256_exp_log_tables(); - } - return gf256_log_table[a]; -} - -uint8_t gf256_exp(uint8_t a) { - if(!initialized) { - precompute_gf256_exp_log_tables(); - } - return gf256_exp_table[a]; -} - -uint8_t gf256_add(uint8_t a, uint8_t b) { - return a ^ b; -} - -uint8_t gf256_mult(uint8_t a, uint8_t b) { - if(!initialized) { - precompute_gf256_exp_log_tables(); - } - - if(a==0 || b==0) { - return 0; - } - - int la = gf256_log(a); - int lb = gf256_log(b); - int e = (la + lb) % 255; - return gf256_exp(e); -} - -uint8_t gf256_div(uint8_t a, uint8_t b) { - if(!initialized) { - precompute_gf256_exp_log_tables(); - } - - if(a==0) { - return 0; - } - - int la = gf256_log(a); - int lb = gf256_log(b); - int e = (255 + la - lb) % 255; - return gf256_exp(e); -} diff --git a/slip39/gf256.h b/slip39/gf256.h deleted file mode 100644 index dbad8e1..0000000 --- a/slip39/gf256.h +++ /dev/null @@ -1,12 +0,0 @@ -#ifndef GF256_H -#define GF256_H - -#include - -uint8_t gf256_log(uint8_t a); -uint8_t gf256_exp(uint8_t a); -uint8_t gf256_add(uint8_t a, uint8_t b); -uint8_t gf256_mult(uint8_t a, uint8_t b); -uint8_t gf256_div(uint8_t a, uint8_t b); - -#endif \ No newline at end of file diff --git a/slip39/gf256_interpolate.c b/slip39/gf256_interpolate.c deleted file mode 100644 index f3ff360..0000000 --- a/slip39/gf256_interpolate.c +++ /dev/null @@ -1,85 +0,0 @@ -#include "gf256_interpolate.h" - -// Lagrange Polynomials: -// n -// --- -// l_n_m(x,[x]) = | | ( x - x_i ) / ( x_m - x_i ) -// i != m -// -// note that x_i != x_j for i != j - -int16_t lagrange( - uint8_t n, // number of points to interpolate - uint8_t m, // index of this point - const uint8_t *xi, // x coordinates of all points (array of size n) - uint8_t x // x coordinate to evaluate -) { - uint8_t result = 1; - uint8_t i; - - for(i=0;i pn_i(x,[x]) * y_i -// --- -// i -// -// On success, this returns the length of the recovered secret. On failure, -// it returns -1 - -int16_t interpolate( - uint8_t n, // number of points to interpolate - const uint8_t* xi, // x coordinates for points (array of length n) - uint32_t yl, // length of y coordinate array - const uint8_t **yij, // n arrays of yl bytes representing y values - uint8_t x, // x coordinate to interpolate - uint8_t* result // space for yl bytes of results -) { - uint8_t lags[n]; - uint8_t i; - uint32_t j; - - // Calculate lagrange polynomials for the target value. - // the lagrange polynomial function will return -1 if there is a - // duplicate in one of the x-coordinates. - for(i=0; i + * + * This code contains the actual Shamir secret sharing functionality. The + * implementation of this code is based on the idea that the user likes to + * generate/combine 32 shares (in GF(2^8) at the same time, because a 256 bit + * key will be exactly 32 bytes. Therefore we bitslice all the input and + * unbitslice the output right before returning. + * + * This bitslice approach optimizes natively on all architectures that are 32 + * bit or more. Care is taken to use not too many registers, to ensure that no + * values have to be leaked to the stack. + * + * All functions in this module are implemented constant time and constant + * lookup operations, as all proper crypto code must be. + */ + + +#include "hazmat.h" +#include +#include + + +typedef struct { + uint8_t x; + uint8_t y; +} ByteShare; + + +static void +bitslice(uint32_t r[8], const uint8_t x[32]) +{ + size_t bit_idx, arr_idx; + uint32_t cur; + + memset(r, 0, sizeof(uint32_t[8])); + for (arr_idx = 0; arr_idx < 32; arr_idx++) { + cur = (uint32_t) x[arr_idx]; + for (bit_idx = 0; bit_idx < 8; bit_idx++) { + r[bit_idx] |= ((cur & (1 << bit_idx)) >> bit_idx) << arr_idx; + } + } +} + + +static void +unbitslice(uint8_t r[32], const uint32_t x[8]) +{ + size_t bit_idx, arr_idx; + uint32_t cur; + + memset(r, 0, sizeof(uint8_t[32])); + for (bit_idx = 0; bit_idx < 8; bit_idx++) { + cur = (uint32_t) x[bit_idx]; + for (arr_idx = 0; arr_idx < 32; arr_idx++) { + r[arr_idx] |= ((cur & (1 << arr_idx)) >> arr_idx) << bit_idx; + } + } +} + + +static void +bitslice_setall(uint32_t r[8], const uint8_t x) +{ + size_t idx; + for (idx = 0; idx < 8; idx++) { + r[idx] = ((int32_t) ((x & (1 << idx)) << (31 - idx))) >> 31; + } +} + + +/* + * Add (XOR) `r` with `x` and store the result in `r`. + */ +static void +gf256_add(uint32_t r[8], const uint32_t x[8]) +{ + size_t idx; + for (idx = 0; idx < 8; idx++) r[idx] ^= x[idx]; +} + + +/* + * Safely multiply two bitsliced polynomials in GF(2^8) reduced by + * x^8 + x^4 + x^3 + x + 1. `r` and `a` may overlap, but overlapping of `r` + * and `b` will produce an incorrect result! If you need to square a polynomial + * use `gf256_square` instead. + */ +static void +gf256_mul(uint32_t r[8], const uint32_t a[8], const uint32_t b[8]) +{ + /* This function implements Russian Peasant multiplication on two + * bitsliced polynomials. + * + * I personally think that these kinds of long lists of operations + * are often a bit ugly. A double for loop would be nicer and would + * take up a lot less lines of code. + * However, some compilers seem to fail in optimizing these kinds of + * loops. So we will just have to do this by hand. + */ + uint32_t a2[8]; + memcpy(a2, a, sizeof(uint32_t[8])); + + r[0] = a2[0] & b[0]; /* add (assignment, because r is 0) */ + r[1] = a2[1] & b[0]; + r[2] = a2[2] & b[0]; + r[3] = a2[3] & b[0]; + r[4] = a2[4] & b[0]; + r[5] = a2[5] & b[0]; + r[6] = a2[6] & b[0]; + r[7] = a2[7] & b[0]; + a2[0] ^= a2[7]; /* reduce */ + a2[2] ^= a2[7]; + a2[3] ^= a2[7]; + + r[0] ^= a2[7] & b[1]; /* add */ + r[1] ^= a2[0] & b[1]; + r[2] ^= a2[1] & b[1]; + r[3] ^= a2[2] & b[1]; + r[4] ^= a2[3] & b[1]; + r[5] ^= a2[4] & b[1]; + r[6] ^= a2[5] & b[1]; + r[7] ^= a2[6] & b[1]; + a2[7] ^= a2[6]; /* reduce */ + a2[1] ^= a2[6]; + a2[2] ^= a2[6]; + + r[0] ^= a2[6] & b[2]; /* add */ + r[1] ^= a2[7] & b[2]; + r[2] ^= a2[0] & b[2]; + r[3] ^= a2[1] & b[2]; + r[4] ^= a2[2] & b[2]; + r[5] ^= a2[3] & b[2]; + r[6] ^= a2[4] & b[2]; + r[7] ^= a2[5] & b[2]; + a2[6] ^= a2[5]; /* reduce */ + a2[0] ^= a2[5]; + a2[1] ^= a2[5]; + + r[0] ^= a2[5] & b[3]; /* add */ + r[1] ^= a2[6] & b[3]; + r[2] ^= a2[7] & b[3]; + r[3] ^= a2[0] & b[3]; + r[4] ^= a2[1] & b[3]; + r[5] ^= a2[2] & b[3]; + r[6] ^= a2[3] & b[3]; + r[7] ^= a2[4] & b[3]; + a2[5] ^= a2[4]; /* reduce */ + a2[7] ^= a2[4]; + a2[0] ^= a2[4]; + + r[0] ^= a2[4] & b[4]; /* add */ + r[1] ^= a2[5] & b[4]; + r[2] ^= a2[6] & b[4]; + r[3] ^= a2[7] & b[4]; + r[4] ^= a2[0] & b[4]; + r[5] ^= a2[1] & b[4]; + r[6] ^= a2[2] & b[4]; + r[7] ^= a2[3] & b[4]; + a2[4] ^= a2[3]; /* reduce */ + a2[6] ^= a2[3]; + a2[7] ^= a2[3]; + + r[0] ^= a2[3] & b[5]; /* add */ + r[1] ^= a2[4] & b[5]; + r[2] ^= a2[5] & b[5]; + r[3] ^= a2[6] & b[5]; + r[4] ^= a2[7] & b[5]; + r[5] ^= a2[0] & b[5]; + r[6] ^= a2[1] & b[5]; + r[7] ^= a2[2] & b[5]; + a2[3] ^= a2[2]; /* reduce */ + a2[5] ^= a2[2]; + a2[6] ^= a2[2]; + + r[0] ^= a2[2] & b[6]; /* add */ + r[1] ^= a2[3] & b[6]; + r[2] ^= a2[4] & b[6]; + r[3] ^= a2[5] & b[6]; + r[4] ^= a2[6] & b[6]; + r[5] ^= a2[7] & b[6]; + r[6] ^= a2[0] & b[6]; + r[7] ^= a2[1] & b[6]; + a2[2] ^= a2[1]; /* reduce */ + a2[4] ^= a2[1]; + a2[5] ^= a2[1]; + + r[0] ^= a2[1] & b[7]; /* add */ + r[1] ^= a2[2] & b[7]; + r[2] ^= a2[3] & b[7]; + r[3] ^= a2[4] & b[7]; + r[4] ^= a2[5] & b[7]; + r[5] ^= a2[6] & b[7]; + r[6] ^= a2[7] & b[7]; + r[7] ^= a2[0] & b[7]; +} + + +/* + * Square `x` in GF(2^8) and write the result to `r`. `r` and `x` may overlap. + */ +static void +gf256_square(uint32_t r[8], const uint32_t x[8]) +{ + uint32_t r8, r10, r12, r14; + /* Use the Freshman's Dream rule to square the polynomial + * Assignments are done from 7 downto 0, because this allows the user + * to execute this function in-place (e.g. `gf256_square(r, r);`). + */ + r14 = x[7]; + r12 = x[6]; + r10 = x[5]; + r8 = x[4]; + r[6] = x[3]; + r[4] = x[2]; + r[2] = x[1]; + r[0] = x[0]; + + /* Reduce with x^8 + x^4 + x^3 + x + 1 until order is less than 8 */ + r[7] = r14; /* r[7] was 0 */ + r[6] ^= r14; + r10 ^= r14; + /* Skip, because r13 is always 0 */ + r[4] ^= r12; + r[5] = r12; /* r[5] was 0 */ + r[7] ^= r12; + r8 ^= r12; + /* Skip, because r11 is always 0 */ + r[2] ^= r10; + r[3] = r10; /* r[3] was 0 */ + r[5] ^= r10; + r[6] ^= r10; + r[1] = r14; /* r[1] was 0 */ + r[2] ^= r14; /* Substitute r9 by r14 because they will always be equal*/ + r[4] ^= r14; + r[5] ^= r14; + r[0] ^= r8; + r[1] ^= r8; + r[3] ^= r8; + r[4] ^= r8; +} + + +/* + * Invert `x` in GF(2^8) and write the result to `r` + */ +static void +gf256_inv(uint32_t r[8], uint32_t x[8]) +{ + uint32_t y[8], z[8]; + + gf256_square(y, x); // y = x^2 + gf256_square(y, y); // y = x^4 + gf256_square(r, y); // r = x^8 + gf256_mul(z, r, x); // z = x^9 + gf256_square(r, r); // r = x^16 + gf256_mul(r, r, z); // r = x^25 + gf256_square(r, r); // r = x^50 + gf256_square(z, r); // z = x^100 + gf256_square(z, z); // z = x^200 + gf256_mul(r, r, z); // r = x^250 + gf256_mul(r, r, y); // r = x^254 +} + + +void +hazmat_lagrange_basis(uint8_t *values, + uint8_t n, + const uint8_t *xc, + uint8_t x) +{ + // call the contents of xc [ x0 x1 x2 ... xn-1 ] + uint8_t xx[32 + 16]; + uint8_t i; + + uint32_t x_slice[8], lxi[n][8]; + + uint32_t numerator[8], denominator[8], temp[8]; + + memset(xx, 0, sizeof(xx)); + for(i=0;i 32) { + return ERROR_SECRET_TOO_LONG; + } + + // The hazmat gf256 implementation needs the y-coordinate data + // to be in 32-byte blocks + uint8_t *y[n]; + uint8_t yv[SECRET_SIZE*n]; + uint8_t values[SECRET_SIZE]; + + memset(yv,0,SECRET_SIZE*n); + for(uint8_t i=0; i + * + * Usage of this API is hazardous and is only reserved for beings with a + * good understanding of the Shamir secret sharing scheme and who know how + * crypto code is implemented. If you are unsure about this, use the + * intermediate level API. You have been warned! + */ + + +#ifndef HAZMAT_H +#define HAZMAT_H + +#include +#include "slip39.h" + +void +hazmat_lagrange_basis(uint8_t *values, + uint8_t n, + const uint8_t *xc, + uint8_t x); + + +int16_t +interpolate( + uint8_t n, // number of points to interpolate + const uint8_t* xi, // x coordinates for points (array of length n) + uint32_t yl, // length of y coordinate array + const uint8_t **yij, // n arrays of yl bytes representing y values + uint8_t x, // x coordinate to interpolate + uint8_t* result // space for yl bytes of results +); + +//void +//sss_interpolate(uint8_t *values, +// uint8_t n, +// const uint8_t *xc, +// const uint8_t **y, // n sets of 32 +// uint8_t x); + + +#endif /* HAZMAT_H */ diff --git a/slip39/slip39.h b/slip39/slip39.h index 5c73c14..ca2f588 100644 --- a/slip39/slip39.h +++ b/slip39/slip39.h @@ -5,7 +5,7 @@ #define RADIX (1< #include @@ -59,14 +62,14 @@ typedef struct group_descriptor_struct { typedef struct slip39_share_struct { uint16_t identifier; - uint8_t iteration_exponent; + uint8_t iteration_exponent; uint8_t group_index; - uint8_t group_threshold; + uint8_t group_threshold; uint8_t group_count; uint8_t member_index; uint8_t member_threshold; uint8_t *value; - uint32_t value_length; + uint32_t value_length; } slip39_share; typedef struct group_struct { @@ -77,8 +80,6 @@ typedef struct group_struct { const uint8_t *value[16]; } slip39_group; -void setup(void); - uint32_t rs1024_polymod( const uint16_t *values, // values - 10 bit words uint32_t values_length // number of entries in the values array @@ -86,7 +87,7 @@ uint32_t rs1024_polymod( void rs1024_create_checksum( uint16_t *values, // data words (10 bit) - uint32_t n // length of the data array, including three checksum word + uint32_t n // length of the data array, including three checksum word ); uint8_t rs1024_verify_checksum( @@ -103,7 +104,7 @@ const char *slip39_word(int16_t word); // converts a string of whitespace delimited mnemonic words // to an array of 10-bit integers. Returns the number of integers -// written to the buffer. +// written to the buffer. uint32_t parse_words( const char *words_string, uint16_t *words, @@ -129,28 +130,33 @@ int32_t fromWords( ); +// fills the destination buffer with count random bytes +void randombytes(uint8_t *dest, uint32_t count); -void randombytes(uint8_t *, uint32_t); - +// creates an hmac from the data, storing it in the result field +// the nnumber of pytes written is stored in the resultlen pointer +// returns a pointer to the result buffer uint8_t * hmac_sha256( - const uint8_t *key, + const uint8_t *key, uint32_t keylen, - const uint8_t *data, + const uint8_t *data, uint32_t datalen, - uint8_t *result, + uint8_t *result, unsigned int *resultlen); - +// TODO: explain uint8_t* create_digest( - const uint8_t *random_data, - uint32_t rdlen, - const uint8_t *shared_secret, + const uint8_t *random_data, + uint32_t rdlen, + const uint8_t *shared_secret, uint32_t sslen, uint8_t *result ); ////////////////////////////////////////////////// -// shamir sharing +// slip39 shamir sharing + +// TODO: explain int32_t split_secret( uint8_t threshold, uint8_t share_count, @@ -159,6 +165,7 @@ int32_t split_secret( uint8_t *result ); +// TODO: explain // returns the number of bytes written to the secret array, or -1 if there was an error int32_t recover_secret( uint8_t threshold, @@ -168,34 +175,36 @@ int32_t recover_secret( uint8_t *secret ); - +// TODO: explain void round_function( - uint8_t i, - const char *passphrase, - uint8_t exp, + uint8_t i, + const char *passphrase, + uint8_t exp, const uint8_t *salt, - uint32_t salt_length, + uint32_t salt_length, const uint8_t *r, uint32_t r_length, uint8_t *dest, uint32_t dest_length ); +// TODO: explain void slip39_encrypt( const uint8_t *input, uint32_t input_length, const char *passphrase, uint8_t iteration_exponent, - uint16_t identifier, + uint16_t identifier, uint8_t *output ); +// TODO: EXPLAIN void slip39_decrypt( const uint8_t *input, uint32_t input_length, const char *passphrase, uint8_t iteration_exponent, - uint16_t identifier, + uint16_t identifier, uint8_t *output ); @@ -216,12 +225,12 @@ unsigned int decode_mnemonic( ); void print_hex( - const uint8_t *buffer, + const uint8_t *buffer, uint32_t length ); void print_mnemonic( - const uint16_t *mnemonic, + const uint16_t *mnemonic, unsigned int mnemonic_length ); @@ -231,7 +240,7 @@ void print_group(slip39_group *g, unsigned int secret_length); // generate mnemonics int generate_mnemonics( uint8_t group_threshold, - const group_descriptor *groups, + const group_descriptor *groups, uint8_t groups_length, const uint8_t *master_secret, uint32_t master_secret_length, diff --git a/slip39/slip39_shamir.c b/slip39/slip39_shamir.c index 5c17436..37a3149 100644 --- a/slip39/slip39_shamir.c +++ b/slip39/slip39_shamir.c @@ -1,5 +1,5 @@ #include "slip39.h" -#include "gf256_interpolate.h" +#include "hazmat.h" ////////////////////////////////////////////////// // hmac sha256 @@ -124,8 +124,8 @@ int32_t recover_secret( ) { memset(secret, 0, sizeof(digest)); memset(digest, 0, sizeof(digest)); - memset(verify, 0, sizeof(verify)); - + memset(verify, 0, sizeof(verify)); + return ERROR_INTERPOLATION_FAILURE; } @@ -137,11 +137,11 @@ int32_t recover_secret( memset(digest, 0, sizeof(digest)); - memset(verify, 0, sizeof(verify)); + memset(verify, 0, sizeof(verify)); if(!valid) { return ERROR_CHECKSUM_FAILURE; } - + return share_length; } diff --git a/slip39/test_gf256_interpolate.c b/slip39/test_interpolate.c similarity index 72% rename from slip39/test_gf256_interpolate.c rename to slip39/test_interpolate.c index 39bf29f..d8dbee9 100644 --- a/slip39/test_gf256_interpolate.c +++ b/slip39/test_interpolate.c @@ -1,20 +1,35 @@ -#include "gf256_interpolate.h" +#include "hazmat.h" #include uint8_t test_lagrange_orthogonality(void); uint8_t test_lagrange_zero_one_uniqueness(void); uint8_t test_interpolation(void); +uint8_t test_simple_interpolation(void); + +int16_t lagrange( + uint8_t n, // number of points to interpolate + uint8_t m, // index of this point + const uint8_t *xi, // x coordinates of all points (array of size n) + uint8_t x // x coordinate to evaluate +) { + uint8_t values[n]; + + hazmat_lagrange_basis(values, n, xi, x); + + return values[m]; +} + uint8_t test_lagrange_orthogonality(void) { uint8_t fail = 0; uint8_t xi[3]; - for(int16_t i=0; i<256; ++i) { + for(int16_t i=0; i<16; ++i) { xi[0] = i; - for(int16_t j=i+1; j<256; ++j) { + for(int16_t j=128; j<144; ++j) { xi[1] = j; - for(int16_t k=j+1; k<256; ++k) { + for(int16_t k=250; k<256; ++k) { xi[2] = k; // Test the essential orthogonality property of the @@ -46,9 +61,9 @@ uint8_t test_lagrange_zero_one_uniqueness(void) { uint8_t fail = 0; uint8_t xi[2]; - for(int16_t i=0; i<256; ++i) { + for(int16_t i=0; i<16; ++i) { xi[0] = i; - for(int16_t j=i+1; j<256; ++j) { + for(int16_t j=16; j<32; ++j) { xi[1] = j; // For two parameter lagrange polynomials, test to see that // zero one results do not result unless x in [xi] @@ -69,6 +84,30 @@ uint8_t test_lagrange_zero_one_uniqueness(void) { return fail; } + +uint8_t test_simple_interpolation(void) { + uint8_t fail = 0; + uint8_t x[] = { 1, 10 }; + uint8_t y0[] = { 1 }; + uint8_t y1[] = { 10 }; + const uint8_t *y[] = {y0, y1}; + + uint8_t yr[256]; + + // Interpolate the entire range of the polynomial + for(uint8_t i=0; i<255; ++i) { + interpolate(2,x,1,y,i,yr+i); + } + + for(uint8_t i=0; i<255; ++i) { + if(yr[i] != i) { + printf("simple interpolation failure %d %d\n", i, yr[i]); + fail = 1; + } + } + return fail; +} + uint8_t test_interpolation(void) { uint8_t fail = 0; uint8_t x[] = { 0, 1 }; @@ -106,10 +145,12 @@ uint8_t test_interpolation(void) { return fail; } + int main(void) { uint8_t fail = 0; uint8_t t; + t = test_lagrange_orthogonality(); fail = fail || t; printf("test lagrage orthogonality: %s\n", t ? "fail" : "pass" ); @@ -118,9 +159,13 @@ int main(void) { fail = fail || t; printf("test lagrage zeros and ones: %s\n", t ? "fail" : "pass" ); + t = test_simple_interpolation(); + fail = fail || t; + printf("test simple interpolation: %s\n", t ? "fail" : "pass" ); +/* t = test_interpolation(); fail = fail || t; printf("test interpolation: %s\n", t ? "fail" : "pass" ); - +*/ return fail; } diff --git a/slip39/test_slip39_shamir.c b/slip39/test_slip39_shamir.c index 7bf75d6..2f59685 100644 --- a/slip39/test_slip39_shamir.c +++ b/slip39/test_slip39_shamir.c @@ -3,7 +3,8 @@ uint8_t test_split_recover(void); uint8_t test_split_recover(void) { uint8_t fail = 0; - char *test = "The quick brown fox jumped over the lazy dog."; + // secret can only be 32 bytes long... + char *test = "The quick brown fox jumped ove"; //r the lazy dog."; uint8_t shares[512]; uint8_t secret[50]; diff --git a/wordlist.c b/wordlist.c deleted file mode 100644 index 79599ea..0000000 --- a/wordlist.c +++ /dev/null @@ -1,588 +0,0 @@ - - -#include -#include -#include - - -#define RADIX_BITS 10 -#define RADIX (1<group_threshold -1) & 15; - uint16_t gc = (mnemonic->group_count -1) & 15; - uint16_t mi = (mnemonic->member_index) & 15; - uint16_t mt = (mnemonic->member_threshold -1) & 15; - - destination[0] = (mnemonic->identifier >> 5) & 1023; - destination[1] = ((mnemonic->identifier << 5) | mnemonic->iteration_exponent) & 1023; - destination[2] = ((mnemonic->group_index << 6) | (gt << 2) | (gc >> 2)) & 1023; - destination[3] = ((gc << 8) | (mi << 4) | (mt)) & 1023; - - uint32_t words = toWords(mnemonic->value, mnemonic->value_length, destination+4, destination_length - METADATA_LENGTH_WORDS); - rs1024_create_checksum(destination, words + METADATA_LENGTH_WORDS); - - return words+METADATA_LENGTH_WORDS; - } - - - - - -////////////////////////////////////////////////// -// decode mnemonic -unsigned int decode_mnemonic( - const uint16_t *mnemonic, - uint32_t mnemonic_length, - slip39_mnemonic *destination -) { - if(mnemonic_length < MIN_MNEMONIC_LENGTH_WORDS) { - printf("Invalid mnemonic- not enough mnemonic words.\n"); - return -1; - } - - if( !rs1024_verify_checksum(mnemonic, mnemonic_length) ) { - printf("Invalid mnemonic - checksum does not verify\n"); - return -1; - } - - unsigned char gt = ((mnemonic[2] >> 2) & 15) +1; - unsigned char gc = (((mnemonic[2]&3) << 2) | ((mnemonic[3]>>8)&3)) +1; - - if(gt > gc) { - printf("Invalid mnemonic - group threshold cannot be larger than group count.\n"); - } - - destination->identifier = mnemonic[0] << 5 | mnemonic[1] >> 5; - destination->iteration_exponent = mnemonic[1] & 31; - destination->group_index = mnemonic[2] >> 6; - destination->group_threshold = gt; - destination->group_count = gc; - destination->member_index = (mnemonic[3]>>4) & 15; - destination->member_threshold = (mnemonic[3]&15) + 1; - return fromWords(mnemonic+4, mnemonic_length - 7, destination->value, destination->value_length); -} - -void print_hex(unsigned char *buffer, unsigned int length) { - printf("0x"); - for(unsigned int i=0;i 0 && i%32== 0) { - printf("\n "); - } - printf("%02x", buffer[i]); - } - printf("\n"); -} - - -void print_mnemonic(uint16_t *mnemonic, unsigned int mnemonic_length) { - unsigned char value[256]; - - uint16_t id; - unsigned char exp, gi, gt, gc, mi, mt; - - unsigned int secret_length = decode_mnemonic(mnemonic, mnemonic_length, - &id, &exp, &gi, >, &gc, &mi, &mt, value, 256); - - for(unsigned int i=0;i< mnemonic_length; ++i) { - printf("%s ", wordlist[ mnemonic[i]]); - } - printf("\n"); - printf("identifier: %d exponent: %d\n", id, exp); - printf("group index: %d threshold: %d count: %d\n", gi, gt, gc); - printf("member index: %d threshold: %d\n", mi, mt); - print_hex(value, secret_length); -} - -////////////////////////////////////////////////// -// generate mnemonics -// -int generate_mnemonics( - unsigned char group_threshold, - group_descriptor *groups, - unsigned char groups_length, - unsigned char *master_secret, - unsigned int master_secret_length, - char *passphrase, - unsigned int iteration_exponent, - unsigned int *mnemonic_length, - uint16_t *mnemonics, - unsigned int buffer_size -) { - uint16_t identifier = 0; - randombytes((unsigned char *)(&identifier), 2); - identifier = identifier & ((1<<15)-1); - if(master_secret_length < MIN_STRENGTH_BYTES) { - printf("The length of the master secret (%d bytes) must be at least %d bytes.\n", master_secret_length, MIN_STRENGTH_BYTES); - return -1; - } - - unsigned int share_length = METADATA_LENGTH_WORDS + bytes_to_words(master_secret_length); - unsigned int total_shares = 0; - - for(unsigned char i=0; i groups[i].count ) { - printf("The member theshold cannot exceed the member count.\n"); - return -1; - } - if( groups[i].threshold == 1 && groups[i].count > 1) { - printf("Creating multiple member shares with member thrshold 1 is not allowed.\n"); - return -1; - } - } - - if(buffer_size < share_length * total_shares) { - printf("Results buffer not large enough for that many shares.\n"); - return -1; - } - - if(master_secret_length % 2 == 1) { - printf("The length of the master secret in bytes must be an even number.\n"); - return -1; - } - - for(unsigned char *p = (unsigned char *) passphrase; *p; p++) { - if( (*p < 32) || (126 < *p) ) { - printf("The passphrase must contain only printable ASCII characters: %s %d\n",passphrase, *p); - return -1; - } - } - - if(group_threshold > groups_length) { - printf("The group threshold cannot exceed the number of groups.\n"); - return -1; - } - - unsigned char encrypted_master_secret[MAX_SECRET_LENGTH]; - - encrypt(master_secret,master_secret_length,passphrase,iteration_exponent,identifier, encrypted_master_secret); - - unsigned char group_shares[MAX_SECRET_LENGTH * MAX_SHARE_COUNT]; - - split_secret(group_threshold, groups_length, encrypted_master_secret, master_secret_length, group_shares); - - unsigned char *group_share = group_shares; - - uint16_t *mnemonic = mnemonics; - unsigned int remaining_buffer = buffer_size; - - unsigned int word_count = 0; - unsigned int share_count = 0; - - for(unsigned char i=0; igroup_index, g->member_threshold, g->count ); - for(unsigned char i=0; icount; ++i) { - printf("%d: ", g->member_index[i]); - print_hex(g->value[i], secret_length); - } -} - - -///////////////////////////////////////////////// -// combine_mnemonics -int combine_mnemonics( - uint16_t **mnemonics, // array of pointers to 10-bit words - unsigned int mnemonics_words, // number of words in each share - unsigned int mnemonics_shares,// total number of shares - char *passphrase, // passphrase to unlock master secret - unsigned char *buffer, // working space, and place to return secret - int buffer_length // total amount of working space -) { - uint16_t identifier; - unsigned char iteration_exponent; - unsigned char group_threshold; - unsigned char group_count; - - if(mnemonics_shares == 0) { - printf("The list of mnemonics is empty.\n"); - return -1; - } - - unsigned char next_group = 0; - group_struct groups[16]; - - unsigned char *next_share = buffer; - int buffer_remaining = buffer_length; - unsigned int secret_length = 0; - - for(unsigned int i=0; i20) { - printf("invalid result from parse_words: %d\n", n); - } - - for(int i=0; i0) { - print_hex(result,m); - } -} - -void setup(void) { - precompute_gf256_exp_log_tables(); -} - -/* -int main(int argc, char *argv[]) { - - setup(); - - //test_toWords_fromWords(); - //test_gf256(); - //test_lagrange(); - //test_interpolation(); - - //test_split_recover(); - - //test_generate_combine1(); - //test_encrypt_decrypt(); - //test_valid_mnemonic(); - //test_round_function(); - //test_encrypt_function(); - test_multi(); -} -*/ \ No newline at end of file From a6208c0912cf0100372cc8158d7dc3dcde891593 Mon Sep 17 00:00:00 2001 From: Chris Howe Date: Fri, 6 Sep 2019 03:30:22 -0500 Subject: [PATCH 2/2] added some explanation of the pedigree of the hazmat.c file --- slip39/README.md | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/slip39/README.md b/slip39/README.md index ca36eae..e82dbcb 100644 --- a/slip39/README.md +++ b/slip39/README.md @@ -22,7 +22,10 @@ to produce C code that can be used to verify that the code implements the spec. hazmat.c provides a side-channel attack resistant implementation of gf256 operations 32 elements at a time, with a couple of additional functions dealing with lagrange -polynomials and polynomial interpolation. +polynomials and polynomial interpolation. Note that this file was copied from +the Daan's original implementation of the hazmat code in the outer directory, and +then his inperpolation functions were removed and new lagrange and interpolation +functions added. test_random.c implements some code to act as filler for random number generation when testing. It is clearly not designed to be used in any real life application.