├── shamir.h ├── tools ├── keymerge.c ├── keypair.c ├── keysplit.c ├── verify.c ├── sign.c ├── encrypt.c ├── decrypt.c ├── cloak.c ├── util.h ├── reveal.c └── README ├── x25519.h ├── COPYING ├── test ├── shamir-speed.c ├── gimli-speed.c ├── duplex-speed.c ├── shamir-sanity.c ├── x25519-speed.c ├── x25519-sanity.c ├── gimli-sanity.c ├── duplex-sanity.c └── shamir-known.c ├── swirl.h ├── Makefile ├── shamir.c ├── duplex.h ├── x25519.c └── README /shamir.h: -------------------------------------------------------------------------------- 1 | /* shamir.h from Pocketcrypt: https://github.com/arachsys/pocketcrypt */ 2 | 3 | #ifndef SHAMIR_H 4 | #define SHAMIR_H 5 | 6 | #include 7 | 8 | enum { secret_size = 32, share_size = 33 }; 9 | typedef uint8_t secret_t[secret_size]; 10 | typedef uint8_t share_t[share_size]; 11 | 12 | void shamir_combine(secret_t secret, uint8_t count, 13 | const share_t shares[count]); 14 | 15 | void shamir_split(share_t share, uint8_t index, uint8_t threshold, 16 | const secret_t secret, const secret_t entropy[threshold - 1]); 17 | 18 | #endif 19 | -------------------------------------------------------------------------------- /tools/keymerge.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | #include "shamir.h" 5 | #include "util.h" 6 | 7 | int main(int argc, char **argv) { 8 | if (argc >= 3) { 9 | secret_t secret; 10 | share_t shares[argc - 2]; 11 | 12 | for (int i = 0; i < argc - 2; i++) 13 | load(argv[i + 2], shares[i], share_size); 14 | shamir_combine(secret, argc - 2, shares); 15 | 16 | save(argv[1], secret, secret_size); 17 | return EXIT_SUCCESS; 18 | } 19 | 20 | fprintf(stderr, "Usage: %s SECRET SHARE...\n", argv[0]); 21 | return 64; 22 | } 23 | -------------------------------------------------------------------------------- /tools/keypair.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | 6 | #include "util.h" 7 | #include "x25519.h" 8 | 9 | int main(int argc, char **argv) { 10 | x25519_t point, scalar; 11 | 12 | if (argc != 3) { 13 | fprintf(stderr, "Usage: %s SK PK\n", argv[0]); 14 | return 64; 15 | } 16 | 17 | randomise(scalar, x25519_size); 18 | scalar[0] &= 0xf8; 19 | scalar[x25519_size - 1] &= 0x7f; 20 | scalar[x25519_size - 1] |= 0x40; 21 | x25519(point, scalar, x25519_base); 22 | 23 | save(argv[1], scalar, x25519_size); 24 | save(argv[2], point, x25519_size); 25 | return EXIT_SUCCESS; 26 | } 27 | -------------------------------------------------------------------------------- /tools/keysplit.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | #include "shamir.h" 5 | #include "util.h" 6 | 7 | int main(int argc, char **argv) { 8 | size_t threshold = argv[1] ? strtoul(argv[1], NULL, 10) : 0; 9 | 10 | if (threshold && threshold < 256 && threshold + 2 < argc) { 11 | secret_t entropy[threshold - 1], secret; 12 | share_t share; 13 | 14 | load(argv[2], secret, secret_size); 15 | for (int i = 0; i < threshold - 1; i++) 16 | randomise(entropy[i], secret_size); 17 | 18 | for (int i = 0; i < argc - 3; i++) { 19 | shamir_split(share, i, threshold, secret, entropy); 20 | save(argv[i + 3], share, share_size); 21 | } 22 | return EXIT_SUCCESS; 23 | } 24 | 25 | fprintf(stderr, "Usage: %s THRESHOLD SECRET SHARE...\n", argv[0]); 26 | return 64; 27 | } 28 | -------------------------------------------------------------------------------- /x25519.h: -------------------------------------------------------------------------------- 1 | /* x25519.h from Pocketcrypt: https://github.com/arachsys/pocketcrypt */ 2 | 3 | #ifndef X25519_H 4 | #define X25519_H 5 | 6 | #include 7 | 8 | enum { x25519_size = 32 }; 9 | typedef uint8_t x25519_t[x25519_size]; 10 | 11 | extern const x25519_t x25519_base; 12 | 13 | int x25519(x25519_t out, const x25519_t scalar, const x25519_t point); 14 | 15 | void x25519_invert(x25519_t out, const x25519_t scalar); 16 | 17 | void x25519_point(x25519_t out, const x25519_t element); 18 | 19 | void x25519_scalar(x25519_t out, const x25519_t scalar); 20 | 21 | void x25519_sign(x25519_t response, const x25519_t challenge, 22 | const x25519_t ephemeral, const x25519_t identity); 23 | 24 | int x25519_verify(const x25519_t response, const x25519_t challenge, 25 | const x25519_t ephemeral, const x25519_t identity); 26 | 27 | #endif 28 | -------------------------------------------------------------------------------- /tools/verify.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | 6 | #include "duplex.h" 7 | #include "util.h" 8 | #include "x25519.h" 9 | 10 | static void process(duplex_t state) { 11 | size_t chunk = 65536, length; 12 | uint8_t data[65536]; 13 | 14 | while ((length = get(in, data, chunk))) 15 | duplex_absorb(state, data, length); 16 | duplex_pad(state); 17 | } 18 | 19 | int main(int argc, char **argv) { 20 | duplex_t state = { 0 }; 21 | x25519_t challenge, identity, signature[2]; 22 | 23 | if (argc != 2 && argc != 3) { 24 | fprintf(stderr, "Usage: %s PK [SIG]\n", argv[0]); 25 | return 64; 26 | } 27 | 28 | load(argv[1], identity, x25519_size); 29 | load(argv[2], signature, 2 * x25519_size); 30 | process(state); 31 | 32 | duplex_absorb(state, identity, x25519_size); 33 | duplex_absorb(state, signature[0], x25519_size); 34 | duplex_squeeze(state, challenge, x25519_size); 35 | 36 | if (x25519_verify(signature[1], challenge, signature[0], identity)) 37 | errx(EXIT_FAILURE, "Verification failed"); 38 | return EXIT_SUCCESS; 39 | } 40 | -------------------------------------------------------------------------------- /COPYING: -------------------------------------------------------------------------------- 1 | Copyright (C) 2019-2020 Chris Webb 2 | Copyright (C) 2015-2016 Cryptography Research, Inc. 3 | 4 | Permission is hereby granted, free of charge, to any person obtaining a copy 5 | of this software and associated documentation files (the "Software"), to 6 | deal in the Software without restriction, including without limitation the 7 | rights to use, copy, modify, merge, publish, distribute, sublicense, and/or 8 | sell copies of the Software, and to permit persons to whom the Software is 9 | furnished to do so, subject to the following conditions: 10 | 11 | The above copyright notice and this permission notice shall be included in 12 | all copies or substantial portions of the Software. 13 | 14 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 17 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 18 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 19 | FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS 20 | IN THE SOFTWARE. 21 | -------------------------------------------------------------------------------- /tools/sign.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | 7 | #include "duplex.h" 8 | #include "util.h" 9 | #include "x25519.h" 10 | 11 | static void process(duplex_t state) { 12 | size_t chunk = 65536, length; 13 | uint8_t data[65536]; 14 | 15 | while ((length = get(in, data, chunk))) 16 | duplex_absorb(state, data, length); 17 | duplex_pad(state); 18 | } 19 | 20 | int main(int argc, char **argv) { 21 | duplex_t seed, state = { 0 }; 22 | x25519_t challenge, identity, point, scalar, response, secret; 23 | 24 | if (argc != 2 && argc != 3) { 25 | fprintf(stderr, "Usage: %s SK [PK]\n", argv[0]); 26 | return 64; 27 | } 28 | 29 | load(argv[1], secret, x25519_size); 30 | if (argv[2]) 31 | load(argv[2], identity, x25519_size); 32 | else 33 | x25519(identity, secret, x25519_base); 34 | 35 | process(state); 36 | duplex_absorb(state, identity, x25519_size); 37 | 38 | memcpy(seed, state, x25519_size); 39 | duplex_absorb(seed, secret, x25519_size); 40 | duplex_squeeze(seed, scalar, x25519_size); 41 | x25519(point, scalar, x25519_base); 42 | 43 | duplex_absorb(state, point, x25519_size); 44 | duplex_squeeze(state, challenge, x25519_size); 45 | x25519_sign(response, challenge, scalar, secret); 46 | 47 | put(out, point, x25519_size); 48 | put(out, response, x25519_size); 49 | return EXIT_SUCCESS; 50 | } 51 | -------------------------------------------------------------------------------- /test/shamir-speed.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | 5 | #include "shamir.h" 6 | 7 | static secret_t entropy[254], secret; 8 | static share_t shares[255]; 9 | 10 | static double combine(size_t repeat, uint8_t count) { 11 | clock_t start = clock(); 12 | for (size_t i = 0; i < repeat; i++) 13 | shamir_combine(secret, count, shares); 14 | return 1.0e6 * (clock() - start) / CLOCKS_PER_SEC / repeat; 15 | } 16 | 17 | static double split(size_t repeat, uint8_t threshold) { 18 | clock_t start = clock(); 19 | for (size_t i = 0; i < repeat; i++) 20 | for (uint8_t j = 0; j < 255; j++) 21 | shamir_split(shares[j], j, threshold, secret, entropy); 22 | return 1.0e6 * (clock() - start) / CLOCKS_PER_SEC / repeat / 255; 23 | } 24 | 25 | int main(void) { 26 | for (size_t i = 0; i < secret_size; i++) { 27 | secret[i] = (uint8_t) i; 28 | for (size_t j = 0; j < 254; j++) 29 | entropy[j][i] = (uint8_t) (i + j); 30 | } 31 | 32 | printf("Secret sharing with threshold 2 takes %0.2f us\n", split(512, 2)); 33 | printf("Secret sharing with threshold 3 takes %0.2f us\n", split(512, 3)); 34 | printf("Secret sharing with threshold 10 takes %0.2f us\n", split(256, 10)); 35 | 36 | printf("Combining 2 secret shares takes %0.2f us\n", combine(65536, 2)); 37 | printf("Combining 3 secret shares takes %0.2f us\n", combine(32768, 3)); 38 | printf("Combining 10 secret shares takes %0.2f us\n\n", combine(4096, 10)); 39 | return EXIT_SUCCESS; 40 | } 41 | -------------------------------------------------------------------------------- /tools/encrypt.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | 6 | #include "duplex.h" 7 | #include "util.h" 8 | #include "x25519.h" 9 | 10 | static void process(duplex_t state) { 11 | size_t chunk = 65536, length; 12 | uint8_t data[65536 + duplex_rate]; 13 | 14 | do { 15 | length = get(in, data, chunk); 16 | duplex_encrypt(state, data, length); 17 | duplex_pad(state); 18 | duplex_squeeze(state, data + length, duplex_rate); 19 | put(out, data, length + duplex_rate); 20 | } while (length == chunk); 21 | } 22 | 23 | int main(int argc, char **argv) { 24 | duplex_t state = { 0 }; 25 | x25519_t point, scalar; 26 | 27 | if (argc == 2) { 28 | randomise(scalar, x25519_size); 29 | x25519(point, scalar, x25519_base); 30 | put(out, point, x25519_size); 31 | load(argv[1], point, x25519_size); 32 | } else if (argc == 3) { 33 | load(argv[1], scalar, x25519_size); 34 | load(argv[2], point, x25519_size); 35 | } else { 36 | fprintf(stderr, "Usage: %s [SK] PK\n", argv[0]); 37 | return 64; 38 | } 39 | 40 | if (x25519(point, scalar, point)) 41 | errx(EXIT_FAILURE, "Invalid public identity"); 42 | duplex_absorb(state, point, x25519_size); 43 | 44 | if (argc == 3) { 45 | uint8_t nonce[duplex_rate]; 46 | randomise(nonce, duplex_rate); 47 | put(out, nonce, duplex_rate); 48 | duplex_absorb(state, nonce, duplex_rate); 49 | } 50 | 51 | process(state); 52 | return EXIT_SUCCESS; 53 | } 54 | -------------------------------------------------------------------------------- /swirl.h: -------------------------------------------------------------------------------- 1 | /* swirl.h from Pocketcrypt: https://github.com/arachsys/pocketcrypt */ 2 | 3 | #ifndef SWIRL_H 4 | #define SWIRL_H 5 | 6 | #include 7 | #include 8 | #include "duplex.h" 9 | 10 | static inline void duplex_spin(duplex_t state, size_t rounds) { 11 | for (size_t round = 0; round < rounds; round++) 12 | duplex_permute(state); 13 | duplex_counter(state) += rounds << 4; 14 | } 15 | 16 | static inline void duplex_swirl(duplex_t state, duplex_t seed, void *buffer, 17 | size_t size, size_t independent, size_t dependent) { 18 | uint32x4_t (*cells)[64] = buffer; /* 1kB pages of 64 uint32x4_t cells */ 19 | size_t pages = size >> 42 ? 1ull << 32 : size >> 10; 20 | 21 | /* Argon2B graph, data-independent rounds before data-dependent rounds */ 22 | for (size_t round = 0; round < independent + dependent; round++) { 23 | for (size_t page = 0; page < pages; page++) { 24 | uint64_t key = round < independent ? seed[0][page & 3] : state[0][0]; 25 | uint64_t offset = 2 + ((key * key >> 32) * (page - 1) >> 32); 26 | for (size_t slot = 0; slot < 64; slot++) { 27 | if (round > 0) 28 | state[0] ^= cells[page][slot]; 29 | if (page > 0) 30 | state[0] ^= cells[page - 1][slot]; 31 | if (page > 1) 32 | state[0] ^= cells[page - offset][slot]; 33 | duplex_spin(state, 1); 34 | cells[page][slot] = state[0]; 35 | } 36 | if (round < independent && (page & 3) == 3) 37 | duplex_spin(seed, 1); 38 | } 39 | if (round < independent && (pages & 3) != 0) 40 | duplex_spin(seed, 1); 41 | } 42 | } 43 | 44 | #endif 45 | -------------------------------------------------------------------------------- /test/gimli-speed.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | 5 | #define duplex_permute duplex_gimli 6 | #include "duplex.h" 7 | 8 | static duplex_t state = { 0 }; 9 | static uint8_t buffer[65536]; 10 | 11 | static double permute(size_t repeat) { 12 | clock_t start = clock(); 13 | for (size_t i = 0; i < repeat; i++) 14 | duplex_permute(state); 15 | return 1.0e9 * (clock() - start) / CLOCKS_PER_SEC / repeat; 16 | } 17 | 18 | static double speed(void (*operation)(void), size_t repeat) { 19 | clock_t start = clock(); 20 | for (size_t i = 0; i < repeat; i++) 21 | operation(); 22 | double seconds = (double) (clock() - start) / CLOCKS_PER_SEC; 23 | return (double) repeat * sizeof(buffer) / seconds / (1 << 20); 24 | } 25 | 26 | static void absorb(void) { 27 | duplex_absorb(state, buffer, sizeof(buffer)); 28 | } 29 | 30 | static void squeeze(void) { 31 | duplex_squeeze(state, buffer, sizeof(buffer)); 32 | } 33 | 34 | static void encrypt(void) { 35 | duplex_encrypt(state, buffer, sizeof(buffer)); 36 | } 37 | 38 | static void decrypt(void) { 39 | duplex_decrypt(state, buffer, sizeof(buffer)); 40 | } 41 | 42 | int main(void) { 43 | for (size_t i = 0; i < sizeof(buffer); i++) 44 | buffer[i] = (uint8_t) i; 45 | 46 | permute(1 << 20); /* warm up any dynamic CPU frequency scaling */ 47 | printf("Gimli permutes in %0.1f ns\n", permute(1 << 21)); 48 | printf("Gimli duplex absorbs at %0.1f MB/s\n", speed(absorb, 512)); 49 | printf("Gimli duplex squeezes at %0.1f MB/s\n", speed(squeeze, 512)); 50 | printf("Gimli duplex encrypts at %0.1f MB/s\n", speed(encrypt, 512)); 51 | printf("Gimli duplex decrypts at %0.1f MB/s\n\n", speed(decrypt, 512)); 52 | 53 | return EXIT_SUCCESS; 54 | } 55 | -------------------------------------------------------------------------------- /test/duplex-speed.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | 5 | #define duplex_permute duplex_xoodoo 6 | #include "duplex.h" 7 | 8 | static duplex_t state = { 0 }; 9 | static uint8_t buffer[65536]; 10 | 11 | static double permute(size_t repeat) { 12 | clock_t start = clock(); 13 | for (size_t i = 0; i < repeat; i++) 14 | duplex_permute(state); 15 | return 1.0e9 * (clock() - start) / CLOCKS_PER_SEC / repeat; 16 | } 17 | 18 | static double speed(void (*operation)(void), size_t repeat) { 19 | clock_t start = clock(); 20 | for (size_t i = 0; i < repeat; i++) 21 | operation(); 22 | double seconds = (double) (clock() - start) / CLOCKS_PER_SEC; 23 | return (double) repeat * sizeof(buffer) / seconds / (1 << 20); 24 | } 25 | 26 | static void absorb(void) { 27 | duplex_absorb(state, buffer, sizeof(buffer)); 28 | } 29 | 30 | static void squeeze(void) { 31 | duplex_squeeze(state, buffer, sizeof(buffer)); 32 | } 33 | 34 | static void encrypt(void) { 35 | duplex_encrypt(state, buffer, sizeof(buffer)); 36 | } 37 | 38 | static void decrypt(void) { 39 | duplex_decrypt(state, buffer, sizeof(buffer)); 40 | } 41 | 42 | int main(void) { 43 | for (size_t i = 0; i < sizeof(buffer); i++) 44 | buffer[i] = (uint8_t) i; 45 | 46 | permute(1 << 20); /* warm up any dynamic CPU frequency scaling */ 47 | printf("Xoodoo permutes in %0.1f ns\n", permute(1 << 21)); 48 | printf("Xoodoo duplex absorbs at %0.1f MB/s\n", speed(absorb, 512)); 49 | printf("Xoodoo duplex squeezes at %0.1f MB/s\n", speed(squeeze, 512)); 50 | printf("Xoodoo duplex encrypts at %0.1f MB/s\n", speed(encrypt, 512)); 51 | printf("Xoodoo duplex decrypts at %0.1f MB/s\n\n", speed(decrypt, 512)); 52 | 53 | return EXIT_SUCCESS; 54 | } 55 | -------------------------------------------------------------------------------- /tools/decrypt.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | 6 | #include "duplex.h" 7 | #include "util.h" 8 | #include "x25519.h" 9 | 10 | static void process(duplex_t state) { 11 | size_t chunk = 65536, length; 12 | uint8_t data[65536 + duplex_rate]; 13 | 14 | do { 15 | length = get(in, data, chunk + duplex_rate); 16 | if (length < duplex_rate) 17 | errx(EXIT_FAILURE, "Input is truncated"); 18 | length -= duplex_rate; 19 | 20 | duplex_decrypt(state, data, length); 21 | duplex_pad(state); 22 | duplex_decrypt(state, data + length, duplex_rate); 23 | if (duplex_compare(data + length, 0, duplex_rate)) 24 | errx(EXIT_FAILURE, "Authentication failed"); 25 | put(out, data, length); 26 | } while (length == chunk); 27 | } 28 | 29 | int main(int argc, char **argv) { 30 | duplex_t state = { 0 }; 31 | x25519_t point, scalar; 32 | 33 | if (argc == 2) { 34 | load(argv[1], scalar, x25519_size); 35 | if (get(in, point, x25519_size) != x25519_size) 36 | errx(EXIT_FAILURE, "Input is truncated"); 37 | } else if (argc == 3) { 38 | load(argv[1], scalar, x25519_size); 39 | load(argv[2], point, x25519_size); 40 | } else { 41 | fprintf(stderr, "Usage: %s SK [PK]\n", argv[0]); 42 | return 64; 43 | } 44 | 45 | if (x25519(point, scalar, point)) 46 | errx(EXIT_FAILURE, "Invalid public identity"); 47 | duplex_absorb(state, point, x25519_size); 48 | 49 | if (argc == 3) { 50 | uint8_t nonce[duplex_rate]; 51 | if (get(in, nonce, duplex_rate) != duplex_rate) 52 | errx(EXIT_FAILURE, "Input is truncated"); 53 | duplex_absorb(state, nonce, duplex_rate); 54 | } 55 | 56 | process(state); 57 | return EXIT_SUCCESS; 58 | } 59 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | BINDIR := $(PREFIX)/lib/pocketcrypt 2 | INCDIR := $(PREFIX)/include/pocketcrypt 3 | LIBDIR := $(PREFIX)/lib 4 | 5 | CFLAGS := -march=native -O3 -Wall -Wfatal-errors 6 | override CFLAGS += -I. 7 | 8 | %:: %.c Makefile 9 | $(CC) $(CFLAGS) -o $@ $(filter %.c,$^) 10 | 11 | test: $(basename $(wildcard test/*.c)) 12 | @echo $(foreach TEST,$^,&& $(TEST)) 13 | 14 | test/duplex-known test/duplex-sanity test/duplex-speed: duplex.h 15 | test/gimli-known test/gimli-sanity test/gimli-speed: duplex.h 16 | test/shamir-known test/shamir-sanity test/shamir-speed: shamir.[ch] 17 | test/x25519-known test/x25519-sanity test/x25519-speed: x25519.[ch] 18 | 19 | tools: $(basename $(wildcard tools/*.c)) 20 | 21 | tools/cloak tools/reveal: duplex.h swirl.h 22 | tools/decrypt tools/encrypt tools/sign tools/verify: duplex.h x25519.[ch] 23 | tools/keymerge tools/keysplit: shamir.[ch] 24 | tools/keypair: x25519.[ch] 25 | 26 | libpocketcrypt.so: shamir.c x25519.c Makefile 27 | $(CC) $(CFLAGS) -fpic -shared -o $@ $(filter %.c,$^) 28 | 29 | libpocketcrypt.a: shamir.c x25519.c Makefile 30 | $(CC) $(CFLAGS) -c $(filter %.c,$^) 31 | $(AR) rcs $@ $(patsubst %.c,%.o,$(filter %.c,$^)) 32 | 33 | install-headers: 34 | mkdir -p $(DESTDIR)$(INCDIR) $(DESTDIR)$(LIBDIR) 35 | install -m 0644 $(wildcard *.h) $(DESTDIR)$(INCDIR) 36 | 37 | install-shared: install-headers libpocketcrypt.so 38 | install -m 0644 libpocketcrypt.so $(DESTDIR)$(LIBDIR) 39 | 40 | install-static: install-headers libpocketcrypt.a 41 | install -m 0644 libpocketcrypt.a $(DESTDIR)$(LIBDIR) 42 | 43 | install-tools: $(basename $(wildcard tools/*.c)) 44 | mkdir -p $(DESTDIR)$(BINDIR) 45 | install -s $^ $(DESTDIR)$(BINDIR) 46 | 47 | clean: 48 | rm -f $(basename $(wildcard test/*.c tools/*.c)) *.a *.o *.so 49 | 50 | .PHONY: clean install-* test tools 51 | -------------------------------------------------------------------------------- /tools/cloak.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | 7 | #include "duplex.h" 8 | #include "swirl.h" 9 | #include "util.h" 10 | 11 | static void process(duplex_t state) { 12 | size_t chunk = 65536, length; 13 | uint8_t data[65536 + duplex_rate]; 14 | 15 | do { 16 | length = get(in, data, chunk); 17 | duplex_encrypt(state, data, length); 18 | duplex_pad(state); 19 | duplex_squeeze(state, data + length, duplex_rate); 20 | put(out, data, length + duplex_rate); 21 | } while (length == chunk); 22 | } 23 | 24 | int main(int argc, char **argv) { 25 | size_t size = argc >= 2 ? strtoul(argv[1], NULL, 10) : 64; 26 | size_t rounds = argc >= 3 ? strtoul(argv[2], NULL, 10) : 2; 27 | size_t independent = rounds != 0, dependent = rounds - independent; 28 | 29 | if (argc <= 3 && size > 0 && rounds > 0) { 30 | duplex_t seed, state = { 0 }; 31 | uint8_t salt[duplex_rate]; 32 | void *buffer, *password; 33 | 34 | if ((password = getpass("Password: ")) == NULL) 35 | errx(EXIT_FAILURE, "Failed to read password"); 36 | randomise(salt, duplex_rate); 37 | put(out, salt, duplex_rate); 38 | 39 | duplex_absorb(state, salt, duplex_rate); 40 | memcpy(seed, state, duplex_size); 41 | duplex_absorb(state, password, strlen(password)); 42 | duplex_pad(state); 43 | 44 | if ((buffer = malloc(size << 20)) == NULL) 45 | err(EXIT_FAILURE, "malloc"); 46 | duplex_swirl(state, seed, buffer, size << 20, independent, dependent); 47 | free(buffer); 48 | 49 | process(state); 50 | return EXIT_SUCCESS; 51 | } 52 | 53 | fprintf(stderr, "Usage: %s [SIZE [ROUNDS]]\n", argv[0]); 54 | fprintf(stderr, "By default, SIZE is 64 (MB) and ROUNDS is 2.\n"); 55 | return 64; 56 | } 57 | -------------------------------------------------------------------------------- /tools/util.h: -------------------------------------------------------------------------------- 1 | #ifndef UTIL_H 2 | #define UTIL_H 3 | 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | 11 | static const int in = STDIN_FILENO, out = STDOUT_FILENO; 12 | 13 | static inline size_t get(int fd, uint8_t *data, size_t length) { 14 | ssize_t count, total = 0; 15 | while (length && (count = read(fd, data, length))) { 16 | if (count >= 0) 17 | data += count, length -= count, total += count; 18 | else if (errno != EINTR && errno != EAGAIN) 19 | err(EXIT_FAILURE, "read"); 20 | } 21 | return (size_t) total; 22 | } 23 | 24 | static inline void load(const char *file, void *data, size_t length) { 25 | int fd = file ? open(file, O_RDONLY) : in; 26 | if (file && fd < 0) 27 | err(EXIT_FAILURE, "%s", file); 28 | if (get(fd, data, length) != length) 29 | errx(EXIT_FAILURE, "%s is truncated", file); 30 | if (file) 31 | close(fd); 32 | } 33 | 34 | static inline void put(int fd, const uint8_t *data, size_t length) { 35 | while (length > 0) { 36 | ssize_t count = write(fd, data, length); 37 | if (count >= 0) 38 | data += count, length -= count; 39 | else if (errno != EINTR && errno != EAGAIN) 40 | err(EXIT_FAILURE, "write"); 41 | } 42 | } 43 | 44 | static inline void randomise(void *data, size_t length) { 45 | int getentropy(void *data, size_t length); 46 | if (getentropy(data, length)) 47 | err(EXIT_FAILURE, "getentropy"); 48 | } 49 | 50 | static inline void save(const char *file, const void *data, 51 | size_t length) { 52 | int fd = file ? open(file, O_WRONLY | O_CREAT | O_TRUNC, 0600) : out; 53 | if (file && fd < 0) 54 | err(EXIT_FAILURE, "%s", file); 55 | put(fd, data, length); 56 | if (file) 57 | close(fd); 58 | } 59 | 60 | #endif 61 | -------------------------------------------------------------------------------- /tools/reveal.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | 7 | #include "duplex.h" 8 | #include "swirl.h" 9 | #include "util.h" 10 | 11 | static void process(duplex_t state) { 12 | size_t chunk = 65536, length; 13 | uint8_t data[65536 + duplex_rate]; 14 | 15 | do { 16 | length = get(in, data, chunk + duplex_rate); 17 | if (length < duplex_rate) 18 | errx(EXIT_FAILURE, "Input is truncated"); 19 | length -= duplex_rate; 20 | 21 | duplex_decrypt(state, data, length); 22 | duplex_pad(state); 23 | duplex_decrypt(state, data + length, duplex_rate); 24 | if (duplex_compare(data + length, 0, duplex_rate)) 25 | errx(EXIT_FAILURE, "Authentication failed"); 26 | put(out, data, length); 27 | } while (length == chunk); 28 | } 29 | 30 | int main(int argc, char **argv) { 31 | size_t size = argc >= 2 ? strtoul(argv[1], NULL, 10) : 64; 32 | size_t rounds = argc >= 3 ? strtoul(argv[2], NULL, 10) : 2; 33 | size_t independent = rounds != 0, dependent = rounds - independent; 34 | 35 | if (argc <= 3 && size > 0 && rounds > 0) { 36 | duplex_t seed, state = { 0 }; 37 | uint8_t salt[duplex_rate]; 38 | void *buffer, *password; 39 | 40 | if ((password = getpass("Password: ")) == NULL) 41 | errx(EXIT_FAILURE, "Failed to read password"); 42 | if (get(in, salt, duplex_rate) != duplex_rate) 43 | errx(EXIT_FAILURE, "Input is truncated"); 44 | 45 | duplex_absorb(state, salt, duplex_rate); 46 | memcpy(seed, state, duplex_size); 47 | duplex_absorb(state, password, strlen(password)); 48 | duplex_pad(state); 49 | 50 | if ((buffer = malloc(size << 20)) == NULL) 51 | err(EXIT_FAILURE, "malloc"); 52 | duplex_swirl(state, seed, buffer, size << 20, independent, dependent); 53 | free(buffer); 54 | 55 | process(state); 56 | return EXIT_SUCCESS; 57 | } 58 | 59 | fprintf(stderr, "Usage: %s [SIZE [ROUNDS]]\n", argv[0]); 60 | fprintf(stderr, "By default, SIZE is 64 (MB) and ROUNDS is 2.\n"); 61 | return 64; 62 | } 63 | -------------------------------------------------------------------------------- /test/shamir-sanity.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | 7 | #include "shamir.h" 8 | 9 | static secret_t entropy[254], secret, secret2; 10 | static share_t shares[255]; 11 | static uint32_t seed = 0x12345678; 12 | 13 | static void bitflip(share_t key) { 14 | seed += seed * seed | 5; 15 | key[1 + (seed >> 27)] ^= 1 << (seed >> 24 & 7); /* secret index */ 16 | } 17 | 18 | static void fill(uint8_t *out, size_t length) { 19 | for (size_t i = 0; i < length; i++) { 20 | seed += seed * seed | 5; 21 | out[i] = seed >> 24; 22 | } 23 | } 24 | 25 | int main(void) { 26 | for (uint8_t k = 255; k > 1; k = 15 * k >> 4) { 27 | fill(secret, secret_size); 28 | fill((uint8_t *) entropy, sizeof(entropy)); 29 | 30 | for (uint8_t i = 0, j = 0; i < 255; i++) { 31 | /* Inline Fisher-Yates shuffle: random array indices */ 32 | seed += seed * seed | 5, j = (uint64_t) seed * (i + 1) >> 32; 33 | memmove(shares[i], shares[j], share_size); 34 | shamir_split(shares[j], i, k, secret, entropy); 35 | } 36 | 37 | memset(secret2, 0, secret_size); 38 | shamir_combine(secret2, k, shares); 39 | if (memcmp(secret, secret2, secret_size) != 0) /* variable time */ 40 | errx(EXIT_FAILURE, "Quorate secret reconstruction failed"); 41 | 42 | memset(secret2, 0, secret_size); 43 | shamir_combine(secret2, 255, shares); 44 | if (memcmp(secret, secret2, secret_size) != 0) /* variable time */ 45 | errx(EXIT_FAILURE, "Overconstrained secret reconstruction failed"); 46 | 47 | memset(secret2, 0, secret_size); 48 | shamir_combine(secret2, k - 1, shares); 49 | if (memcmp(secret, secret2, secret_size) == 0) /* variable time */ 50 | errx(EXIT_FAILURE, "Non-quorate secret reconstruction succeeded"); 51 | 52 | bitflip(shares[0]); /* corrupt random share as shares are shuffled */ 53 | memset(secret2, 0, secret_size); 54 | shamir_combine(secret2, k, shares); 55 | if (memcmp(secret, secret2, secret_size) == 0) /* variable time */ 56 | errx(EXIT_FAILURE, "Invalid secret reconstruction succeeded"); 57 | } 58 | 59 | printf("Secret sharing operations sanity-checked\n"); 60 | return EXIT_SUCCESS; 61 | } 62 | -------------------------------------------------------------------------------- /test/x25519-speed.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | 5 | #include "x25519.h" 6 | 7 | static uint8_t buffer[128]; 8 | 9 | static double exchange(size_t repeat) { 10 | clock_t start = clock(); 11 | for (size_t i = 0; i < repeat; i++) 12 | x25519(buffer + 64, buffer + 32, buffer); 13 | return 1.0e6 * (clock() - start) / CLOCKS_PER_SEC / repeat; 14 | } 15 | 16 | static double invert(size_t repeat) { 17 | clock_t start = clock(); 18 | for (size_t i = 0; i < repeat; i++) 19 | x25519_invert(buffer + 32, buffer); 20 | return 1.0e6 * (clock() - start) / CLOCKS_PER_SEC / repeat; 21 | } 22 | 23 | static double pointmap(size_t repeat) { 24 | clock_t start = clock(); 25 | for (size_t i = 0; i < repeat; i++) 26 | x25519_point(buffer + 32, buffer); 27 | return 1.0e6 * (clock() - start) / CLOCKS_PER_SEC / repeat; 28 | } 29 | 30 | static double scalarmap(size_t repeat) { 31 | clock_t start = clock(); 32 | for (size_t i = 0; i < repeat; i++) 33 | x25519_scalar(buffer + 32, buffer); 34 | return 1.0e9 * (clock() - start) / CLOCKS_PER_SEC / repeat; 35 | } 36 | 37 | static double sign(size_t repeat) { 38 | clock_t start = clock(); 39 | for (size_t i = 0; i < repeat; i++) { 40 | x25519_sign(buffer, buffer + 32, buffer + 64, buffer + 96); 41 | x25519(buffer + 64, buffer + 64, x25519_base); 42 | } 43 | return 1.0e6 * (clock() - start) / CLOCKS_PER_SEC / repeat; 44 | } 45 | 46 | static double verify(size_t repeat) { 47 | clock_t start = clock(); 48 | for (size_t i = 0; i < repeat; i++) 49 | x25519_verify(buffer, buffer + 32, buffer + 64, buffer + 96); 50 | return 1.0e6 * (clock() - start) / CLOCKS_PER_SEC / repeat; 51 | } 52 | 53 | int main(void) { 54 | for (size_t i = 0; i < sizeof(buffer); i++) 55 | buffer[i] = (uint8_t) i; 56 | 57 | exchange(512); /* warm up any dynamic CPU frequency scaling */ 58 | printf("X25519 exchanges in %0.1f us\n", exchange(1024)); 59 | printf("X25519 inverts scalars in %0.1f us\n", invert(8192)); 60 | printf("X25519 maps to curve points in %0.1f us\n", pointmap(2<<12)); 61 | printf("X25519 maps to safe scalars in %0.1f ns\n", scalarmap(2<<18)); 62 | printf("X25519 signs in %0.1f us\n", sign(1024)); 63 | printf("X25519 verifies in %0.1f us\n\n", verify(1024)); 64 | 65 | return EXIT_SUCCESS; 66 | } 67 | -------------------------------------------------------------------------------- /shamir.c: -------------------------------------------------------------------------------- 1 | /* shamir.c from Pocketcrypt: https://github.com/arachsys/pocketcrypt */ 2 | 3 | #include 4 | 5 | typedef uint8_t secret_t[32]; 6 | typedef uint8_t share_t[33]; 7 | typedef uint32_t sliced_t[8]; 8 | 9 | static void add(sliced_t r, const sliced_t x, const sliced_t y) { 10 | for (int i = 0; i < 8; i++) 11 | r[i] = x[i] ^ y[i]; 12 | } 13 | 14 | static void mla(sliced_t r, const sliced_t x, const sliced_t y, 15 | const sliced_t z) { 16 | uint32_t t[16] = { 0 }; 17 | 18 | for (int i = 0; i < 8; i++) 19 | for (int j = 0; j < 8; j++) 20 | t[i + j] ^= x[i] & y[j]; 21 | 22 | for (int i = 6; i >= 0; i--) { 23 | t[i + 4] ^= t[i + 8]; 24 | t[i + 3] ^= t[i + 8]; 25 | t[i + 1] ^= t[i + 8]; 26 | t[i + 0] ^= t[i + 8]; 27 | } 28 | 29 | for (int i = 0; i < 8; i++) 30 | r[i] = (z ? z[i] : 0) ^ t[i]; 31 | } 32 | 33 | static void mul(sliced_t r, const sliced_t x, const sliced_t y) { 34 | mla(r, x, y, 0); 35 | } 36 | 37 | static void sqr(sliced_t r, sliced_t x) { 38 | uint32_t t[16] = { 39 | x[0], 0, x[1], 0, x[2], 0, x[3], 0, 40 | x[4], 0, x[5], 0, x[6], 0, x[7], 0 41 | }; 42 | 43 | for (int i = 6; i >= 0; i--) { 44 | if (i == 5 || i == 3) 45 | continue; 46 | t[i + 4] ^= t[i + 8]; 47 | t[i + 3] ^= t[i + 8]; 48 | t[i + 1] ^= t[i + 8]; 49 | t[i + 0] ^= t[i + 8]; 50 | } 51 | 52 | for (int i = 0; i < 8; i++) 53 | r[i] = t[i]; 54 | } 55 | 56 | static void div(sliced_t r, sliced_t x, sliced_t y) { 57 | sliced_t t, u, v; 58 | 59 | sqr(t, y); 60 | sqr(t, t); 61 | sqr(u, t); 62 | mul(v, u, y); 63 | sqr(u, u); 64 | mul(u, u, v); 65 | sqr(u, u); 66 | sqr(v, u); 67 | sqr(v, v); 68 | mul(u, u, t); 69 | mul(u, u, v); 70 | mul(r, u, x); 71 | } 72 | 73 | static void dice(secret_t r, const sliced_t x) { 74 | for (int i = 0; i < 8; i++) 75 | for (int j = 0; j < 32; j++) 76 | r[j] = (i ? r[j] : 0) ^ (x[i] >> j & 1) << i; 77 | } 78 | 79 | static void fill(sliced_t r, const uint8_t x) { 80 | for (int i = 0; i < 8; i++) 81 | r[i] = -(x >> i & 1); 82 | } 83 | 84 | static void slice(sliced_t r, const secret_t x) { 85 | for (int i = 0; i < 32; i++) 86 | for (int j = 0; j < 8; j++) 87 | r[j] = (i ? r[j] : 0) ^ ((uint32_t) x[i] >> j & 1) << i; 88 | } 89 | 90 | void shamir_combine(secret_t secret, uint8_t count, 91 | const share_t shares[count]) { 92 | sliced_t x[255], y[255], z = { 0 }; 93 | 94 | for (int i = 0; i < count; i++) { 95 | fill(x[i], shares[i][0]); 96 | slice(y[i], shares[i] + 1); 97 | } 98 | 99 | for (int i = 0; i < count; i++) { 100 | sliced_t s = { -1 }, t = { -1 }, u; 101 | for (int j = 0; j < count; j++) 102 | if (i != j) { 103 | mul(s, s, x[j]); 104 | add(u, x[i], x[j]); 105 | mul(t, t, u); 106 | } 107 | div(s, s, t); 108 | mla(z, s, y[i], z); 109 | } 110 | 111 | dice(secret, z); 112 | } 113 | 114 | void shamir_split(share_t share, uint8_t index, uint8_t threshold, 115 | const secret_t secret, const secret_t entropy[threshold - 1]) { 116 | sliced_t x, y, z = { -1 }; 117 | 118 | index += index != 255; 119 | fill(x, index); 120 | slice(y, secret); 121 | 122 | for (int i = 0; i < threshold - 1; i++) { 123 | mul(z, z, x); 124 | mla(y, z, (uint32_t *) entropy[i], y); 125 | } 126 | 127 | share[0] = index; 128 | dice(share + 1, y); 129 | } 130 | -------------------------------------------------------------------------------- /test/x25519-sanity.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | 7 | #include "x25519.h" 8 | 9 | /* Based on test_x25519.c from Mike Hamburg's STROBE test suite */ 10 | 11 | static uint32_t seed = 0x12345678; 12 | 13 | static void bitflip(x25519_t key) { 14 | seed += seed * seed | 5; 15 | key[seed >> 27] ^= 1 << (seed >> 24 & 7); /* secret index */ 16 | } 17 | 18 | static void generate(x25519_t key) { 19 | for (size_t i = 0; i < x25519_size; i++) { 20 | seed += seed * seed | 5; 21 | key[i] = seed >> 24; 22 | } 23 | } 24 | 25 | int main(void) { 26 | for (size_t i = 0; i < 1000; i++) { 27 | x25519_t shared1, shared2, public1, public2, secret1, secret2; 28 | 29 | generate(secret1); 30 | generate(secret2); 31 | 32 | x25519(public1, secret1, x25519_base); 33 | x25519(public2, secret2, x25519_base); 34 | 35 | x25519(shared1, secret1, public2); 36 | x25519(shared2, secret2, public1); 37 | if (memcmp(shared1, shared2, x25519_size) != 0) /* variable time */ 38 | errx(EXIT_FAILURE, "Valid key exchange failed"); 39 | 40 | bitflip(secret2); /* secret index */ 41 | x25519(shared2, secret2, public1); 42 | if (memcmp(shared1, shared2, x25519_size) == 0) /* variable time */ 43 | errx(EXIT_FAILURE, "Invalid key exchange succeeded"); 44 | } 45 | 46 | for (size_t i = 0; i < 1000; i++) { 47 | x25519_t challenge, ephemeral, identity, response; 48 | 49 | generate(identity); 50 | generate(ephemeral); 51 | generate(challenge); 52 | x25519_sign(response, challenge, ephemeral, identity); 53 | 54 | x25519(ephemeral, ephemeral, x25519_base); 55 | x25519(identity, identity, x25519_base); 56 | if (x25519_verify(response, challenge, ephemeral, identity) != 0) 57 | errx(EXIT_FAILURE, "Valid signature failed to verify"); 58 | 59 | bitflip(challenge); 60 | if (x25519_verify(response, challenge, ephemeral, identity) == 0) 61 | errx(EXIT_FAILURE, "Invalid signature successfully verified"); 62 | } 63 | 64 | for (size_t i = 0; i < 1000; i++) { 65 | x25519_t scalar1, scalar2, inverse, point1, point2, point3; 66 | 67 | generate(scalar1); 68 | generate(scalar2); 69 | x25519(point1, scalar1, x25519_base); 70 | x25519(point2, scalar2, x25519_base); 71 | x25519(point2, scalar1, point2); 72 | 73 | x25519_invert(inverse, scalar2); 74 | x25519(point3, inverse, point2); 75 | if (memcmp(point1, point3, x25519_size) != 0) /* variable time */ 76 | errx(EXIT_FAILURE, "Valid scalar inversion failed"); 77 | 78 | bitflip(scalar2); 79 | x25519_invert(inverse, scalar2); 80 | x25519(point3, inverse, point2); 81 | if (memcmp(point1, point3, x25519_size) == 0) /* variable time */ 82 | errx(EXIT_FAILURE, "Invalid scalar inversion succeeded"); 83 | } 84 | 85 | for (size_t i = 0; i < 1000; i++) { 86 | x25519_t scalar1, scalar2, point1, point2; 87 | generate(scalar1); 88 | x25519_scalar(scalar2, scalar1); 89 | if (scalar2[0] & 7) 90 | errx(EXIT_FAILURE, "Scalar representative is not torsion-free"); 91 | 92 | x25519(point1, scalar1, x25519_base); 93 | x25519(point2, scalar2, x25519_base); 94 | if (memcmp(point1, point2, x25519_size) != 0) /* variable time */ 95 | errx(EXIT_FAILURE, "Scalar representative is not equivalent"); 96 | } 97 | 98 | printf("Key exchange and signatures sanity-checked\n"); 99 | return EXIT_SUCCESS; 100 | } 101 | -------------------------------------------------------------------------------- /test/gimli-sanity.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | 7 | #define duplex_permute duplex_gimli 8 | #include "duplex.h" 9 | 10 | static void chunk_absorb(duplex_t state, uint8_t *buffer, size_t length, 11 | size_t chunk) { 12 | while (chunk <= length) { 13 | duplex_absorb(state, buffer, chunk); 14 | buffer += chunk, length -= chunk; 15 | } 16 | duplex_absorb(state, buffer, length); 17 | duplex_pad(state); 18 | } 19 | 20 | static void chunk_decrypt(duplex_t state, uint8_t *buffer, size_t length, 21 | size_t chunk) { 22 | while (chunk <= length) { 23 | duplex_decrypt(state, buffer, chunk); 24 | buffer += chunk, length -= chunk; 25 | } 26 | duplex_decrypt(state, buffer, length); 27 | duplex_pad(state); 28 | } 29 | 30 | static void chunk_encrypt(duplex_t state, uint8_t *buffer, size_t length, 31 | size_t chunk) { 32 | while (chunk <= length) { 33 | duplex_encrypt(state, buffer, chunk); 34 | buffer += chunk, length -= chunk; 35 | } 36 | duplex_encrypt(state, buffer, length); 37 | duplex_pad(state); 38 | } 39 | 40 | static void chunk_squeeze(duplex_t state, uint8_t *buffer, size_t length, 41 | size_t chunk) { 42 | while (chunk <= length) { 43 | duplex_squeeze(state, buffer, chunk); 44 | buffer += chunk, length -= chunk; 45 | } 46 | duplex_squeeze(state, buffer, length); 47 | } 48 | 49 | static void fill(void *out1, void *out2, size_t length) { 50 | static uint32_t seed = 0x12345678; 51 | for (size_t i = 0; i < length; i++) { 52 | seed += seed * seed | 5; 53 | ((uint8_t *) out1)[i] = seed >> 24; 54 | ((uint8_t *) out2)[i] = seed >> 24; 55 | } 56 | } 57 | 58 | int main(void) { 59 | const size_t min = 16, max = 48, size = 4096; 60 | uint8_t buffer1[size], buffer2[size]; 61 | duplex_t state1, state2; 62 | 63 | /* Check streaming absorb + pad matches a padded bulk absorb */ 64 | for (size_t length = size - 15; length <= size; length++) 65 | for (size_t chunk = min; chunk <= max; chunk++) { 66 | fill(buffer1, buffer2, length); 67 | fill(state1, state2, duplex_size); 68 | duplex_absorb(state1, buffer1, length); 69 | duplex_pad(state1); 70 | chunk_absorb(state2, buffer2, length, chunk); 71 | if (memcmp(state1, state2, duplex_size)) 72 | errx(EXIT_FAILURE, "Streaming absorb failure"); 73 | } 74 | 75 | /* Check streaming decrypt + pad matches a padded bulk decrypt */ 76 | for (size_t length = size - 15; length <= size; length++) 77 | for (size_t chunk = min; chunk <= max; chunk++) { 78 | fill(buffer1, buffer2, length); 79 | fill(state1, state2, duplex_size); 80 | duplex_decrypt(state1, buffer1, length); 81 | duplex_pad(state1); 82 | chunk_decrypt(state2, buffer2, length, chunk); 83 | if (memcmp(buffer1, buffer2, length)) 84 | errx(EXIT_FAILURE, "Streaming decrypt failure"); 85 | if (memcmp(state1, state2, duplex_size)) 86 | errx(EXIT_FAILURE, "Streaming decrypt failure"); 87 | } 88 | 89 | /* Check streaming encrypt + pad matches a padded bulk encrypt */ 90 | for (size_t length = size - 15; length <= size; length++) 91 | for (size_t chunk = min; chunk <= max; chunk++) { 92 | fill(buffer1, buffer2, length); 93 | fill(state1, state2, duplex_size); 94 | duplex_encrypt(state1, buffer1, length); 95 | duplex_pad(state1); 96 | chunk_encrypt(state2, buffer2, length, chunk); 97 | if (memcmp(buffer1, buffer2, length)) 98 | errx(EXIT_FAILURE, "Streaming encrypt failure"); 99 | if (memcmp(state1, state2, duplex_size)) 100 | errx(EXIT_FAILURE, "Streaming encrypt failure"); 101 | } 102 | 103 | /* Check streaming squeeze matches a bulk squeeze */ 104 | for (size_t chunk = min; chunk <= max; chunk++) { 105 | fill(buffer1, buffer2, size); 106 | fill(state1, state2, duplex_size); 107 | duplex_squeeze(state1, buffer1, size); 108 | chunk_squeeze(state2, buffer2, size, chunk); 109 | if (memcmp(buffer1, buffer2, size)) 110 | errx(EXIT_FAILURE, "Streaming squeeze failure"); 111 | if (memcmp(state1, state2, duplex_size)) 112 | errx(EXIT_FAILURE, "Streaming squeeze failure"); 113 | } 114 | 115 | printf("Streaming duplex operations sanity-checked\n"); 116 | return EXIT_SUCCESS; 117 | } 118 | -------------------------------------------------------------------------------- /test/duplex-sanity.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | 7 | #define duplex_permute duplex_xoodoo 8 | #include "duplex.h" 9 | 10 | static void chunk_absorb(duplex_t state, uint8_t *buffer, size_t length, 11 | size_t chunk) { 12 | while (chunk <= length) { 13 | duplex_absorb(state, buffer, chunk); 14 | buffer += chunk, length -= chunk; 15 | } 16 | duplex_absorb(state, buffer, length); 17 | duplex_pad(state); 18 | } 19 | 20 | static void chunk_decrypt(duplex_t state, uint8_t *buffer, size_t length, 21 | size_t chunk) { 22 | while (chunk <= length) { 23 | duplex_decrypt(state, buffer, chunk); 24 | buffer += chunk, length -= chunk; 25 | } 26 | duplex_decrypt(state, buffer, length); 27 | duplex_pad(state); 28 | } 29 | 30 | static void chunk_encrypt(duplex_t state, uint8_t *buffer, size_t length, 31 | size_t chunk) { 32 | while (chunk <= length) { 33 | duplex_encrypt(state, buffer, chunk); 34 | buffer += chunk, length -= chunk; 35 | } 36 | duplex_encrypt(state, buffer, length); 37 | duplex_pad(state); 38 | } 39 | 40 | static void chunk_squeeze(duplex_t state, uint8_t *buffer, size_t length, 41 | size_t chunk) { 42 | while (chunk <= length) { 43 | duplex_squeeze(state, buffer, chunk); 44 | buffer += chunk, length -= chunk; 45 | } 46 | duplex_squeeze(state, buffer, length); 47 | } 48 | 49 | static void fill(void *out1, void *out2, size_t length) { 50 | static uint32_t seed = 0x12345678; 51 | for (size_t i = 0; i < length; i++) { 52 | seed += seed * seed | 5; 53 | ((uint8_t *) out1)[i] = seed >> 24; 54 | ((uint8_t *) out2)[i] = seed >> 24; 55 | } 56 | } 57 | 58 | int main(void) { 59 | const size_t min = 16, max = 48, size = 4096; 60 | uint8_t buffer1[size], buffer2[size]; 61 | duplex_t state1, state2; 62 | 63 | /* Check streaming absorb + pad matches a padded bulk absorb */ 64 | for (size_t length = size - 15; length <= size; length++) 65 | for (size_t chunk = min; chunk <= max; chunk++) { 66 | fill(buffer1, buffer2, length); 67 | fill(state1, state2, duplex_size); 68 | duplex_absorb(state1, buffer1, length); 69 | duplex_pad(state1); 70 | chunk_absorb(state2, buffer2, length, chunk); 71 | if (memcmp(state1, state2, duplex_size)) 72 | errx(EXIT_FAILURE, "Streaming absorb failure"); 73 | } 74 | 75 | /* Check streaming decrypt + pad matches a padded bulk decrypt */ 76 | for (size_t length = size - 15; length <= size; length++) 77 | for (size_t chunk = min; chunk <= max; chunk++) { 78 | fill(buffer1, buffer2, length); 79 | fill(state1, state2, duplex_size); 80 | duplex_decrypt(state1, buffer1, length); 81 | duplex_pad(state1); 82 | chunk_decrypt(state2, buffer2, length, chunk); 83 | if (memcmp(buffer1, buffer2, length)) 84 | errx(EXIT_FAILURE, "Streaming decrypt failure"); 85 | if (memcmp(state1, state2, duplex_size)) 86 | errx(EXIT_FAILURE, "Streaming decrypt failure"); 87 | } 88 | 89 | /* Check streaming encrypt + pad matches a padded bulk encrypt */ 90 | for (size_t length = size - 15; length <= size; length++) 91 | for (size_t chunk = min; chunk <= max; chunk++) { 92 | fill(buffer1, buffer2, length); 93 | fill(state1, state2, duplex_size); 94 | duplex_encrypt(state1, buffer1, length); 95 | duplex_pad(state1); 96 | chunk_encrypt(state2, buffer2, length, chunk); 97 | if (memcmp(buffer1, buffer2, length)) 98 | errx(EXIT_FAILURE, "Streaming encrypt failure"); 99 | if (memcmp(state1, state2, duplex_size)) 100 | errx(EXIT_FAILURE, "Streaming encrypt failure"); 101 | } 102 | 103 | /* Check streaming squeeze matches a bulk squeeze */ 104 | for (size_t chunk = min; chunk <= max; chunk++) { 105 | fill(buffer1, buffer2, size); 106 | fill(state1, state2, duplex_size); 107 | duplex_squeeze(state1, buffer1, size); 108 | chunk_squeeze(state2, buffer2, size, chunk); 109 | if (memcmp(buffer1, buffer2, size)) 110 | errx(EXIT_FAILURE, "Streaming squeeze failure"); 111 | if (memcmp(state1, state2, duplex_size)) 112 | errx(EXIT_FAILURE, "Streaming squeeze failure"); 113 | } 114 | 115 | printf("Streaming duplex operations sanity-checked\n"); 116 | return EXIT_SUCCESS; 117 | } 118 | -------------------------------------------------------------------------------- /tools/README: -------------------------------------------------------------------------------- 1 | Pocketcrypt examples 2 | ==================== 3 | 4 | To compile these example tools within the source tree, run 5 | 6 | make tools 7 | 8 | and to install them into $HOME/bin (for example), use 9 | 10 | make install-tools BINDIR=$HOME/bin 11 | 12 | For simplicity, this code assumes getentropy() is available to securely 13 | generate keys/nonces, and that the kernel will not leak memory contents 14 | after a process exits. 15 | 16 | 17 | Key generation 18 | -------------- 19 | 20 | To generate an X25519 keypair, run 21 | 22 | keypair SK PK 23 | 24 | This writes a 32-byte secret scalar and 32-byte public identity to files SK 25 | and PK respectively. The scalar is uniformly distributed modulo the order 26 | of the base point and divisible by the cofactor 8, so it safely annihilates 27 | any torsion component during key exchange. It is invariant under RFC 7748 28 | clamping for ease of interoperation with other tools and libraries. 29 | 30 | 31 | Authenticated encryption 32 | ------------------------ 33 | 34 | To encrypt data from the secret key in keyfile SK to the public identity in 35 | keyfile PK, use 36 | 37 | encrypt SK PK 38 | 39 | supplying the plaintext on stdin. This writes encrypted data (comprising a 40 | 16-byte random nonce followed by 65536-byte ciphertext chunks, each with a 41 | 16-byte authentication tag) to stdout. 42 | 43 | To authenticate and decrypt data sent from the public identity in keyfile PK 44 | to the secret key in keyfile SK, run 45 | 46 | decrypt SK PK 47 | 48 | supplying the encrypted data on stdin. Decrypted plaintext chunks are 49 | written to stdout only once authenticated. If authentication fails, 50 | decryption aborts with an error and the invalid plaintext is not released. 51 | 52 | Encryption and decryption are implemented as streaming operations: running 53 | authentication tags are emitted after every 65536-byte chunk of ciphertext 54 | as well as at the end of the stream. Without these, decryption would need 55 | to buffer the entire stream until the final tag is verified, to avoid 56 | releasing unauthenticated plaintext. 57 | 58 | The stream will always end with one chunk shorter than 65536 bytes. If the 59 | plaintext is a multiple of 65536 bytes long, an empty final data chunk is 60 | authenticated to distinguish premature truncation from real end-of-stream. 61 | 62 | For keypairs (a, A) and (b, B), the same shared secret abG results from aB 63 | and bA. Messages can therefore be encrypted and decrypted using either the 64 | sender's secret or the recipient's secret, making them repudiable: anyone 65 | who can decrypt and authenticate a message could also have forged it. 66 | 67 | 68 | Anonymous encryption 69 | -------------------- 70 | 71 | To encrypt data anonymously to the public identity in keyfile PK, use 72 | 73 | encrypt PK 74 | 75 | supplying the plaintext on stdin. This writes a 32-byte ephemeral identity 76 | to stdout, followed by 65536-byte ciphertext chunks and their 16-byte 77 | authentication tags. 78 | 79 | To authenticate and decrypt data sent anonymously to the secret key in 80 | keyfile SK, run 81 | 82 | decrypt SK 83 | 84 | supplying the encrypted data (ephemeral identity, ciphertext chunks and 85 | their tags) on stdin. Decrypted plaintext chunks are written to stdout only 86 | once authenticated. Otherwise, decryption aborts with an error. 87 | 88 | 89 | Signatures 90 | ---------- 91 | 92 | To sign data on stdin using the secret key in keyfile SK and optional 93 | corresponding public identity PK, run 94 | 95 | sign SK [PK] 96 | 97 | The 64-byte signature is written to stdout. If PK is not supplied, it is 98 | calculated from SK at the cost of an additional scalar multiplication. 99 | 100 | To verify a signature file SIG corresponding to the public identity in 101 | keyfile PK, use 102 | 103 | verify PK [SIG] 104 | 105 | passing the signed data on stdin. SIG is optional: if it is omitted, the 106 | 64-byte signature is read from stdin before the message. If verification 107 | fails, an error is returned. 108 | 109 | These signatures use Ed25519-style deterministic nonces as described in the 110 | Pocketcrypt documentation. This eliminates the risk of reusing an ephemeral 111 | key and the need for unbiased entropy during signing. 112 | 113 | 114 | Secret sharing 115 | -------------- 116 | 117 | To divide a 32-byte keyfile SECRET into 33-byte share files, run 118 | 119 | keysplit THRESHOLD SECRET SHARE... 120 | 121 | where 0 < THRESHOLD < 256 is the minimum quorum required to reconstruct the 122 | secret. It must not exceed the total number of shares. 123 | 124 | To recombine a set of share files into a keyfile SECRET, use 125 | 126 | keymerge SECRET SHARE... 127 | 128 | If too few shares are provided, a random secret will be derived. This error 129 | case is not detected. 130 | -------------------------------------------------------------------------------- /duplex.h: -------------------------------------------------------------------------------- 1 | /* duplex.h from Pocketcrypt: https://github.com/arachsys/pocketcrypt */ 2 | 3 | #ifndef DUPLEX_H 4 | #define DUPLEX_H 5 | 6 | #include 7 | #include 8 | 9 | #if defined __clang_major__ && __clang_major__ >= 4 10 | #define duplex_swap(x, ...) __builtin_shufflevector(x, x, __VA_ARGS__) 11 | #elif defined __GNUC__ && __GNUC__ >= 5 12 | #define duplex_swap(x, ...) __builtin_shuffle(x, (typeof(x)) { __VA_ARGS__ }) 13 | #else 14 | #error Vector extensions require clang >= 4.0.0 or gcc >= 5.1.0 15 | #endif 16 | 17 | #if defined __BYTE_ORDER__ && __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__ 18 | #define duplex_byte(state, i) ((uint8_t *) state)[i] 19 | #define duplex_r24 1, 2, 3, 0, 5, 6, 7, 4, 9, 10, 11, 8, 13, 14, 15, 12 20 | #define duplex_rho 11, 8, 9, 10, 15, 12, 13, 14, 3, 0, 1, 2, 7, 4, 5, 6 21 | #elif defined __BYTE_ORDER__ && __BYTE_ORDER__ == __ORDER_BIG_ENDIAN__ 22 | #define duplex_byte(state, i) ((uint8_t *) state)[(i) ^ ((i) < 48 ? 3 : 7)] 23 | #define duplex_r24 3, 0, 1, 2, 7, 4, 5, 6, 11, 8, 9, 10, 15, 12, 13, 14 24 | #define duplex_rho 9, 10, 11, 8, 13, 14, 15, 12, 1, 2, 3, 0, 5, 6, 7, 4 25 | #else 26 | #error Byte order could not be determined 27 | #endif 28 | 29 | #define duplex_counter(state) ((uint64_t *) state)[6] 30 | #define duplex_extra(state) ((uint64_t *) state)[7] 31 | 32 | #ifndef duplex_permute 33 | #define duplex_permute duplex_xoodoo 34 | #endif 35 | 36 | typedef uint8_t uint8x16_t __attribute__((vector_size(16))); 37 | typedef uint32_t uint32x4_t __attribute__((vector_size(16))); 38 | typedef uint32x4_t duplex_t[4]; 39 | 40 | enum { 41 | duplex_rate = sizeof(uint32x4_t), 42 | duplex_size = sizeof(duplex_t) 43 | }; 44 | 45 | static inline void duplex_gimli(uint32x4_t state[3]) { 46 | for (int round = 24; round > 0; round--) { 47 | uint32x4_t x = (uint32x4_t) duplex_swap((uint8x16_t) state[0], duplex_r24); 48 | uint32x4_t y = state[1] << 9 | state[1] >> 23; 49 | uint32x4_t z = state[2]; 50 | 51 | state[2] = x ^ (z << 1) ^ ((y & z) << 2); 52 | state[1] = y ^ x ^ ((x | z) << 1); 53 | state[0] = z ^ y ^ ((x & y) << 3); 54 | 55 | switch (round & 3) { 56 | case 0: 57 | state[0] = duplex_swap(state[0], 1, 0, 3, 2); 58 | state[0] ^= (uint32x4_t) { 0x9e377900 | round, 0, 0, 0 }; 59 | break; 60 | case 2: 61 | state[0] = duplex_swap(state[0], 2, 3, 0, 1); 62 | break; 63 | } 64 | } 65 | } 66 | 67 | static inline void duplex_xoodoo(uint32x4_t state[3]) { 68 | const uint32_t rk[12] = { 69 | 0x058, 0x038, 0x3c0, 0x0d0, 0x120, 0x014, 70 | 0x060, 0x02c, 0x380, 0x0f0, 0x1a0, 0x012 71 | }; 72 | 73 | for (int round = 0; round < 12; round++) { 74 | uint32x4_t p = duplex_swap(state[0] ^ state[1] ^ state[2], 3, 0, 1, 2); 75 | uint32x4_t e = (p << 5 | p >> 27) ^ (p << 14 | p >> 18); 76 | state[0] ^= e, state[1] ^= e, state[2] ^= e; 77 | 78 | uint32x4_t x = state[0] ^ (uint32x4_t) { rk[round], 0, 0, 0 }; 79 | uint32x4_t y = duplex_swap(state[1], 3, 0, 1, 2); 80 | uint32x4_t z = state[2] << 11 | state[2] >> 21; 81 | 82 | state[0] = x ^ (~y & z); 83 | state[1] = y ^ (~z & x); 84 | state[2] = z ^ (~x & y); 85 | 86 | state[1] = state[1] << 1 | state[1] >> 31; 87 | state[2] = (uint32x4_t) duplex_swap((uint8x16_t) state[2], duplex_rho); 88 | } 89 | } 90 | 91 | static inline uint32x4_t duplex_get(const uint8_t in[16]) { 92 | uint32x4_t out; 93 | for (int i = 0; i < 16; i++) 94 | duplex_byte(&out, i) = in[i]; 95 | return out; 96 | } 97 | 98 | static inline void duplex_put(uint8_t out[16], uint32x4_t in) { 99 | for (int i = 0; i < 16; i++) 100 | out[i] = duplex_byte(&in, i); 101 | } 102 | 103 | static inline void duplex_absorb(duplex_t state, const void *data, 104 | size_t length) { 105 | const uint8_t *bytes = data; 106 | uint8_t offset = duplex_counter(state) & 15; 107 | duplex_counter(state) += length; 108 | 109 | while (1) { 110 | if (length < 16 || offset > 0) { 111 | for (int i = offset; i < 16; i++, length--) { 112 | if (length == 0) 113 | return; 114 | duplex_byte(state, i) ^= bytes[i - offset]; 115 | } 116 | bytes += 16 - offset, offset = 0; 117 | duplex_permute(state); 118 | } 119 | 120 | while (length >= 16) { 121 | state[0] ^= duplex_get(bytes); 122 | bytes += 16, length -= 16; 123 | duplex_permute(state); 124 | } 125 | } 126 | } 127 | 128 | static inline int duplex_compare(const void *a, const void *b, 129 | size_t length) { 130 | const uint8_t *as = a, *bs = b; 131 | uint8_t result = 0; 132 | 133 | for (size_t i = 0; i < length; i++) 134 | result |= (a ? as[i] : 0) ^ (b ? bs[i] : 0); 135 | return result ? -1 : 0; 136 | } 137 | 138 | static inline void duplex_decrypt(duplex_t state, void *data, 139 | size_t length) { 140 | uint8_t *bytes = data, offset = duplex_counter(state) & 15; 141 | duplex_counter(state) += length; 142 | 143 | while (1) { 144 | if (length < 16 || offset > 0) { 145 | for (int i = offset; i < 16; i++, length--) { 146 | if (length == 0) 147 | return; 148 | bytes[i - offset] ^= duplex_byte(state, i); 149 | duplex_byte(state, i) ^= bytes[i - offset]; 150 | } 151 | bytes += 16 - offset, offset = 0; 152 | duplex_permute(state); 153 | } 154 | 155 | while (length >= 16) { 156 | uint32x4_t words = duplex_get(bytes); 157 | words ^= state[0], state[0] ^= words; 158 | duplex_put(bytes, words); 159 | bytes += 16, length -= 16; 160 | duplex_permute(state); 161 | } 162 | } 163 | } 164 | 165 | static inline void duplex_encrypt(duplex_t state, void *data, 166 | size_t length) { 167 | uint8_t *bytes = data, offset = duplex_counter(state) & 15; 168 | duplex_counter(state) += length; 169 | 170 | while (1) { 171 | if (length < 16 || offset > 0) { 172 | for (int i = offset; i < 16; i++, length--) { 173 | if (length == 0) 174 | return; 175 | duplex_byte(state, i) ^= bytes[i - offset]; 176 | bytes[i - offset] = duplex_byte(state, i); 177 | } 178 | bytes += 16 - offset, offset = 0; 179 | duplex_permute(state); 180 | } 181 | 182 | while (length >= 16) { 183 | state[0] ^= duplex_get(bytes); 184 | duplex_put(bytes, state[0]); 185 | bytes += 16, length -= 16; 186 | duplex_permute(state); 187 | } 188 | } 189 | } 190 | 191 | static inline void duplex_pad(duplex_t state) { 192 | uint8_t offset = duplex_counter(state) & 15; 193 | duplex_counter(state) += 16 - offset; 194 | 195 | duplex_byte(state, offset) ^= 1; 196 | duplex_byte(state, 47) ^= 1; 197 | duplex_permute(state); 198 | } 199 | 200 | static inline void duplex_ratchet(duplex_t state) { 201 | uint8_t offset = duplex_counter(state) & 15; 202 | duplex_counter(state) += 16; 203 | 204 | for (int i = offset; i < 16; i++) 205 | duplex_byte(state, i) = 0; 206 | duplex_permute(state); 207 | for (int i = 0; i < offset; i++) 208 | duplex_byte(state, i) = 0; 209 | } 210 | 211 | static inline void duplex_squeeze(duplex_t state, void *data, 212 | size_t length) { 213 | uint8_t *bytes = data, offset = duplex_counter(state) & 15; 214 | duplex_counter(state) += length; 215 | 216 | while (1) { 217 | if (length < 16 || offset > 0) { 218 | for (int i = offset; i < 16; i++, length--) { 219 | if (length == 0) 220 | return; 221 | bytes[i - offset] = duplex_byte(state, i); 222 | } 223 | bytes += 16 - offset, offset = 0; 224 | duplex_permute(state); 225 | } 226 | 227 | while (length >= 16) { 228 | duplex_put(bytes, state[0]); 229 | bytes += 16, length -= 16; 230 | duplex_permute(state); 231 | } 232 | } 233 | } 234 | 235 | static inline void duplex_zero(void *data, size_t length) { 236 | __builtin_memset(data, 0, length); 237 | __asm__ __volatile__ ("" :: "r" (data) : "memory"); 238 | } 239 | 240 | #endif 241 | -------------------------------------------------------------------------------- /x25519.c: -------------------------------------------------------------------------------- 1 | /* x25519.c from Pocketcrypt: https://github.com/arachsys/pocketcrypt */ 2 | /* Adapted from Mike Hamburg's STROBE: https://strobe.sourceforge.io/ */ 3 | 4 | #include 5 | #define memcpy __builtin_memcpy 6 | #define memset __builtin_memset 7 | 8 | typedef uint8_t x25519_t[32]; 9 | const x25519_t x25519_base = { 9 }; 10 | 11 | #ifdef __SIZEOF_INT128__ 12 | 13 | #define limb(x) x##ull 14 | enum { limbs = 4, width = 64 }; 15 | typedef uint64_t limb_t; 16 | typedef __uint128_t dlimb_t; 17 | typedef __int128_t sdlimb_t; 18 | 19 | #else /* __SIZEOF_INT128__ */ 20 | 21 | #define limb(x) (uint32_t) (x##ull), (uint32_t) ((x##ull) >> 32) 22 | enum { limbs = 8, width = 32 }; 23 | typedef uint32_t limb_t; 24 | typedef uint64_t dlimb_t; 25 | typedef int64_t sdlimb_t; 26 | 27 | #endif /* __SIZEOF_INT128__ */ 28 | 29 | typedef limb_t element_t[limbs]; 30 | typedef limb_t scalar_t[limbs]; 31 | 32 | static const limb_t zero[limbs] = { 0 }, one[limbs] = { 1 }; 33 | 34 | static const scalar_t scalar_l = { 35 | limb(0x5812631a5cf5d3ed), limb(0x14def9dea2f79cd6), 36 | limb(0x0000000000000000), limb(0x1000000000000000) 37 | }; 38 | 39 | static const scalar_t scalar_r2 = { 40 | limb(0xa40611e3449c0f01), limb(0xd00e1ba768859347), 41 | limb(0xceec73d217f5be65), limb(0x0399411b7c309a3d) 42 | }; 43 | 44 | static void propagate(element_t x, limb_t over) { 45 | over = x[limbs - 1] >> (width - 1) | over << 1; 46 | x[limbs - 1] &= ~((limb_t) 1 << (width - 1)); 47 | 48 | dlimb_t carry = over * 19; 49 | for (int i = 0; i < limbs; i++) 50 | x[i] = carry = carry + x[i], carry >>= width; 51 | } 52 | 53 | static void add(element_t out, const element_t x, const element_t y) { 54 | dlimb_t carry = 0; 55 | for (int i = 0; i < limbs; i++) 56 | out[i] = carry = carry + x[i] + y[i], carry >>= width; 57 | propagate(out, carry); 58 | } 59 | 60 | static void sub(element_t out, const element_t x, const element_t y) { 61 | sdlimb_t carry = -38; 62 | for (int i = 0; i < limbs; i++) 63 | out[i] = carry = carry + x[i] - y[i], carry >>= width; 64 | propagate(out, 1 + carry); 65 | } 66 | 67 | static void mul(element_t out, const element_t x, const element_t y) { 68 | limb_t accum[2 * limbs] = { 0 }; 69 | for (int i = 0; i < limbs; i++) { 70 | dlimb_t carry = 0; 71 | for (int j = 0; j < limbs; j++) { 72 | carry += (dlimb_t) y[i] * x[j] + accum[i + j]; 73 | accum[i + j] = carry, carry >>= width; 74 | } 75 | accum[i + limbs] = carry; 76 | } 77 | 78 | dlimb_t carry = 0; 79 | for (int i = 0; i < limbs; i++) { 80 | carry += (dlimb_t) 38 * accum[i + limbs] + accum[i]; 81 | out[i] = carry, carry >>= width; 82 | } 83 | propagate(out, carry); 84 | } 85 | 86 | static void mul1(element_t out, const element_t x, const limb_t y) { 87 | dlimb_t carry = 0; 88 | for (int i = 0; i < limbs; i++) 89 | out[i] = carry += (dlimb_t) y * x[i], carry >>= width; 90 | carry *= 38; 91 | for (int i = 0; i < limbs; i++) 92 | out[i] = carry += out[i], carry >>= width; 93 | propagate(out, carry); 94 | } 95 | 96 | static void mulsqrn(element_t out, const element_t x, const element_t y, 97 | uint8_t n) { 98 | for (int i = 0; i < n; i++) 99 | mul(out, x, x), x = out; 100 | mul(out, out, y); 101 | } 102 | 103 | static limb_t canon(element_t x) { 104 | dlimb_t carry0 = 19; 105 | for (int i = 0; i < limbs; i++) 106 | x[i] = carry0 += x[i], carry0 >>= width; 107 | propagate(x, carry0); 108 | 109 | limb_t result = 0; 110 | sdlimb_t carry = -19; 111 | for (int i = 0; i < limbs; i++) 112 | result |= x[i] = carry += x[i], carry >>= width; 113 | return ((dlimb_t) result - 1) >> width; 114 | } 115 | 116 | static void condswap(element_t x, element_t y, limb_t mask) { 117 | for (int i = 0; i < limbs; i++) { 118 | limb_t xor = (x[i] ^ y[i]) & mask; 119 | x[i] ^= xor, y[i] ^= xor; 120 | } 121 | } 122 | 123 | static limb_t invsqrt(element_t out, const element_t x) { 124 | const element_t sqrtm1 = { 125 | limb(0xc4ee1b274a0ea0b0), limb(0x2f431806ad2fe478), 126 | limb(0x2b4d00993dfbd7a7), limb(0x2b8324804fc1df0b) 127 | }; 128 | 129 | element_t u, v, y, z; 130 | mulsqrn(u, x, x, 1); 131 | mulsqrn(u, u, x, 1); 132 | mulsqrn(v, u, u, 3); 133 | mulsqrn(u, v, v, 6); 134 | mulsqrn(z, u, x, 1); 135 | mulsqrn(z, z, u, 12); 136 | mulsqrn(v, z, z, 25); 137 | mulsqrn(u, v, z, 25); 138 | mulsqrn(u, u, v, 50); 139 | mulsqrn(z, u, u, 125); 140 | mulsqrn(z, z, x, 2); 141 | 142 | mul(y, z, z); 143 | mul(y, y, x); 144 | add(u, y, one); 145 | add(v, y, sqrtm1); 146 | mul(out, z, sqrtm1); 147 | condswap(out, z, ~canon(u) & ~canon(v)); 148 | 149 | sub(v, y, one); 150 | return ~canon(u) & ~canon(v); 151 | } 152 | 153 | static void ladder1(element_t x2, element_t z2, element_t x3, element_t z3, 154 | element_t t1) { 155 | const limb_t a24 = 121665; 156 | 157 | add(t1, x2, z2); 158 | sub(z2, x2, z2); 159 | add(x2, x3, z3); 160 | sub(z3, x3, z3); 161 | mul(z3, z3, t1); 162 | mul(x2, x2, z2); 163 | add(x3, z3, x2); 164 | sub(z3, z3, x2); 165 | mul(t1, t1, t1); 166 | mul(z2, z2, z2); 167 | sub(x2, t1, z2); 168 | mul1(z2, x2, a24); 169 | add(z2, z2, t1); 170 | } 171 | 172 | static void ladder2(const element_t x1, element_t x2, element_t z2, 173 | element_t x3, element_t z3, const element_t t1) { 174 | mul(z3, z3, z3); 175 | mul(z3, z3, x1); 176 | mul(x3, x3, x3); 177 | mul(z2, z2, x2); 178 | sub(x2, t1, x2); 179 | mul(x2, x2, t1); 180 | } 181 | 182 | static void montmla(scalar_t out, const scalar_t x, const scalar_t y) { 183 | const limb_t montgomery = (limb_t) 0xd2b51da312547e1b; 184 | dlimb_t highcarry = 0; 185 | 186 | for (int i = 0; i < limbs; i++) { 187 | dlimb_t carry1 = 0, carry2 = 0; 188 | limb_t mand1 = x[i], mand2 = montgomery; 189 | for (int j = 0; j < limbs; j++) { 190 | carry1 += (dlimb_t) mand1 * y[j] + out[j]; 191 | if (j == 0) 192 | mand2 *= (limb_t) carry1; 193 | carry2 += (dlimb_t) mand2 * scalar_l[j] + (limb_t) carry1; 194 | if (j > 0) 195 | out[j - 1] = carry2; 196 | carry1 >>= width, carry2 >>= width; 197 | } 198 | out[limbs - 1] = highcarry += carry1 + carry2; 199 | highcarry >>= width; 200 | } 201 | 202 | sdlimb_t scarry = 0; 203 | for (int i = 0; i < limbs; i++) 204 | out[i] = scarry = scarry + out[i] - scalar_l[i], scarry >>= width; 205 | 206 | dlimb_t addl = -(scarry + highcarry), carry = 0; 207 | for (int i = 0; i < limbs; i++) 208 | out[i] = carry += addl * scalar_l[i] + out[i], carry >>= width; 209 | } 210 | 211 | static void montmul(scalar_t out, const scalar_t x, const scalar_t y) { 212 | scalar_t z = { 0 }; 213 | montmla(z, x, y); 214 | memcpy(out, z, sizeof(scalar_t)); 215 | } 216 | 217 | static void get(limb_t out[limbs], const x25519_t in) { 218 | for (int i = 0; i < limbs; i++) { 219 | out[i] = (limb_t) *in++; 220 | for (int j = 8; j < width; j += 8) 221 | out[i] |= (limb_t) *in++ << j; 222 | } 223 | } 224 | 225 | static void put(x25519_t out, const limb_t in[limbs]) { 226 | for (int i = 0; i < limbs; i++) { 227 | for (int j = 0; j < width; j += 8) 228 | *out++ = (uint8_t) (in[i] >> j); 229 | } 230 | } 231 | 232 | static void x25519_core(element_t x2, element_t z2, const x25519_t scalar, 233 | const x25519_t point) { 234 | element_t x1, x3, z3, t1; 235 | limb_t swap = 0; 236 | 237 | get(x1, point); 238 | memcpy(x2, one, sizeof(element_t)); 239 | memcpy(z2, zero, sizeof(element_t)); 240 | memcpy(x3, x1, sizeof(element_t)); 241 | memcpy(z3, one, sizeof(element_t)); 242 | 243 | for (int i = 255; i >= 0; i--) { 244 | uint8_t byte = scalar[i >> 3]; 245 | limb_t bit = -((limb_t) byte >> (i & 7) & 1); 246 | condswap(x2, x3, swap ^ bit); 247 | condswap(z2, z3, swap ^ bit); 248 | swap = bit; 249 | 250 | ladder1(x2, z2, x3, z3, t1); 251 | ladder2(x1, x2, z2, x3, z3, t1); 252 | } 253 | condswap(x2, x3, swap); 254 | condswap(z2, z3, swap); 255 | } 256 | 257 | int x25519(x25519_t out, const x25519_t scalar, const x25519_t point) { 258 | element_t t, u, v, x, z; 259 | x25519_core(x, z, scalar, point); 260 | 261 | mulsqrn(u, z, z, 1); 262 | mulsqrn(u, u, z, 1); 263 | mulsqrn(v, u, u, 3); 264 | mulsqrn(u, v, v, 6); 265 | mulsqrn(t, u, z, 1); 266 | mulsqrn(t, t, u, 12); 267 | mulsqrn(v, t, t, 25); 268 | mulsqrn(u, v, t, 25); 269 | mulsqrn(u, u, v, 50); 270 | mulsqrn(t, u, u, 125); 271 | mulsqrn(t, t, z, 2); 272 | mulsqrn(t, t, z, 2); 273 | mulsqrn(t, t, z, 1); 274 | mul(x, x, t); 275 | 276 | limb_t result = canon(x); 277 | put(out, x); 278 | return result; 279 | } 280 | 281 | void x25519_invert(x25519_t out, const x25519_t scalar) { 282 | scalar_t x, y, z[8]; 283 | get(x, scalar); 284 | 285 | montmul(z[0], x, scalar_r2); 286 | montmul(z[7], z[0], z[0]); 287 | for (int i = 0; i < 7; i++) 288 | montmul(z[i + 1], z[i], z[7]); 289 | memcpy(y, z[0], sizeof(scalar_t)); 290 | 291 | uint8_t residue = 0, trailing = 0; 292 | for (int i = 248; i >= -3; i--) { 293 | limb_t limb = i < 0 ? 0 : scalar_l[i / width] - (i < width ? 2 : 0); 294 | residue = residue << 1 | (limb >> (i % width) & 1); 295 | montmul(y, y, y); 296 | if (residue >> 3 != 0) 297 | trailing = residue, residue = 0; 298 | if (trailing > 0 && (trailing & 7) == 0) 299 | montmul(y, y, z[trailing >> 4]), trailing = 0; 300 | trailing <<= 1; 301 | } 302 | 303 | montmla(y, zero, zero); 304 | put(out, y); 305 | } 306 | 307 | void x25519_point(x25519_t out, const x25519_t element) { 308 | const limb_t a = 486662; 309 | const element_t k = { 310 | limb(0x7623c9b16be2be8d), limb(0xa179cff2a5a0370e), 311 | limb(0xa965fecd840850b1), limb(0x28f9b6ff607c41e9) 312 | }; 313 | 314 | element_t r, s, x, y, z; 315 | get(r, element); 316 | 317 | mul(s, r, r); 318 | add(s, s, s); 319 | add(x, s, one); 320 | mul(y, x, x); 321 | mul1(z, s, a); 322 | mul1(z, z, a); 323 | sub(z, z, y); 324 | mul1(z, z, a); 325 | mul(s, y, x); 326 | mul(s, s, z); 327 | 328 | limb_t mask = invsqrt(s, s); 329 | mul1(x, s, a); 330 | mul(x, x, s); 331 | mul(x, x, y); 332 | mul(x, x, z); 333 | sub(x, zero, x); 334 | 335 | mul(s, k, r); 336 | mul(s, s, r); 337 | mul(s, s, x); 338 | condswap(x, s, mask); 339 | 340 | canon(x); 341 | put(out, x); 342 | } 343 | 344 | void x25519_scalar(x25519_t out, const x25519_t scalar) { 345 | const scalar_t k = { 346 | limb(0x6106e529e2dc2f79), limb(0x07d39db37d1cdad0), 347 | limb(0x0000000000000000), limb(0x0600000000000000) 348 | }; 349 | 350 | scalar_t x; 351 | get(x, scalar); 352 | montmul(x, x, k); 353 | montmul(x, x, scalar_r2); 354 | 355 | dlimb_t carry = 0; 356 | for (int i = 0; i < limbs; i++) 357 | x[i] = carry += (dlimb_t) x[i] << 3, carry >>= width; 358 | put(out, x); 359 | } 360 | 361 | void x25519_sign(x25519_t response, const x25519_t challenge, 362 | const x25519_t ephemeral, const x25519_t identity) { 363 | scalar_t x, y, z; 364 | get(x, ephemeral); 365 | get(y, identity); 366 | get(z, challenge); 367 | 368 | montmla(x, y, z); 369 | montmul(y, x, scalar_r2); 370 | put(response, y); 371 | } 372 | 373 | int x25519_verify(const x25519_t response, const x25519_t challenge, 374 | const x25519_t ephemeral, const x25519_t identity) { 375 | element_t x1, z1, x2, z2, x3, z3, t1; 376 | x25519_core(x1, z1, challenge, identity); 377 | x25519_core(x2, z2, response, x25519_base); 378 | 379 | memcpy(x3, x1, sizeof(element_t)); 380 | memcpy(z3, z1, sizeof(element_t)); 381 | ladder1(x2, z2, x3, z3, t1); 382 | mul(z2, z2, x1); 383 | mul(z2, z2, z1); 384 | 385 | get(t1, ephemeral); 386 | mul(z2, z2, t1); 387 | mul1(z2, z2, 16); 388 | 389 | mul(z3, z3, t1); 390 | sub(z3, z3, x3); 391 | mul(z3, z3, z3); 392 | 393 | sub(z3, z3, z2); 394 | return canon(z2) | ~canon(z3); 395 | } 396 | -------------------------------------------------------------------------------- /README: -------------------------------------------------------------------------------- 1 | Pocketcrypt 2 | =========== 3 | 4 | Pocketcrypt is a tiny legacy-free cryptographic library providing duplex 5 | constructions using Gimli or Xoodoo, together with X25519 for key exchange 6 | and Schnorr signatures. Eschewing interoperability with the standard museum 7 | of primitives and protocols, it offers concise, easily-understood code that 8 | avoids the ugly boilerplate and obfuscation of larger libraries. 9 | 10 | Beware! This early version of Pocketcrypt is neither formally audited nor 11 | officially released. Safely composing these low-level primitives requires 12 | cryptographic expertise. Gimli and Xoodoo are also relatively new and still 13 | under active cryptanalytic scrutiny. Please review the relevant literature 14 | and audit the implementation carefully before considering this library. 15 | 16 | 17 | Duplex 18 | ====== 19 | 20 | duplex.h is a fast, architecture-independent implementation of the Gimli and 21 | Xoodoo permutations using gcc/clang vector extensions, together with 22 | associated duplex operations. 23 | 24 | To use this header-only library, copy duplex.h into your tree. There are no 25 | link-time dependencies. 26 | 27 | 28 | Initialising and permuting state 29 | -------------------------------- 30 | 31 | Define duplex_permute as duplex_gimli or duplex_xoodoo before including 32 | duplex.h to explictly select a permutation. The default is duplex_xoodoo. 33 | 34 | Both permutations operate over a state of twelve 32-bit words, manipulated 35 | here as a vector array uint32x4_t[3]. When individual bytes of the state are 36 | required, the uint32_t words are accessed little-endian, ensuring results 37 | are independent of platform byte order. 38 | 39 | Internally, higher-level duplex operations absorb and squeeze bytes in 40 | 16-byte chunks corresponding to the permutation rate. By maintaining a byte 41 | counter alongside the duplex state, operations can also seamlessly absorb, 42 | squeeze, encrypt and decrypt partial chunks. The combined state and counter 43 | are stored as duplex_t, a type alias for uint32x4_t[4]. 44 | 45 | For convenience, duplex.h defines duplex_rate as 16 (the permutation rate) 46 | and duplex_size as sizeof(duplex_t) to help avoid magic numbers for nonce, 47 | tag and state sizes in client code. 48 | 49 | Initialise a duplex state including the byte counter with 50 | 51 | duplex_t state = { 0 }; 52 | 53 | Use duplex_counter(state) to dereference the uint64_t counter as an lvalue. 54 | 55 | To directly permute the state, use 56 | 57 | duplex_permute(state); 58 | 59 | Typically this is bundled into the higher-level operations. 60 | 61 | 62 | Absorbing and squeezing data 63 | ---------------------------- 64 | 65 | Absorb a buffer of bytes into a duplex state with 66 | 67 | duplex_absorb(state, data, length); 68 | 69 | Each rate-sized chunk is absorbed in turn, then the state is permuted before 70 | continuing. Once any final partial chunk is absorbed, the counter will 71 | advance by length ready for the next duplex operation. 72 | 73 | Squeeze data from a duplex state into a buffer with 74 | 75 | duplex_squeeze(state, data, length); 76 | 77 | Bytes are extracted from the duplex state into the buffer, permuting the 78 | state after each rate-sized chunk. Once any final partial chunk is squeezed, 79 | the counter will advance by length as with duplex_absorb(). 80 | 81 | 82 | Encryption and decryption 83 | ------------------------- 84 | 85 | To encrypt a plaintext using a duplex state, for example after absorbing a 86 | shared key and nonce, call 87 | 88 | duplex_encrypt(state, data, length); 89 | 90 | Similarly, decrypt a ciphertext using a duplex state with 91 | 92 | duplex_decrypt(state, data, length); 93 | 94 | The rate portion of the state is mixed into rate-sized chunks of the buffer 95 | in turn. Before permuting and continuing to the next chunk, the plaintext is 96 | absorbed back into the rate. (For encryption this is the original chunk; for 97 | decryption it is the updated chunk.) The counter will advance by length. 98 | 99 | To implement authenticated encryption, squeeze and append a rate-sized tag 100 | after encrypting a message and padding the state. This can then be checked 101 | against duplex_rate bytes squeezed by the recipient after decryption. 102 | 103 | 104 | Padding 105 | ------- 106 | 107 | To prevent extension attacks following an operation on variable-length data, 108 | pad the state with 109 | 110 | duplex_pad(state); 111 | 112 | after the final duplex_absorb(), duplex_encrypt() or duplex_decrypt(). 113 | 114 | This pads the final partial/empty chunk then forks the capacity part of the 115 | state before permuting it, as described in the Gimli NIST submission. The 116 | counter will advance to the next multiple of the rate. 117 | 118 | When designing protocols, compulsory fixed-length inputs that are naturally 119 | multiples of duplex_rate such as keys or nonces can safely be absorbed 120 | unpadded. However, to avoid extension attacks, variable-length messages must 121 | be padded even if they happen to be an exact multiple of the rate. 122 | 123 | 124 | Ratcheting 125 | ---------- 126 | 127 | To ensure forward secrecy during a session, call 128 | 129 | duplex_ratchet(state); 130 | 131 | This irreversibly ratchets the duplex state by zeroing the rate portion of 132 | the state and permuting, thus preventing rollback even from a completely 133 | compromised state. The counter will advance by exactly duplex_rate. 134 | 135 | 136 | Constant-time comparison 137 | ------------------------ 138 | 139 | Compare two equal-sized byte arrays in constant time with 140 | 141 | duplex_compare(a, b, length); 142 | 143 | This returns 0 for equality, -1 otherwise. If a or b is null, the other 144 | argument is compared with zero. 145 | 146 | This is useful for validating authentication tags or checking other secret 147 | data without inadvertently revealing the location of the first discrepancy 148 | through the time taken to detect it. 149 | 150 | 151 | Clearing sensitive data 152 | ----------------------- 153 | 154 | Clear sensitive data such as keys or cleartext using 155 | 156 | duplex_zero(data, length); 157 | 158 | Unlike memset() or bzero(), the compiler is forbidden to optimise this away 159 | as a 'dead store' even if the buffer is subsequently discarded or unused. 160 | 161 | 162 | Serialising duplex state 163 | ------------------------ 164 | 165 | Use duplex_byte(state, index) to dereference individual bytes of a duplex 166 | state as lvalues, where 0 <= index < duplex_size. This macro may evaluate 167 | the index argument more than once. 168 | 169 | The resulting representation is independent of host byte order: the twelve 170 | 32-bit state words are accessed in turn, followed by the 64-bit counter, 171 | each in standard little-endian order. 172 | 173 | The last eight bytes of duplex_t are serialised as a second 64-bit integer. 174 | They are unused by duplex.h and can safely be discarded. Alternatively, use 175 | duplex_extra(state) to address them as an auxiliary uint64_t lvalue. 176 | 177 | For example, to serialise state to uint8_t packed[56], use 178 | 179 | for (size_t i = 0; i < sizeof(packed); i++) 180 | packed[i] = duplex_byte(state, i); 181 | 182 | and it can later be restored from this packed form with 183 | 184 | for (size_t i = 0; i < sizeof(packed); i++) 185 | duplex_byte(state, i) = packed[i]; 186 | 187 | Similar loops can be used to store or stream serialised state directly. 188 | 189 | 190 | Implementation notes 191 | -------------------- 192 | 193 | This is a straightforward vector conversion, trivial to check against the 194 | reference Gimli and Xoodoo implementations. There is no manual unrolling of 195 | loops and architecture-specific intrinsics are not needed, but compiled with 196 | -O3 -march=native using gcc or clang on x86-64, this code is as fast as the 197 | optimised SSE3 Gimli submitted to NIST and the Keccak team's SSE2 Xoodoo in 198 | XKCP. Modern gcc and clang compile vector extensions impressively well. 199 | 200 | At the time of writing, duplex.h runs about 5% faster compiled with clang 201 | 13.0.1 than with gcc 11.2.0 on AMD Ryzen 4800U. Byte-by-byte duplex calls 202 | achieve 66% and 62% of the throughput of bulk encryption and decryption on 203 | gcc and clang respectively, rising to 99% and 96% for 16-byte operations. 204 | 205 | Even with state-of-the-art compilers, vector types are worthwhile. On the 206 | same AMD Ryzen 4800U, when rewritten as a loop over four uint32_t columns, 207 | permutation takes 30% longer with clang 13.0.1 and more than double the 208 | time with gcc 11.2.0. Similarly, a simpler byte-by-byte absorb/squeeze loop 209 | sacrifices around 20% bulk throughput with both compilers. 210 | 211 | Although duplex.h works on both little- and big-endian architectures, it 212 | will refuse to build on a mixed-endian system even if you contrive one with 213 | sufficient gcc support. Both duplex_gimli() and duplex_xoodoo() explicitly 214 | shuffle uint32x4_t rows as uint8x16_t vectors to optimise byte-aligned 215 | rotations and word-exchanges. This improves Xoodoo and Gimli throughput by 216 | 10% and 20% respectively with gcc 11.2.0, although the performance gap is 217 | much smaller on clang. 218 | 219 | For information on Gimli, see https://gimli.cr.yp.to/gimli-20170627.pdf and 220 | the documentation at https://csrc.nist.gov/projects/lightweight-cryptography 221 | where Gimli is a candidate. Sponge and duplex constructions are well-covered 222 | by https://eprint.iacr.org/2011/499.pdf including generic security analysis. 223 | 224 | For information on Xoodoo, see https://keccak.team/xoodoo.html and 225 | https://eprint.iacr.org/2018/767.pdf - but note the Pocketcrypt duplex 226 | construction is the simple one specified for Gimli rather than the more 227 | complicated Xoodyak cyclist object. 228 | 229 | 230 | X25519 231 | ====== 232 | 233 | This X25519 implementation is adapted and extended from the elegant code in 234 | Mike Hamburg's STROBE protocol framework at https://strobe.sourceforge.io/ 235 | It is very portable but can detect and take advantage of 128-bit integer 236 | types where they are available. 237 | 238 | Prototypes for available operations are in x25519.h. Code using them must be 239 | linked against x25519.c. To use the library, copy both files into your tree. 240 | 241 | Both curve points (represented by Montgomery x-coordinates) and scalars are 242 | manipulated as 32-byte little-endian arrays. The correct-sized array type 243 | and standard base point (generator) are defined by x25519.h as 244 | 245 | typedef uint8_t x25519_t[x25519_size]; 246 | const x25519_t x25519_base = { 9 }; 247 | 248 | x25519.c assumes constant-time integer multiplication. This is valid for 249 | modern x86-64 and arm64 processors, but variable-time multiplies on some 250 | embedded platforms may introduce timing leaks. 251 | 252 | The library currently runs faster compiled with clang -O3 than with gcc -O3. 253 | For best performance on clang, build with aggressive function inlining using 254 | -mllvm -inline-threshold=5000 to obtain 30-35% faster code. However, raising 255 | the analogous -finline-limit value on gcc appears to hinder performance. 256 | 257 | 258 | Scalar multiplication 259 | --------------------- 260 | 261 | Multiply an x25519_t curve point by an x25519_t scalar with 262 | 263 | x25519(out, scalar, point); 264 | 265 | x25519() returns -1 if the product vanishes, otherwise 0. This is used to 266 | detect non-contributory behaviour as described below. 267 | 268 | Generate a key pair (sk, pk) by randomising sk then calling 269 | 270 | x25519(pk, sk, x25519_base); 271 | 272 | to calculate pk. 273 | 274 | Similarly, calculate a shared secret corresponding to sk and pk by calling 275 | 276 | x25519(key, sk, pk); 277 | 278 | x25519(key, sk1, pk2) and x25519(key, sk2, pk1) will generate the same key 279 | for all pairs (sk1, pk1) and (sk2, pk2). The computational ECDH assumption 280 | is that recovering this key with neither sk1 nor sk2 is infeasible. 281 | 282 | Shared keys have high entropy but as curve points they are not free of 283 | structure. They are safe to absorb into a duplex construction or otherwise 284 | hash to obtain unbiased bits. 285 | 286 | For an overview of X25519, see https://cr.yp.to/ecdh/curve25519-20060209.pdf 287 | and https://tools.ietf.org/html/rfc7748 sections 4.1 and 6.1. 288 | 289 | 290 | Small-subgroup confinement attacks 291 | ---------------------------------- 292 | 293 | Some protocols require that neither participant has sole control of a shared 294 | secret. However, the curve has cofactor 8 and its twist has cofactor 4, so 295 | there exist a handful of low-order torsion points which generate at most 8 296 | distinct scalar products. An attacker might submit these as public keys. 297 | 298 | The simplest way to detect this non-contributory behaviour is to generate 299 | secret keys as multiples of eight by masking with sk[0] &= 0xf8 after 300 | randomising sk[]. Such keys annihilate any torsion component, so if x25519() 301 | returns 0, the scalar product is non-zero and the key exchange was safe. 302 | 303 | RFC 7748 itself specifies a clamping operation on both scalars and points, 304 | implemented as clamp() in test/known-x25519.c. Alas, rather than framing it 305 | as part of key generation, the RFC bundles it into key exchange. This is 306 | unfortunate as it is not well-defined modulo the base point order so doesn't 307 | preserve group structure, causing problems if (x + y)P = xP + yP is needed, 308 | such as with Schnorr signatures. It is not bundled into this x25519(). 309 | 310 | For cases where the scalar is derived rather than generated (perhaps from 311 | hierarchical key assignment) and where the group structure needs to be 312 | preserved, a cleaner option is to map it to a torsion-safe representative. 313 | 314 | Given any 32-byte scalar, use 315 | 316 | x25519_scalar(out, scalar) 317 | 318 | to cheaply calculate a representative congruent to the original modulo the 319 | order of the base point, and so whose product with valid public keys in the 320 | prime-order subgroup is unchanged, but which is also a multiple of eight to 321 | annihilate any torsion component under scalar multiplication. 322 | 323 | None of this complexity is needed for standard Diffie-Hellman exchanges, and 324 | wherever possible, it is preferable to design protocols that do not rely on 325 | contributory behaviour. 326 | 327 | 328 | Scalar inversion 329 | ---------------- 330 | 331 | Invert a scalar modulo the order of the X25519 base point with 332 | 333 | x25519_invert(out, scalar); 334 | 335 | This is typically used to remove a blinding factor from a point in 336 | oblivious pseudorandom functions. For example, given a compound scalar 337 | product rsG, further multiplying by the scalar inverse of r or s will 338 | recover sG or rG respectively. 339 | 340 | 341 | Mapping field elements to curve points 342 | -------------------------------------- 343 | 344 | For any field element, call 345 | 346 | x25519_point(out, element); 347 | 348 | to map it onto a curve point using the Elligator 2 mapping with non-square 349 | parameter u = 2. See https://elligator.cr.yp.to/elligator-20130828.pdf for 350 | more details. 351 | 352 | A point on the full curve is returned, not necessarily in the prime-order 353 | subgroup. If an attacker has control of the input element in a protocol, the 354 | earlier discussion of small-subgroup confinement might be relevant. 355 | 356 | The map is efficiently invertible and its range is around half of the points 357 | on the curve. Assuming the probability of efficiently calculating discrete 358 | logarithms for random curve points is negligible, the same is therefore true 359 | for the images of random field elements under this function. 360 | 361 | This is a building block for hash-to-curve functions. For example, if two 362 | parties already share a secret duplex state, an ephemeral key exchange can 363 | be authenticated by substituting a secret point for the standard base point. 364 | Squeeze a uniformly-distributed x25519_t field element from the shared 365 | state, map it to a curve point using this function, then pick multiples of 366 | eight as ephemeral key-exchange secrets to avoid small-subgroup attacks. 367 | 368 | 369 | Signatures 370 | ---------- 371 | 372 | STROBE-compatible X25519 Schnorr signatures are also supported. These are 373 | different from standard Ed25519 signatures, but they minimise additional 374 | code expenditure in protocols based around X25519 key exchange. 375 | 376 | To sign a 32-byte scalar challenge c with identity key pair (a, A), generate 377 | an ephemeral key pair (e, E) then call 378 | 379 | x25519_sign(response, challenge, ephemeral, identity); 380 | 381 | This calculates the scalar response s = e + ca (mod l), where l is the order 382 | of the X25519 base point. Discard the ephemeral secret e. The signature is 383 | the 64-byte pair (E, s). 384 | 385 | Given a scalar response s, scalar challenge c, ephemeral public key E and 386 | identity public key A, call 387 | 388 | x25519_verify(response, challenge, ephemeral, identity); 389 | 390 | to verify the response. This checks sG = ± E ± cA and rules out torsion 391 | points. It returns 0 for a valid signature, -1 otherwise. 392 | 393 | Schnorr challenges must hash the ephemeral public key as well as the message 394 | to be signed, because the prover must commit before the verifier challenges 395 | in the corresponding sigma protocol. Absorb the ephemeral public key on top 396 | of a duplex state before squeezing a challenge to sign that state. 397 | 398 | The signer's public identity should also be absorbed before signing unless 399 | the state is already bound to it. https://eprint.iacr.org/2015/996.pdf 400 | shows that multi-user attacks against key-prefixed Schnorr signatures are 401 | no easier than single-user attacks against unprefixed signatures. 402 | 403 | Signatures are malleable: if s' = ± s (mod l) where s is a valid response, 404 | s' is also a valid response. Similarly, a valid signature for a challenge c 405 | verifies for any c' = ± c (mod l). As with all Schnorr signatures, leaking 406 | or reusing an ephemeral secret trivially compromises the identity secret, 407 | and more generally, bias in random key generation across many signatures 408 | will leak key bits. 409 | 410 | For deterministic signatures, clone the duplex state after absorbing the 411 | message and public identity, absorb the identity secret on top then squeeze 412 | the ephemeral secret, in the style of Ed25519's hashing. Discard the cloned 413 | state and continue as before, absorbing the ephemeral public key into the 414 | original duplex and squeezing a challenge. This eliminates the risk of 415 | reusing an ephemeral key and the need for unbiased entropy during signing. 416 | 417 | See https://eprint.iacr.org/2012/309.pdf for details on STROBE signatures, 418 | and also https://eprint.iacr.org/2017/518.pdf for the qDSA scheme. 419 | 420 | 421 | Secret sharing 422 | ============== 423 | 424 | A simple constant-time implementation of Shamir's secret sharing scheme over 425 | bitsliced GF(256) is included, allowing 32-byte keys to be divided between 426 | up to 255 key-holders, then later reconstructed from a sufficiently large 427 | subset of shares. 428 | 429 | Prototypes for these operations are in shamir.h and code calling them must 430 | be linked against shamir.c. Copy both files into your tree to use them. 431 | 432 | 433 | Splitting secrets 434 | ----------------- 435 | 436 | To generate n 33-byte shares of a 32-byte secret, call 437 | 438 | shamir_split(share, index, threshold, secret, entropy); 439 | 440 | for each index = 0, 1, ..., n - 1, where threshold is the minimum number of 441 | shares that will be required to reconstruct the secret. index and threshold 442 | must not exceed 254 and 255 respectively. 443 | 444 | The scheme's security depends heavily on the block of 32 * (threshold - 1) 445 | bytes supplied as uint8_t entropy[threshold - 1][32], which is used to set 446 | random coefficients of a polynomial over GF(256). Randomise this before 447 | first calling shamir_split(), leave it unchanged for subsequent invocations, 448 | then securely discard it once all the shares are generated. 449 | 450 | Flaws in the randomness of the entropy (or leaks of it) will compromise the 451 | secret. 452 | 453 | 454 | Reconstructing secrets 455 | ---------------------- 456 | 457 | Given a quorum in uint8_t shares[][33], reconstruct the 32-byte secret with 458 | 459 | shamir_combine(secret, count, shares); 460 | 461 | where count is the number of 33-byte shares provided. This will silently 462 | generate an incorrect secret if too few shares are supplied or if they are 463 | inconsistent/corrupt. 464 | 465 | 466 | Testing and installing the library 467 | ================================== 468 | 469 | To run the test suite and basic benchmarks, use 470 | 471 | make test 472 | 473 | The easiest way to link against this library is to copy the .c and .h files 474 | directly into your source tree. However, an object library can also be built 475 | and installed along with the header files. Run 476 | 477 | make install-shared 478 | 479 | to install libpocketcrypt.so into PREFIX/lib and the pocketcrypt .h files 480 | into PREFIX/include/pocketcrypt. Similarly 481 | 482 | make install-static 483 | 484 | installs libpocketcrypt.a and the header files. The usual DESTDIR and PREFIX 485 | variables are respected, as well as CC, CFLAGS, BINDIR, INCDIR and LIBDIR 486 | for more detailed control of compilation and install paths. 487 | 488 | 489 | Copying 490 | ======= 491 | 492 | x25519.c was originally written by Mike Hamburg as part of the STROBE 493 | protocol framework, and is distributed as Free Software under the terms of 494 | the MIT license by Cryptography Research, Inc. 495 | 496 | The rest of the software (including duplex.h) and this documentation were 497 | written by Chris Webb and the combined library is 498 | distributed as Free Software under the terms of the MIT license in COPYING. 499 | -------------------------------------------------------------------------------- /test/shamir-known.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | 7 | #include "shamir.h" 8 | 9 | const char *secret[254] = { 10 | "13f5e8e8f583206ba332fa117a5f438fe4d1bb16d6a5b4812817e2fdde68132e", 11 | "3c3d34c45bc6aed8e3922090bfe501a84197d123d14eae3c767b07930f9da7aa", 12 | "66f8137aa058aadcb92413cb15a079f1ee4490bf8ec85581d8aa64b650b643fc", 13 | "03193c3faf8ed9ee2ad692244f78a2fad2b718e8809aff0fc957868d886d8ae5", 14 | "557883dc082a313fdc93aa8a6eb68dde28cc510f632c9a5dca47afcf90fa1c6d", 15 | "2e9c73a3414eeb53f04efddc2c443afed996c36fd9539459067e5a2ada7f692a", 16 | "757b82e82dcdd0325ed333651e7907b892d0eb7de6b77efbaf3c62c51e72d4be", 17 | "4d646beae7e561c0e6a687dfc2882030581e85f17c100f21f74fccd6943d6c92", 18 | "9b045e1366394ad4569e3bf9d2c92c1a5d3ad18a297cda05a768e58096475518", 19 | "1806e2b262248ea1565a2e87bfed07838a3282d2f3fd673da110b8ceaa41750a", 20 | "b428c37c2e6fc08fcf27590ab10b6e14fc1775035469d202e64e83742cc97411", 21 | "14b955a9779ec8d96cdb72eb3176e7699892408f9738e5a219f887f05fdd31a7", 22 | "8940838a659fb2ee193eb043df513e9ebe044086810079e61743d07a51172aad", 23 | "7ab2bab0a0f3eb1b9d71d4c3a0059bdf7bc7fa87facc4263e26b3c87057572de", 24 | "0ac7c2583d1a3658c190bdd77e7b16a19624e5b900209075bd8879ff8b9f862e", 25 | "567cac361e72a456a51960ff02c2824401523e38fb4839a5041d9a6e4f2c214c", 26 | "993cef3d9eb0f0832fd7184d883269ef9643bd5fb3be69f0fea917e7645fc95f", 27 | "8d0a9e63305a7fac89b714d5d892ec36a0383ac430e7112f7e17a4ea65e2afe0", 28 | "0b0e00354511272663e4bdd37e9ce6ae153ed88ac1a9129d54c89b3e286945cd", 29 | "d393bd722f8e9cf46a0e2f8722f2edc15a8916a032ec1588b3535984ba2c8cf7", 30 | "ab348af5157c1974e82c37f9be242fcc387e423ab0ab84cdd22e82ed96f676f6", 31 | "2ece7ea29d62188db0732accc31c72e1bfe749be6046cb2300216651c966621c", 32 | "098c70d73587d4c0770ac6acf87b29ee047fbc7f82ec51e5477473b178e1c5f9", 33 | "e362b9fa2ebfdbfb2a934a3a191fa4e25cb7b136cf12c3364fc86d55a8c5adbe", 34 | "c8f5db8eb73687bcab266a5a2bce1bddc1d33d3f0b7b7488440b8f093ab96ed1", 35 | "8bd8792cdce1aa3f39dd831e515b2e63a13e971541a8dc72f356e595c6991ac5", 36 | "b66303a669707c2030de11f8d00aefba67fe19fb2996a960b3b8b074d4afc9b0", 37 | "86cbb9133e48959bff5eee12694c9508b0b0920a8b01c6cb69b01e4956c372e7", 38 | "bc02cff82e86c751f33d99f19c557aebf25f2879e11cb08c9f5e76ee81e9ce33", 39 | "b3449875049c4c93903955649b96d84456838d8f46f4242d68a56cf600c1e212", 40 | "17e5ff944d021b895e42ba850d4c07dbd1ba4f4772152e5ac8df94830ce1ce2d", 41 | "b865438a62fc099e2a10421bea02c1881c210d9926581612fbde07f87e0d9118", 42 | "b444bab9d974e70f259d76b220cea4a90281355cc4a36a2987656945f7a281f7", 43 | "fbdbcac8393cde189a841e0c49696c2f4b9276b89472acc21d17efbef597822a", 44 | "8ce1c4e22dfde32709630d38591b4c28b7d5462ac9d91893760e180d5c29a3fe", 45 | "8c0d2c047a0e21b7ae32fc67ff1496732d244569578c9dde26cd110e0f26a917", 46 | "a5fa649dbd3d59cb41a59ae58f7479d84729fec5e1201b0e14bb86076405d73d", 47 | "5507b3e4f046e176fcc17e8ce900429c4f09ddd5eb69731cd318c9455ffd00a0", 48 | "3416e0104ff7b10d757e3b36777f16c13b6ac30b8c31b196b16141223008957e", 49 | "56080db64170c0aa0296cd33421ebedcb3872c9a5f9986010786eb6266db171b", 50 | "234fd62cbd2e7d194ba0d42d308bf30175a045051d21106eafaa53d7b920f7d2", 51 | "9d8255e2a75ffa848feb8cd800c00d471ce227826b5ce31ff94754973d552d34", 52 | "a46d638609dcd08ac570d1d01bd7087c397b7645a7173330ef8d689bca94cf56", 53 | "46473428b7fb0b295f151cbb284c7c4382bb7fcde492db1d2290dff09224d419", 54 | "599d8e9824dbe3f29197b9df2813dae1d55b952ea9b3222198c0474b7132d5f4", 55 | "62ff443a5d1909d49e186ac50665a9e2e221dc4c30bd82d58da0acbcaa6ea81e", 56 | "c2ee022eb9bf0c875a25986940687e00c04b6239723cf37408afeed175fcd01f", 57 | "6f00fab02551851cc0fd6da88fae8f67a3be993b0c4a55fbdfcd50f547180e13", 58 | "5c4f9adcdf941a7c6120c49a6aec2124943eed455cb8f5410051d47fccb1b554", 59 | "88352da0810efc548cd43888d36f75440af15956db82d86ba0c2c273e27ddca4", 60 | "60e30bba4c1e16f58a634ef15898511499af5ebeebcde387962fa6adde81c342", 61 | "beb9c31ce133a9919563639353d619045ebf7804d4d433e3d5adb9a636be8432", 62 | "5e7b5de24a8f94034c4637c8d9369f5c7c5b752464985da40c45ab0f7afb89ec", 63 | "2633a6c1ba54ef7c1b1da453b4f627be206f3fb95f34be12e8078690ccd172df", 64 | "b38789d8917d4c3509f1a33bfd91b07ccc27d58fe187718973e0f7b767c7625a", 65 | "75e550719f59dda291a647745dfd33e253396cd9c8d9d5da02bb33221e60c0c1", 66 | "49050c6f1523e3e3501d21a564091f9082d9ea66ca5d6b8a1c634a5274d86c11", 67 | "02d437f1f88a2e9ba7a2610157c079151e1dc1496a5057c3cb6f94674b20af3a", 68 | "e20828e80921f2f91a44c200bfeb950ffb08bae658430f68b99c79f5b5f3f12c", 69 | "b3d8caccac110b5342808b03be46bebbeff8312b73c1c91e1430b1a574b113db", 70 | "a214267374c63859c42ab7442f996eea707a915613bc2f4ef2867275f8d665c9", 71 | "33c348f8ece3d6b4eac00bb4feeef7fea270d30bca2086b3be8ff03627f333ea", 72 | "d1390fbac67c7f90711d34506029e4c4636a5450da620ad10b297b316d6cb290", 73 | "7d6b809ac3123b639b99a62100261af05819de7f258b99a26fd723280ce40c02", 74 | "7b548241b167db56526510207458b28bfa9a1a0ab0d7d1714b7e5e9cacd122e9", 75 | "defd244efebefd5c9c0b3727d2813d4c985a68899762f0a42954b57e043d7515", 76 | "1e539528cca398c2e5d8ca226f4e4a6c8a6f1c996829313d67c62bc6a1371c75", 77 | "abc0aa790b08dd17c395b9571b2ff399289cca799ff7f230e7a8f3ed19d62f38", 78 | "aacf4be4465492877aad601d191893c2707844a36fc240b3a1a288cdc59db298", 79 | "f45fd0c58c535326e2e2a3e2c2e5a83baa149d2aa56272c8cd54c87f0f200bd9", 80 | "3cb2de54fcfc41468f7d09a3d875860e0fb0be9b02f91743d8f50cdd2f02fbce", 81 | "79b67ac229ec636604e3b3f5a4e0b9f0e85a0bce1ef3c18335a86346c6b95588", 82 | "98c5321d8d5211e4d2ef64cf02f84926e196f1bbdcbea7267a3a186d2a0190f4", 83 | "5186c46167f52ae463ca13a160d86a8281a108561f4a3464523f57b5977b8818", 84 | "3b39a5636f6104051ad85b7ada311c3fbf252de9e887ac7dab0af4fd01f2db4d", 85 | "d2b7449b1a0d735fae624d139b442558133ef0a8135d2a97cf309126b1bbbf06", 86 | "6819f0446a7791f580c28ba933639d3fa1b6068feb2ec0aecb2248d8bf0cab07", 87 | "1413b64dba14445beabb0bf5dabc68624af3cd4fbf306b158d8a37de261438d7", 88 | "3d9f59830ff9c93938109a0306fbe2f21765fe271ed54d2542bffb4b36b96534", 89 | "02a827cca02c440cefcba32d3f429ab703ff13bf2e91bb68491d7fd325d1a983", 90 | "96a45750b74b4da4bd2a0550c7d5b4510653e96201fb190bb0816fefed486700", 91 | "ef248f466cb6ce8f9fb2609bd8bb9ca5c9ca47520f34f42ec4a1845a69494409", 92 | "f21c30a459a4201b4b21b2135cef6fe710f761f9157c53e0f905efbf79582eb6", 93 | "9fea44aeb9eb99325f3d61a4e42b4fe02a9c78089836320c02906f3728fbecb7", 94 | "c7ec5fc771dafbc7687fc74599a6bb22efe009dd78447658a1f3dbc8c462fa01", 95 | "340f00b017aff86d561163f1fdfd75c0fdf076f260bcbd274867ef2b7fee6c48", 96 | "eea9c61982fce7ed9033001935a9b9d1d23297d44610e130ec20627346e50612", 97 | "3c412aff9da430722bc71703b814ee272d3c3eb30e2cd46eb8646bcad72d2613", 98 | "71d58a41b195c6718a018fc8106f227a936bb95e1eaadd45e6721cd979094376", 99 | "3bea236fbf56575853e3d6a36d37bd8c8f34a0519dd53a086364183062410785", 100 | "65fa7ed79b27ebd0b08db6670179e4fa20604b4eb92b8cfec65c3b87be4075d5", 101 | "a5c0df47ddcd9d3edc72723c08fba0ba68f6606585abee7536df1aa775461bf7", 102 | "2485cb0864a5d87c5be1a902a42f0b8deef41c7808be2ffddaea61c3c3de58ff", 103 | "c38fcc80f23a9e27a0ad98d62db365a62bfa4d8ec961213e667de98117b361da", 104 | "fe043adb62dbc263c3c7d1b3813a21683379c8d19bce79edca87df1339e75a25", 105 | "6f0eee91fee82217f230bfe11d43b268995caa00e6a3b2730287d471a4ed2317", 106 | "5e453c9754186e0654ccd84a101da579986618f74fc0c2168f945c8b7592c839", 107 | "5d2afa4fafdfed75282d5ea1a0d02aa83a0062395b6256e526d3780bf1eac807", 108 | "14924078d4e64fdedc6a8cd8036ddf18082e1f688e6bb2b1d35ab92e559ae1ca", 109 | "580bb59a5138f30833fe4cb5b9c5a9921b2b39c7e6c1a341c76946c4738781df", 110 | "4b122e01a5d06f5ff0e85dbde631fadc62795896e20918742be8e244f6126f5d", 111 | "1d816ab910e538a9c940fa4b5294c5b0b111e44789e845c7b236875cffa79815", 112 | "2806e41c33fe85d13676b2ad8e048fb8bc9c6a188d0193f758bd040f998670bf", 113 | "8a0d37a0ccebe053f824585bab80618e20cd9f7481a8e004110b46852974584c", 114 | "c907d2fe9711965a48c0e70ddcc24734677c0c36598a98755b594f4d4ad8216e", 115 | "e74aaf0ba5f077fb4cb5e9bcd0695024de4cc06af7db1924f798511bb1ad732f", 116 | "49088749d4f3626b63d01d57fde6742288b4590bd221da8d0ac7c90f628c571c", 117 | "501cc2f1a4d8037a0ec1372ed723c248baa7f0bf64259d46e52a5c5530f6364e", 118 | "044e80d73c82b5b706a23f566047e65fd1ddbdb1829050d837792f4135b2f243", 119 | "0012d8c51c4fb4449a9ad2af0cdb5ba2783006b83ef4fd220cbaea5d52a0572b", 120 | "ab41687cda80f10a0bb2166dd8a2760151d0f40a199570e103c3f0260fd249e5", 121 | "f0eb7d53717720e3d6f1981770c0ade9d9db1452272db9c73b2121a7588d8cfc", 122 | "3b3243faee2375a842b329c53d00f5d57b76f71f876230ee4cf6d958db0a1863", 123 | "b2bec9e087944296f19dfa611058ec706cee24f7cbad0a6499c439ceb0ea60e4", 124 | "0617caa9422c9bffa17994859b0636e3cf588de85bf807f3a9416ebb6c2b22e5", 125 | "6157480eac44b313846b736cc57b2b88c0c3789afdb2a7d3f5a0f8dd44505bd5", 126 | "e1b7ad43ad40d3442a4285d11d4b94e9249d46488fc5e2c493cf9408e4e2f423", 127 | "d5e4ae7d5939d0924ff71e5329d6383b6e616ba7bcab60510bbfa7eca32a2f56", 128 | "1aa6be9bf372f8621be925ab533a45100b0e9eff8b635e02c10ba619c34461b5", 129 | "6a82cb738f92a860aab4bf2dcc07492f1ef761dc351aae05448e18ecfa3464c0", 130 | "3de005ba67dd255125c259e3892092dbb430b289e97b79146a8c7e3d2129b6a4", 131 | "e9e4de585c6fa0193402bae762d7b164760c3dc1306ff15962ad92d423cdfe46", 132 | "109e908413ec2f13dd16d2a3f13128857170bbd2e66c78f24a70368ac222d204", 133 | "a251546628f637c37f8dcd881af6fb485065cd31eff7de00dd598413a98a9e22", 134 | "d7ec7922924b2e615d0cd5d2121887a3a81398e32095acf30218916a5f1f5648", 135 | "730ebffdd04a4262d60f4058b9ae3317f0fb159a827150ab31cb5a62bd739733", 136 | "d4648488f98266374a7e2e3fddfd4e4adf3265da1473e4fbb79561ac963063a6", 137 | "b8223a735a93327c0e965eea89b82635ed64061bc7e7ab08677abb077e1a90d7", 138 | "a5af5367521038a1f3d20a2ae439c548b9c2a456b7b7897665403976f99bc644", 139 | "db9e5e6492054c17dbe8de18e2fe575ddecd9658c4c74e79e16fc14e484bdc6b", 140 | "2637a8a842e61ecae0933c2492885c8dd7e5d5e59cc2188fef09721e27e159f8", 141 | "16f24d7d6da731aa0439ea2387c28d1aa60c5b0b3c792453d776177376cf0db6", 142 | "11c00abf0751cb04c047fd45c6c27b4445eb9dfa49e768d66af72fe91d526dea", 143 | "f2dab76a0fdca40425cbbea01bb0ff8dc67a1357c3c2b0831110c01b49bc62b2", 144 | "0e81c8820edad7c5012cd2015ee862414d3476558b5fb6860ed924089e800efd", 145 | "b0e09dcba86fa74b7e0631a5fa0a3e66e048befc96a3349e14ac38e11fb73712", 146 | "c4ceeee051b18281aa54af27873851ce5a9f2321f8815ed28d35c0ab53038ea6", 147 | "785293c2fa4beee2d91e801c3a21c5ad6a86a4b2049b041133c5d9442f1b7e6d", 148 | "bcaad78a4709beab0f742849e155b8704f071566ba9d47d4da938d1f0409e92d", 149 | "c68e93da5223da441c6de153cb574cc5ad9b5fe0bf4bc450fa810237eaadb684", 150 | "bb8c1a6610d0b2da6bfe9b75992796f4720baa6e44c4c179ddf606be59251054", 151 | "860c0aa96bfcae5883fd67b35954a9a1bdaf0d4d20fb08bc1fdb0df14ab8961a", 152 | "c0d682e13490f3be35f7577201022e939494dd634f46d7e2fb050ad26936d034", 153 | "f5f28291ea475e12543fe500f43b038cff41ecc3e0815d727263f3a93d55c12f", 154 | "60eaf57d40e0db2dcca61389b681755e90a12c45d643eb1f3e4325a1406eb934", 155 | "5445a8ae7aec52352d2a7b90e963ccf392ebf5f78b5761ac124282435a4a2d9d", 156 | "617791a28af3d706fdd2a2df76aae9aa8b4f87c1bc9286c04c1bf79dce0ef558", 157 | "ef380504c39756b6df19c52c7fcbc9164ed271f8bf70a8dc2a60cb536f4937b4", 158 | "adf61f68a7efde7fbc3ad141c701caecf17d63768bb1ec542304a3ba56bf2a02", 159 | "02d7cb56c996da956d9a392375076fe28ccf884e5e05b6286b9b3e38f57401e1", 160 | "c31f3cca19e4ebaef4c1438ccc339725eef2f309ded3cc62bc4325172e4fe613", 161 | "43f33246174126ad03bd65c99a438de1f9d347c93acb5a156704cd82695fd1ea", 162 | "6a831228da6108c46c4dc8a6de4e32e18c428fc9e61708baad75730cd1f7004f", 163 | "771b9a1f140dfdf103f3850f8ad5e0dba50e3fd2eaf44f1ec14377827c0017d3", 164 | "676d14cb5cc8f7d017c537756564e26505c34a32416a97a00f46ab80c9f415be", 165 | "5156146f8650edbaedc945abb673cc47ade97ed36bf4e68bc462558ed8864600", 166 | "b13debf6f7302f31071fda4e8b960196b678c85dc5ebed225828ab26f48f890a", 167 | "d4598d28ab3037e868d5e74bebcf5c7aad27dd1ce58d8fca0fca05f1266df214", 168 | "8975a41cd6b7ecfb5d946ebfd3fdd58bf629b168186eeae26227925c656dae87", 169 | "d95fd9820c2ea9adb8d27d7d7647677873d91d5af79bc8018d2f7fc8ee00aba7", 170 | "aa779214c8b6244e6fc1899725edd1b9e75a265ffeb2a6fb023ec95661a51d7a", 171 | "9de206ed262284d5975cbdaaca0d275b05e5a45b44134b786980d71b29ad6626", 172 | "cc074925b70df5a0715a97d44c3f31dc86899c13daa2431d5416821074a4c9b1", 173 | "301921f6f4240e11da11fe36182598f653b54896b26078572d5b5e6d5e7f2da5", 174 | "3a009240340bd42fc7e71b10f5530382921222807a3e9ac91e2caef6b3e26088", 175 | "56fef7043e3282315250cdc174053e74340d983832d4e346e6cfae5d55d91c90", 176 | "4c16f6ece1eddfea48721cc6695b01b60eb5fd2ce30452ca8f242aabd712b507", 177 | "4daae2a9ccd388ab64bfe3fdd26074ff8f03475ce48b714504345ffab4f7bfdd", 178 | "2fde8b5a219d9caa0e4c0257fed61436bcd3b858a7ebbca2fead40382ca988e7", 179 | "585ddc56bb1b18b0eea222a5594cc3ce20d1fd1e7075454de6d84eebd06af01a", 180 | "ac55cad84ff5ec48b83e45f802aa9c15d9f2efa21442040ae9dc656e5df53a4f", 181 | "b505cee3adc0d5b37d4d19fcab637b5095146dcf030ad477436f72716a57ce7d", 182 | "8717e3ff79efdfed170e9185f2e017b4b84ce14ee4c246f20ff4680477b63834", 183 | "4257d24d013eab3a916c97a0370213e82f6635740bc6d5b24c4a5ebd8c98983c", 184 | "3cfdf698ac3eec3ba0a22816eeecd21eea7b05906d87f89cf94feaa9f62266e2", 185 | "319b195a8e0c2902bfe1a2cafd18d6866f4f6a61e28b50bd6e8bf6a77aec750e", 186 | "7627e8ac04b1fea7ad61dce8546ffe61356af3473436d500b1e430fa4dfc159a", 187 | "b5f8fa033268d07f3d2d441e031219cb379f27268f187f1e2bcf2436f4201f2e", 188 | "69383724fc8e854914bd5522fe49c47de97dbd911a5180222dde4e88e1c5bfe0", 189 | "33daa522f82d50634cd0205d977d15f5f3e100640a5af9a469313f89a93310e2", 190 | "4150c98d9fd3918445b288aa6a7048961ff970b5e8832ebf63cd6512368a5670", 191 | "c53c3e9bf1a6c01ec6a7e94f7c0b073e80cb833194e6972411055460904896c2", 192 | "95589bc65613f5e83566b2af209dcc06a9078f54a4a6c7e2161e2eb4b6d67c44", 193 | "178053bda43a49d09b77b4f857c0d926367c3b63a7bd120d6220b27504ce4f18", 194 | "6ead8435c8ee1095c2796555ca66c7a13c34c292281dd8ea53b00a10ea747730", 195 | "b3bb97412d13bd00177761edb7b87f83b39858084676d6cb38f00bf870f9bfc6", 196 | "7a0c49213a7777f92820fd3fcb9d31c746ffc61bca2fca73136ba7b7ff9ccb90", 197 | "4e7e693f2bf2efc2d24490f8dc274acc53da1d5008690dd7a67e82149b111000", 198 | "2f1fe645894149a4d8040ed9f5416d368d8aa3a6bdfea936b2050b331d489160", 199 | "e30c810c47dd73d23b329d032c196ef0b02af7ed00f3b8a979e360ad5016930a", 200 | "309a250ac03a205b1870fba6d10844250cf02e04a4831d8c20d568c62ac3b731", 201 | "f1da47c44cd755e6c367de66a4e8e9d96d0f2d95351331a24f3952d7ddefaa1c", 202 | "772cfc0bff7f8803b1f821be37d8692fc400178cd11ccae77dcce30765ab16cc", 203 | "efce093c86f2dc8da4136987ac9fed708cb36b3f5fa00611b8e53ec58d9611f0", 204 | "f26b0f4a400512802bb7fd33ea09b3a4b4ce384f8aeae3507e62680d1280aa19", 205 | "a1c17c52b6319039ea1b78ec0b29c9a8cb3255151740bfc42f89d4634694e3f7", 206 | "1a34422734034c5e83ba2f86eebd766e525ed466deac57433bb43f5bba797a1b", 207 | "40351a21e3ce4ce9f731db93e6a60e13d8ecab05718b81942c2de07b58018e97", 208 | "c4e4abae3371c6c1da825750543f6564de536facfdd6c50cf2effdddf6461e4e", 209 | "f73caae84d5986617de872dea759c888e90771c8d79456788692fb8477723535", 210 | "f69fdaf685b24968ca77371e4e9238c1a73e97b9faebac85611788d35d1d5b1f", 211 | "8a1687c05daa62d816fe8f4ea59d1b55dfb9ba0b8bdeb71fa2b857ff166b3327", 212 | "32a3f3aff3d2ced4fce0cb1d5c1d48603d8a178f1762bac14d67270b6e5e6c1d", 213 | "b438d792161ea9b6518c2eb4a749e8a049d17b71fe94e93b6d5f59e6ac0961ca", 214 | "303d903ef77044c56b6905b74dde66712123aab5d6767085ff15e1c1a866e2a7", 215 | "2547ee38d49b756229122519b6eed4ce5393c52c035737670d1ffa17675728ee", 216 | "be3e3aa506c67d6c4ca14cbd4acc9c19babf5273b04fd425b1f733e4625a1a03", 217 | "77f4fced36ad4b00030aeb049ab601da100027be94098387dad4fafbf10ccefe", 218 | "e9570733263ef5c89cb64ae7623a4a9229821a71f4c9c120d5658aa66c9ff907", 219 | "279f09b7f8c0a733c662cd212be21c3618a971a1f76e69bd91514aced522140a", 220 | "24407373b47907d9c05753972fbd73eabe310c108aa501fae87f05b4db070aaf", 221 | "8291b2098beb19931069fb80d2ec047c28b85b0de556ce0baab3897e2d52751b", 222 | "7d36927b76fa119141733f52f9ed40419e4c8828a802f35395913ad2705a5d34", 223 | "57090dbc954964ef4e2cd5a42055e85ef932fc3804f7fd716ec51383680cc05a", 224 | "998e390b705cd304331049800c2bcb4e1c0cd5177122058f37a223d6ab636713", 225 | "84ab745e4383b335cb596fbcadb24d6a179970c5d75fee4afbb217db6668e0d5", 226 | "57967663801903e7019298798ecc93d2136b34e4f3bd99333aee1d229bdd9c68", 227 | "c4d7d35a6ea99c4c9d8d934977b620c6c7d7ebfbaf636985632506879d9baa1a", 228 | "e8c12933d5b1bbe211f3e59b8b539a7212ca3115a696bea16ce07aecde9fda3b", 229 | "8b2d5f0adb73f49ca0e4afc3de8533bd3273596ab64708ce4d20e609295f9a40", 230 | "5f95c408a1de730528ec7e5a24608bd456d93a8cf787aadc39f55b3ee0fecfa5", 231 | "ec53e10100749cdef54d463b245f2837c126cd943a8a36472adc3da112fa83a6", 232 | "e84c84d5666d4818835522f390d307c73ef5935065d3dab3ab79999a73d89a48", 233 | "45ee458b43e17a3c4a8ae552de967442a44e857075ee3380a0730b49882f2579", 234 | "ef217a0dca67e07e57e733f78b5f1263fbac772a5b7a35b1c62dc09a160ff56d", 235 | "dbfc569e67bce0d8cd4f6565590b4570876544d4a124bfb18818eede1443ef67", 236 | "37c20d880dd69ae5e54fc0ed36f8be0be6bb2cef009654b9f00004d1efdf8bcf", 237 | "c063b7642af25d1187e1d82f9c156d8d352b3d97b4609de866cbd1bb57da9456", 238 | "f33049d1504627c97e6c4b3e89febd3aef05e1d84e87c6e09fef8ab1a5bf7605", 239 | "89b82394477e8b4f3c228167065dfcb1d0b18f71b232a51cdca723207b557ebf", 240 | "1e25638fc58f2e75326a90fcc15dbafb326fdc1f3b8c897f22c98157d7c07e59", 241 | "42b54bcc6fea150c005d152421e8b99e459b3c3eb44e22a09d72b4594abf59b2", 242 | "7d965f6a4159787f2412086c1096987a53f89fa1b023db7aed96f6e1e02eb3f9", 243 | "f94acfa3622d7391a246c5a56af195d12b354526cbfac4c2b11e4738e8406d27", 244 | "b909695a1acf62f8178c5225b735c1cea6460ed4ae807f6bd82df88287c7df4a", 245 | "ff65be48b45ba18b57267710fd914333f494c9bd8fbe12c4e6f30079519bb39b", 246 | "8e964f7e7eb1419bd7b1df812ade3d4d1cbdc26eda486d674c87c8242d33bff5", 247 | "38a1245d0374f4a38e08007b8af7fa945dd3773baca7c2d4b3b103d1e717e211", 248 | "8a4d00e77aaa06b66705b3d224f7eac73a4a879f0c531f27e7107868a7519f23", 249 | "93bd969684c69b7acfe9913bb2374f733a4bb21e5e6ff222eb7ab0a45ad1bd24", 250 | "9bc02e26916874ffbe9c4b7e2bc4ddb080ca10935819744ad0d5c2d22efe85e0", 251 | "1ae2cca315e95e4c8325a60c0034d75dbb5ba8bc0cff4ce297d54fc1c6463136", 252 | "bd0946ec6bf8c806e3575f2a47af4265d583deb10535565b2b47406875912921", 253 | "1f3fd1e689236e52b832357a7ec26c0c72b80e112a7764185f8784a0a86a41ce", 254 | "de8fd4c18b05f55cf1387b6d5483b4ea2c87db4460b3dae08f38d0051b508674", 255 | "72f67491ba77df05693557fb3061afe1f34564246f66d9a100fda1778cdcbc5e", 256 | "48e9bc598ffec4a25340570b5a3c0432ae32e3296faf11529ecd7120cd8caefb", 257 | "04df22139454e249aa7ac3b978a88e30920da4afb74eb3baf62b1555d588e469", 258 | "31ff9128bbd76c06b6a827c6fb555a70229fb5df747dfa383839704e7ab2b3e4", 259 | "d189d0f48480b3fade6509247ac5a5029839fe85ec67363069fbbea5d7ac3116", 260 | "5bb8702fc6bea9842a0b8a1628fb8cff198fe7f0d6198f10b41b36813f7ee05b", 261 | "d9060ddd94ba9f8fac3526eb94610ee52c4bb87855f2189e9c055ab15b7e031f", 262 | "3097722030d16b7129b3e40532a55dac44f3ae9e74943904913299b5ce5c650b", 263 | "093f14b77fe7a2dafb3280060dc63f02b0d84d04b7c16c5b76e8f9764e81fa6a" 264 | }; 265 | 266 | const char *share[255] = { 267 | "8013f5e8e8f583206ba332fa117a5f438fe4d1bb16d6a5b4812817e2fdde68132e", 268 | "6f7234f02bec8444e859a0bd69929f8c3a8b74dd251ebbe689ea2e77f1ef4e58ab", 269 | "c08e44fe06b210f22a84ab18359aea663f4f17f37a2432a9839d0363754743b6cf", 270 | "ab75f3ee7373ec12a6bc873f871f4f7824dc53b0d11551726db3e7f97173189dc5", 271 | "a692a43a64e27ed151b1ef9fab4d8900c670f730235e3a3da37fd97e7f38c872bf", 272 | "1b2a4d2cad63e746fa2587b8e05cfb52d75a25c1d1e626947a9ee313059ae8eda1", 273 | "dd5877790aa289482b2fc5631514818e6aa6377f60bff28451f3bbf005bacccae2", 274 | "d75c3f84ed5171cc25a9de97f5b30b2016e7ef80bc4fe7395f5122b05d47c2d3d2", 275 | "b6d5c8d3b4a7d7c6ff3b04a33d24a0b0cc5bae800c5c682b3b777fb673aa4ec6dc", 276 | "eb289f712c213fabf2cfff54ebc9a5c86d326d121f0ce66feceaa462cc84200934", 277 | "ae13006ce552f517d82bc3809751d17729a6c70994694008a546fb970c223780d5", 278 | "8e91a68f9b79d8bda49f711acc8dc8d0479a480cd0484c2742a2f4b1594d84c061", 279 | "77f9690d9f8e25010caf10f842d5fb072999e1f93b46175a2e09988d0d2eb57998", 280 | "ecea43a13a3431a4dc342660ab1555210f7487f977edeab370167d2fe160ecc427", 281 | "8890deefa6284f89a1ed9a6caef79832adee7a3e3736382161dc166ef068ca3966", 282 | "63024c7cac339dbc9ddad4585e7cac99c5b65af0488337e23d791d38d9265cd6c8", 283 | "19e82d021303b80ca76ab255b3fb1603b2d6b1bcf33b2035a1135d4239a948e402", 284 | "76493339d877a7404862a2c8083a9a217f81dbeec930fbf7a8bd3be95b9b869d62", 285 | "fa161690a53177be0515c47ff0dab9b5a4f7d32a5a46f36558fa55833b5a4d3cf1", 286 | "b443a1417952ac68dab35a3ce10e589128e67afc2f94372ef4fab6d8474bf2987f", 287 | "b8c2f6dced057cb5da06f519d2e91fc903f9d687a0f292ff182433c09817f6f24c", 288 | "a46ea3e219933cbf896bb0fbeb68397ecadc41ef4a98fb8e9c9653a0ef38c42ae2", 289 | "fd26ba1ac6e172528b6f53202643f9418c5f1d199008504c7440f1fd7a54b5f144", 290 | "a08c033e0b6ec07628122303ee8cf526957d262812203900b1f54b214acf3a310b", 291 | "3c7585956d769d322042b76c9f3e633187bfd77388c204e39913a22fb7d866fd82", 292 | "a113cdef4e03b08675922dc927880c5eb8f5dc53f9378f338283a6360c23b73356", 293 | "72fbc6b0919eef609c991f84c82da35424273e90c5f1ff34686c43bb1c2ef818ce", 294 | "4f4e6bdf97ae02716b255498672e18e7488ba080531e97465af38fbfd6d6edffd6", 295 | "deb7909b208fc15d450219ee184a28fc5a4151e71945ab69ef814da63c47eff22b", 296 | "3b3a4f5ed7043784b9405d4b354922c81df57b5c397d84192b4253cd6f9e3a59bc", 297 | "9344fb96ab8c97e06187b262643e0a76329f36c01fd2f87309c817e3df711366b9", 298 | "2a83156e9e44e64427cebe795355969a10bc5b658c0fcafac320e4739c3e30a576", 299 | "f658fc727e86468f82d479c53b88b53561fd4c095b5895a26dc53a4267df163a4e", 300 | "a7b5c2910933279dd8d0d06e13d9ff97dd9a7aa60a4d3d81d388107519440b7b05", 301 | "52b324930bf8f48918fe5b5c065aa0a3a4493cf8c3831ee65b76d40b4dd34b5535", 302 | "e15bafc5ace47d312461a04326135e900813674a1a38919dada33477e7b3614abb", 303 | "98b030e73ecedeb2b96e8f9daa21f0e243a79553b1c750042e8b76f1e4d88d2d42", 304 | "d4627c75a0016915c4ae78a7aabd451468f924e799f201c2aa86069df08dae6a0c", 305 | "4b38edbe55dc2006ca961d872b6346ed768fe856bb43546f41f7201045a1cd69c2", 306 | "846254250c32d8b7d9ee83d41e61de2b16d0b3275ab14ff75ae6faef738b5aa621", 307 | "0c377b4f978cc9f3a5f34b2eb39425d4e0b7365cb8d23b676f1db3196420c5543f", 308 | "58053bd939d908af748ac6cc9d8f9c20987182c288754538f6345247de65c04adc", 309 | "d66a33eca21cafd1f42feab39942a8358c80cfc41ea75af2f9b32d9b37b46beace", 310 | "2db260588e215bf70958671a7147325cf2d94ec8f430900e67d1c8fc0ef1401b72", 311 | "53bb6de3b61197441fbf6512345729c2d9a84d4336228a93146c1ed38a7b9ea162", 312 | "cf8885157607d1723ab133c307e7051cd216972a04ac2069e94d1220fe6c11e9f5", 313 | "28945b3156356ca1298ae932d5a266fa01e082302bf52a790efa6ddb64598061b6", 314 | "d89946f5d8e10ebcce0acc6bb9a607d5664af3033dbb15495c665e9accf30a4a95", 315 | "6edb2d707c72d906cf745e570c52af1e87e896e25f29af0f7f170aff9bbdbc0e94", 316 | "32e68194a750bef45b264a809395e92cd13d410b2e4880f705bd906c5524a6f983", 317 | "6add0db6c1e9671aca604d704ff577255d5c8cf632e1adc2ad4d2fd324fb16318f", 318 | "4e9924ccbaffd6550e1ade0b2c42cadf6a75a80439a5e2b6d6d3add9204c6a1560", 319 | "6c2d33f4ae13c55132fbc3af201af62609dfae7e27f3ae9714add7810cac53fb3e", 320 | "256a1d2d7d320b5841594497ca32bd5253358acf5f803b1ed6cdbc7c36278e006f", 321 | "2160a450e5a86e3101f93acce139f4e63d2d19d05da44fae31d76cbe151bb60a7f", 322 | "f57dc905fdfa1bb5e21851a7af28834eb32f0a96753d4391882621ef62d77d3b70", 323 | "bbb81bb6217006a806f49b0790f9d905e05fd35bce2a68c1e80c0ef8bd275d0101", 324 | "e813a3ab6ec80871f59b6835bcbcace58bae662f5c04466506114f26a6dcc4bcfd", 325 | "0ddab9a8870514876de51d9a8bf4c17d3be3102c704cc4326990204a4fa953567a", 326 | "075a93d381e177019c0eeaed98f39f5f65510650274ebba044fe0d8c7168ea39f4", 327 | "cda79f76f385ceddc285e69bab1fdaa1b6ca1849c51427aff715fa5ce67b5b3b7b", 328 | "831b9f5f7d9493271dbc214d624b5267228437d690807cb533afa56add776c7768", 329 | "f8183d5a48b584388ee52c18f79d19fc3c44090bfc9525e318f2f45ddaf2eb5594", 330 | "9e81bc96ab89ea60d3b560dc6b12fa0af49279119e0944ab76516f10cc345c8220", 331 | "1f6d32ae2365ab54674953fefae1676223456cc9de2b7a0e77614d82f60ebe7bb4", 332 | "78b103447c86aa9a2b705ebc3f98bab958a12f976cb6f9faa05cb2afef1ba63eb7", 333 | "2eba674cb43bf93b019e49a8f831d711c208dbd43e20fb2302d92b2465c8fc4be5", 334 | "b3f087cde1e6314a8df1aba1a78111f1db7b9572db2947c2d4beb426599ef37fbb", 335 | "6d98cff90790c0df4b11558be6e9b106e0590958613e6312f88678af1198d457d2", 336 | "466b109722dbde6de728f62174e08b45a880487030544b7fb82e22115fdc61c9d9", 337 | "89b1b0387386a91bd427113a757d4304ffded33a14b67ce90290b016d21280232e", 338 | "9f72d04178201c85e7777647520aa24e0f0a2552de325c60a1b47292fc8efc6a70", 339 | "9498561c78b85865d71fc4428ac5c9eecfcabeff888f1060465385ff258d15b43d", 340 | "8f369a031b7b89e1d75b80b2d7d772f9067ee1dd318bfe345f980b7b03a7aa24a6", 341 | "ff57c1525c679922c7f2d004b3d5d6ae652fa3c089c3c656a79c7e5326931da93b", 342 | "d3c6963b36c7795c2ece0b744df0615e06ec5531a3ab37e44237bc3c3827bddeee", 343 | "b917c85a811459f92b0f218bd2ec680c65605cb76cd411e4ebec5b0da2d4349af8", 344 | "2606d10355d1e8b428e369eb0c049dc570bd6e1ecb0508b636fd49290d14744cd2", 345 | "9678b4f8aceede7dfd170d042ae7ac14b9a0b6f0b3805fefb3c49cf5f854fa0cd4", 346 | "3daa13b4ea86998182f155ebebe866aaf8e65e5dc6db7826cb6f6bbf2ff6170f74", 347 | "eafcd3def32dc7635c9a06cb397062e3b242ea0e8fd8ac4afdc56b01abd3c7fcb4", 348 | "c46baa86241385e5768ff2f9c589ca526458412f08d4e07d5eb162f430ae2ab764", 349 | "12ebe8272dfdc308e95b675efa9a467eb7e88033e1edfdfeb2ba9ba6d53f7ac75d", 350 | "50440a4454203bb3e6d3daf4d1ef89c4367506f5f25531207e516bfff18f9ab59f", 351 | "4954e5e05460cf363da5611ca30ac4533e31d302ab348a2d75a374fdbb43ff7ee8", 352 | "3f32970a545885fd24196a964c0426b5844c80469774f60858fa96965f6a634ce8", 353 | "c34fa791b7a0f2d0f0d8221f50df5ebf7628a179089d4c09ef2bacf84869f20b4b", 354 | "dc3a458142d21a1cfdf2d67be90cf2c11fec12cfcac7ad09acd88f835cc945f0e4", 355 | "43f05f8bbdbb11a5e1cda2442a144b6eb7151fdc6e6080d633e9a82d2081c82c1a", 356 | "cb6c4e77a134697cdfc5479f2b88502454543e40e1f3992caac297f0cb43ce391a", 357 | "3e2bda3edfb6e08b33213e7d0a7e699bb8faae8392496d9e00d6076ddb517b84a6", 358 | "ba8ebf587e457c191a8752c8b91e3a69e11a64675b96124e0d622c2490c6ce1d44", 359 | "fca3a452ce280aaacb4d73b983283569a1c36537e3e12ebea9cca300a92512cea4", 360 | "e9cc62ceb93c5df886f106d7017a0c7ee87816748207f0d5030ae6a48b4d1fb34e", 361 | "adc738fa863cbe92f3490fdcaf269d1d77aec996185e4a1ac2a06dfc5060e24817", 362 | "56ebb45e3f10c3fa6a4cd342d495f81e6da7c252ec2dd21129767ff3cdb22143fc", 363 | "97d16b979225266809c72f8b5f83a5c2a14347da3c1c5dbbedbfe2bad6c5b91ad8", 364 | "8a85e2976455595891e7c72f6a2792bd794306517a7cefedeef376b4947271ae8e", 365 | "2f08d64cb5b289f351be161f562a5ba915760462ddbe9621c94d53ec1578c89123", 366 | "387547a47e8962bd3e408bfed2a1adb62968ee75e684593d6d318e3ad18cf6ed9e", 367 | "0b06eae3093032a195b57c3488996208f2b50ea28439e89fd1dfc0c6e96736d942", 368 | "0a99687e7d969905a9d5feeb1c25b89178e13e3a4b62a823ec162857abd02c8bdd", 369 | "7e8216de006bf952fa03a8a52b2adf5f2eacfa7b2fb5b0523f602b958181a30d69", 370 | "0e9a13679f2288df96d0af09ccc995f454dcb0c99381f370ae277363c05052a14b", 371 | "ac6866bd9a3a02176a5aff8902ae7ee3ad08e729ef57fb9f96294ca9dec91a7428", 372 | "ccc3afd5373582b9673b2b6cddd609c91954027a5bad65bff0aa5af5ca0d057e70", 373 | "db62587729cc6ceeaf85ceba12d72d7965944011a0dcd08ad64fed91419e08d73d", 374 | "74f7c6fa7b17d7617d543f9dda3fcb05c45cb9c85cffe8a5d7062c919736689f95", 375 | "06274892426ea0c7aa9e6515bbaa6d1634ec3e6951b835b3f92078a3efbb36c2ee", 376 | "54aa724ea75537e745b402294af837b70000c609ec5bbf00337f44cd4bac458e74", 377 | "e00ad6d86590a70acdb118860be8346a048f1d3a97f3577e1b630badb1728aa5be", 378 | "57254be65afca62bb73207d4bb9e4fdf08517c4231ed41d7296f6331eb863f506e", 379 | "34f2086e54a7b1a1910ed51763fd532d6fd094feb121dc59f9925341b6b587cc0e", 380 | "09ca71d21c790d20ed1ef7082c94a9175a9e98d6e5f018d0cb8ccd029a02d0bad5", 381 | "710af77f6edfbd4507714becd32ce0973aac9a288a610d6c3c7b78b7de4242fd37", 382 | "67e68ba6214d2505186b46b75e0b3da0455ce4829882a7f51db66cfb0a2987a069", 383 | "799076c994c65eeac785911e753d3dac32ca879cecbf869ef0434bf554dfb0826a", 384 | "4a309efa75f5768841969ff2e9849fc85bc8cfb56981608081320d29b522bce93b", 385 | "457245e4ba806934795c3014638f81a7f2d9afda5a0ba85dee63acca438dd22dea", 386 | "1c00ccae0a4b7343aaf18818872f67b69282e47fd84e8c9ddca006f26948bf748c", 387 | "c85e76c33b6a103ff66da6663203ce5e143e436330cf4c08a6359fc3f2caa12826", 388 | "7f17fc5a69c706bf870fa3f7d1cb3641884cdc55016f99806284b8d9ed70ef1d5f", 389 | "16a3838a92a249977f8343dc5de1bdc152fd4243812be39189faef9a64bfc68630", 390 | "c65da6ab13ff8509aeb830a009bfb8a5f524ed2c2dccc9c39c507fcc165dd1832a", 391 | "7a660d7372073c5a6dcc74e04c59be36b3f02b2dedee51327bb3cdf2067b5ac1ab", 392 | "c7d80af7d7a6119393c921a459e1ea84032930f4d4d6a15ca6d43ce7efcd89963b", 393 | "a3601decfd8b8709927909896fe5aea7afc7f489642f53ca402b7bdb7921d3020c", 394 | "f1f4f4da4ace5ac14f6d930a4d2219c023969c437c82b9595d5c30c9149b444099", 395 | "fe36d8068e259c3e1ec5aca6feb425d7bcf7e149547b1fc0a92640b131cd939ea3", 396 | "14ab0ab2a017e99e2c6e9eb2e3a0c74763621b36ad49fe4c86e85e398dc8deab97", 397 | "e798a97b2335fbcc24e7814331b897decb9c861394994481f05061a0a50085017e", 398 | "b571651d7970e49ac02de195538b7d6289a125deb724da2f4b503cc22f0a52f5fd", 399 | "698a6af4aab349ba3b51c12f1091ab3b5db360feb7afaaa1896e07af81fbc27e9e", 400 | "9bb5b7b4020db714ed662dd8c09b633df344a4ea687096e7fa1e2190e29e8a85bc", 401 | "4838a126ee0f06083546b83c95bd527dbd63e298eb30d49366afdf1eacc3577f30", 402 | "f9c20fba915ac66b4e053de6ff8e06adda2bd99751eac9be3ea5036c0e3c66c414", 403 | "dfb83ba4802115f7ed86cc00b6e96e5256278c35e5761aa5d1450d60f612e30424", 404 | "853f7b27d028827eca6cfb714e9b4c04d5598ad73e7628071dda411dfd44c97789", 405 | "10fb3765ce420ffe886cefe8cff63670f37144688ee7f1c8271e6944336231e32b", 406 | "2b99b6a124ff4342249770b2827f6a11d1ead6aafb0b669f6c1e37766df312fe10", 407 | "e4d596d06e91227e026bc960bfbad6dad234af0784e7b052c214e59036d841f84b", 408 | "e37b448b84967d5da9acd23d88e12f53dc56be45ff61d43d0284d8be8d786a85a4", 409 | "3a6ad9077cac1abf36ac2f0bc2042fb3a589286d8cc535f1a882f7e865ecc6c665", 410 | "1d7a76e6b05b9fa292759cae3ba232462f6778401498571983af354327083b13ac", 411 | "90e7e1bea1c75bc2ecb604489e2889f99424c6412ac99609f635706dc24e288fa9", 412 | "8253a313954202586881445d44cddac00ad30ef906915f96f5804e202891254e76", 413 | "028965514012ad3e6f6069464c6a510ad8cbc1e5c0a053194092cec85830d0d549", 414 | "6bcfe8e293006dbf4fa1f7a42574bd7539c70e9580f39ea6cd9f89e33f715c3eab", 415 | "b04d1cd5dc12700793e98b18b30c7ffbcb3bf0cb0f08e862aee6d73da29272cef9", 416 | "5d65790cb6138c6e87c7f82869e7f9e508d517313515a4e6d08c31cfd60182018d", 417 | "55a15d59629692cc8c6d538bdc21c044ecfc8a798afb47a343887bbe49596efb6f", 418 | "eebda828a8ad78b6b3cf82aebd5c28dbfc0b0766d53846b0e9e9b9b1adecaae1ff", 419 | "33d990d1f00fe3f47ca4ed3befaef3bde6a8c8042e1340db88d089b37256cb7bb3", 420 | "22ed7f00e8263e7388b63010f856f83a44b99a4097245b39e4792e01d71400b4bd", 421 | "5aa73ae10018c987c604b6d1e616857683a1fcaa17202264c2c484b672eb68d0f6", 422 | "b2e7848de943b088ab710c8a335f84a8a8af46ebcc91ee12b507163d553296d673", 423 | "4c0a28b06cd17b2339041b5dfa10e59c21117df32c8e8ddd1db4d733dd48fdd3f9", 424 | "bcabb28377215d70ac4b4897886efffbcc2b98eb415b6013ef0782e0e7d71ca5b8", 425 | "f4aed186b0465b44d6db1b17a3c908108d3bd2daed63ef52a3633b1f8504e03742", 426 | "1782bfbdd76f12217fb6955a6526b98a3ccef3b325bda61c71ff4170a614fdb47f", 427 | "ce49d17e2495f0360494db1a0c24db5fc60e2446bc8088b0c62e3390d8988b8a54", 428 | "a86fdcdffc59ae25501e73e5c3894b1356f9a314fb431767625802d49ab05b52ce", 429 | "4286c4165ada0bc326137e47b051941c3308950fc8c930e36adff5b78ffd27f325", 430 | "47d54a2ff68bb648985d770ffb454b23cc5a297108e0d2528c0ccb57b3f2548f2b", 431 | "ca8ba4b883ecf4a7c6e57af205670aaff2b4945b834bd61de1f5ff950b95e11d5e", 432 | "e508ebcce4a6e031a25da50130b00d2ed669917cfa10e598109c3bfca4ff0c7ed8", 433 | "622928e34289ca7ca794eab9bdd64832f4e825345b3b1799259348ca3b8ce3ce14", 434 | "088b10a1741e0311108a83e44b8caee36b64791d346fb17b99a10589c0de4695ec", 435 | "0486f8992dcbc6ef03e4cbeb74f49826ac9f65a1a24aa8e321f3e463a0e246913a", 436 | "b74743bc44d1e01782fe70dc5874da65d813d3a069e5be1fd34eaf2339ff4953eb", 437 | "5bcacc7f2881d59755386f9ae3a7759889610cb164cabc5baa2f7eb8767bd4dbdb", 438 | "7cd420a3b9df75b9b85e25d9a37edf69099b891db053d9fbc78cf799c730b53b45", 439 | "5ebb83f3156cd70575373f63bd70fb4007a83f562bd049b31bf8736d5d1d7be22d", 440 | "a9c5f8016e1a2a9217383e5497dce2e0340aae4624f53559c3ff1b6e651d2ede27", 441 | "c5113093037c96387de351cad442e78638c09bf590bb8785bcf4ac14655a39d15e", 442 | "51ae4299083f773d072ed81795ffd12c1703ccbaf30524a69981227f9bd4a4f017", 443 | "1a5f341e151239eded410b4bc72a2dcb780a84b8f740983ceb58c421cd552abc61", 444 | "d0f24a60791d81fb811b6f6be8dbc6b6fc171c474b5f076f754b2cafcaef7b46f1", 445 | "8cd7a6f89c35faa242e193f3eeea12893d577bdc224312e693061c0bec5112b92f", 446 | "d90c32b7e45e70d8f5e5f0180d4948bec89bf5a051de1a8461c8d1e4d0fef28c8d", 447 | "8188c7846e976cccdb32ffe6421ff85b7f7973ff26b61d3e8c5853f16e0fd9059d", 448 | "c159ec6aa4c99310d2118b5837c42a4653c9a5cad637466381b1fa4502c79c07d4", 449 | "867b33d2492a90b1b41ba2f22c53eb924a0060914dfb06b6247dec96304dca40be", 450 | "18963059cda4a6b22aa0b8de596ea6e66b861d62b847ed99dc97e5d66af37ccc48", 451 | "130b0b516b1e806568fe30356812309f7f63711f241622c96b011acfa06cc5cc16", 452 | "f26d16f7c717666a7d487dd0912d35d06328dbb34292d7b3fee8a3461b5f967f35", 453 | "658974abc1defcee49ea5ec0c8bbaf7f308ca1edd985691993695f7a8c2fd14a62", 454 | "e295c9c6773fd57b2c56d4f36cf2c031816b0386021cab43d48675fcffad7424a9", 455 | "20db5888fe478f1a0184da257df42717a61d42e4156eb7ec77a4a754c3ea9927fc", 456 | "59876750b18bb49888e374a764d0462bdb750a5296e9a8858ce45c7e1f8f808d56", 457 | "398e4ac15e36d03577a816b2370f790d741f74af9fec63b7f1b9d8a422420a4f91", 458 | "afaa028719df64f03f26925796cb1605f58b6eedcd9dea8a004a2a9f23b94f1bd0", 459 | "a2aadb7a7a2f77bbe299afc273ff7186b21b49f27621c393b877c1dd8afde803c9", 460 | "29ec04c3edd6c63e9755e240f47986a74a3879f70f5ce4ce13dd16c4bc6193750b", 461 | "f77c6b95cb0781beaf43e27032bdb46dabd6c5481a77797d5799fd3db2b042b651", 462 | "68abf7e8103be243bce0cbdd05db7d9abbd9124fb323dc77eb69d0f1617d57fbe6", 463 | "3684ef0317894d2ba6dae8d062a1323815decff345b5c90939602523154e03426f", 464 | "66b6bd899e3a0426b4ead0cae4ee4be12a2ce319a5dcc9b0a5b0afd56af0172d50", 465 | "f0bd137ace470e68662c63e46c86f17b40efcb9b9d3a2beee483159cb455b5240a", 466 | "fb9edd642441df8e41f9329154ef781b051ae78078349faf7fb50b0f20d7fe8e26", 467 | "35a56e562f8dd745df1476a0701d9afaeda3463c7523478c0acc21fdcdc168e4e0", 468 | "92cc9a9f50af060e69d07e0f59a5f7acd926f033027c6c97afadaf9b1d551f522a", 469 | "03f44a8ecf7f24feaacd3752988c058372ae6f2281f45bf63e1b0999c42cb91add", 470 | "4449147a21c844d961a1cfe9b4d8fddc47db36384effa3b1e9d0f3232fc138a4cf", 471 | "95f2c0215b49cc3fdff17cfa108d2414bc6ba3ecd7344c46730210cbca28b239f5", 472 | "374181ea664b0b9b5722be935fbb2388e24a8bafacda335234609ddca8200c8b6a", 473 | "da36ab5b7f127ba6a04f889a6d8f5a8151922c80ef3e0c4491f256094f894c43bc", 474 | "11f1cd283c4b4769477299fcc27998c1b0d976d551d90cf9711d17c3936c41c90a", 475 | "4d8be917e7d93e47e72f014dd5ff4a98e9d66c54da4aab939574cdb6e925f784d4", 476 | "30e6365a9ee0993954060aeefbe5c0a15833acefc3194e13385fc293dce255e3b7", 477 | "758bda4eed489163d184c95a5ae820c6adb59918cff141cc9ba22e4c512dfb6ea8", 478 | "bf593d0dbcc9debe8def61a463177b1479482b7677be275065cd077986b7e8193d", 479 | "9a6c584400604fce2d20829376fe55ffb1a4416e797de033281e186e48445d28cc", 480 | "bd2391ce96e522337dc4bb11eee97111b3abc36a7f86590a9e778083de6a10bfff", 481 | "b1ec40a7feb2aa7f9fc9bf72d43f65984551c9cb9deb9c5ee317a2a7cc92ea9568", 482 | "8d3eb3ea69978277900850c302870e6ae6b8b43147712ffac866d8f0f385cffe86", 483 | "99d6dd5beb150c7ceea1208e698d164a2a48e68f6410d4673f64b58109644e40b2", 484 | "70f19019a3c8c300aa2486d5d138d7a52cef5c372a5eac2fc547e2305c30c603ce", 485 | "156368eadc25a8f8c0c6ffa8968e370da454d892665819d6e4b9d7f6a6dc516c91", 486 | "7326e23307326033cc792253cf3452edb08961e6382f2c9fc8fbbc88ec64fbf48d", 487 | "8b0e40b2f041181f5f477018900be52add4ec079885b0dd23632e5a2141fca78be", 488 | "d215bb240ac1c2a98650ddeb8e81cb37b3dfbe4502eb8a7ca3acd42bdd78914469", 489 | "5c9b3157418ca305528d5c0d0e757ccc92abafad35ad32f41323bce9b1affc5f02", 490 | "f3c06635b47ebd8e765d9c682a7e4680687e05425ecc450967cefe164b1b12a2d2", 491 | "7d786492893a98626d63695c5da8ef6bf08586f5ca6654cddd52081a8e1c9cf267", 492 | "9c2cfbfd6977a2d374e447d901846daf2be8d45d18781ca6edccc601de47b3ec7e", 493 | "41049a856695589c23499856208762f6e612f4ffd440d0c9385e6857dffd651f6a", 494 | "61be67189cbb4d0989f4155135364665cb8c492734dd5aac398968e72c020a8b19", 495 | "5fcd9792d71228ed0880bc5affb2c98d9824ece1a92c13f727ac31ec715683c72b", 496 | "01dca800f73a27f0f73217641bec9fdaae00b45f29c813d234adfdcee2e397f76c", 497 | "9d38bb57a1e1835917618f75213184114d2f7ffd96f736c6e631be389c13e28abf", 498 | "ef1fe93db5bc8b84ed0dc51058cc05bcd22c9489cce9cfc995a13d10a20dc99e8e", 499 | "2791283af6bfc39b642f8b87da627e686efb4e73cd14132c14b1f459dcf42ed846", 500 | "beebfb41bfc3e1d5add37add8b8bef6a6a466eae83d7cff319823a9cf581fe655a", 501 | "23735818d455146ca3f39f3fcfd638da4a29e176ede24dab0a347368569faec512", 502 | "a52e456b58cc4cd508e411ef936d437fba81c7bb926a4a4c9bf6611316592e3126", 503 | "317cf911e27b11cc16bc24d3b06b3a73370ff3faf9963b9aec33e4abaeec64a2df", 504 | "ed706f0c50153be2e93453cd0532121e96b01f06e4cbd3a895284f61d502f23bcd", 505 | "408637e6f874040e296878b4047afe6e39af79be85903e2c36ecb67f1feb9b6cb3", 506 | "912857a73ab8989238780faec4beb9598707e4455fbd7d982c7f5cbf2a4b68f480", 507 | "64a71faac7e60d9f57dec01848d3af5c1118a02133198ffc36e1a2b94ce5002f1e", 508 | "2468eed760f519ef19ee951fb278445e2e3fc75e2eff5300687d1802992e4aacfe", 509 | "d1ffb8fcb25cd7aca7d64debc29c2e795242ed36d945ec6af1e6472dde7184a946", 510 | "d52f43dcbec8a59d8b54fe109a7afb959adf93c5eb0f1881148c488446795abd16", 511 | "2c691a89799b76046729bbea582ca78a52d63cb242608a4ddefcba6926e5689730", 512 | "607fe5742c24912f3c938087e128e4234a5583518b4d5f27b84d0c2b96d350a9cf", 513 | "e62a6efb6d4389128b1cb57d45d8c5ed7a49f810164e76a88432fffc58d109c46c", 514 | "1e45761303e79c8ae8ce9b3c9e71b52e50180d4f3101af95950ca265893a1da9b0", 515 | "aa573d877960806ca814e443897e8fb437068bd93efe6d3cb3fdd57c02ee1cc500", 516 | "7ba6be551ee57cb3c939c6ab92dc704d255076870f370690b0bc5ab1a773d4b881", 517 | "8749c3bc3be4412dfdefb987e882121a40bc4355eb9f058fa5f73f46f0f45de60c", 518 | "0f11cc6c66a9ed7f1fdd5bf4cb8f69a67008a316db7024c4364ba6de9e355d5657", 519 | "c222f6f8db342d9660959f41bdb19b729d3520b35db450d629a4a92315bf0ece79", 520 | "c9d9b8df4929fa84731586aa9baeccd3428019d5422d31ad4f6b1c99a5e43eb2a3", 521 | "059df6eadda392f1e1f52978e568148863bdad9a76b196875d541d272ba08a0140" 522 | }; 523 | 524 | static void hex(uint8_t *out, const char *in) { 525 | while (sscanf(in, "%02hhx", out++) == 1) 526 | in += 2; 527 | } 528 | 529 | int main(void) { 530 | secret_t sx, sy; 531 | share_t ss[255]; 532 | 533 | for (size_t i = 0; i < 255; i++) 534 | hex(ss[i], share[i]); 535 | 536 | for (size_t i = 1; i < 255; i++) { 537 | hex(sx, secret[i - 1]); 538 | shamir_combine(sy, i, ss); 539 | if (memcmp(sx, sy, secret_size)) /* variable time */ 540 | errx(EXIT_FAILURE, "Reconstruction failure from %zd known shares", i); 541 | } 542 | 543 | printf("Reference secrets successfully reconstructed\n"); 544 | return EXIT_SUCCESS; 545 | } 546 | --------------------------------------------------------------------------------