diff --git a/crypto_kem/babybear/META.yml b/crypto_kem/babybear/META.yml new file mode 100644 index 00000000..3731bf02 --- /dev/null +++ b/crypto_kem/babybear/META.yml @@ -0,0 +1,15 @@ +name: BabyBear +type: kem +claimed-nist-level: 1 +claimed-security: IND-CCA2 +length-public-key: 804 +length-ciphertext: 917 +length-secret-key: 40 +length-shared-secret: 32 +nistkat-sha256: b8442ffaad8e74c6ebfd75d02e13f8db017a7a6dd8458f5d1a5011de6057d775 +principal-submitters: + - Mike Hamburg +implementations: + - name: clean + version: round2 + diff --git a/crypto_kem/babybear/clean/LICENSE b/crypto_kem/babybear/clean/LICENSE new file mode 100644 index 00000000..5fb15a7c --- /dev/null +++ b/crypto_kem/babybear/clean/LICENSE @@ -0,0 +1,24 @@ +Copyright (c) 2016-2019 Rambus, Inc. +and licensed under the following MIT license. + +The MIT License (MIT) + +Copyright (c) 2016-2019 Rambus Inc. + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. diff --git a/crypto_kem/babybear/clean/Makefile b/crypto_kem/babybear/clean/Makefile new file mode 100644 index 00000000..dbe69c35 --- /dev/null +++ b/crypto_kem/babybear/clean/Makefile @@ -0,0 +1,21 @@ +# This Makefile can be used with GNU Make or BSD Make + +LIB=libbabybear_clean.a + +HEADERS = api.h melas_fec.h params.h ring.h threebears.h +OBJECTS = kem.o melas_fec.o ring.o threebears.o + + +CFLAGS=-O3 -Wcast-align=strict -Wall -Wconversion -Wextra -Wpedantic -Wvla -Wmissing-prototypes -Wredundant-decls -std=c99 -I../../../common $(EXTRAFLAGS) + +all: $(LIB) + +%.o: %.c $(HEADERS) + $(CC) $(CFLAGS) -c -o $@ $< + +$(LIB): $(OBJECTS) + $(AR) -r $@ $(OBJECTS) + +clean: + $(RM) $(OBJECTS) + $(RM) $(LIB) diff --git a/crypto_kem/babybear/clean/Makefile.Microsoft_nmake b/crypto_kem/babybear/clean/Makefile.Microsoft_nmake new file mode 100644 index 00000000..27e5cdda --- /dev/null +++ b/crypto_kem/babybear/clean/Makefile.Microsoft_nmake @@ -0,0 +1,19 @@ +# This Makefile can be used with Microsoft Visual Studio's nmake using the command: +# nmake /f Makefile.Microsoft_nmake + +LIBRARY=libbabybear_clean.lib +OBJECTS = kem.obj melas_fec.obj ring.obj threebears.obj + +CFLAGS=/nologo /I ..\..\..\common /W4 /WX + +all: $(LIBRARY) + +# Make sure objects are recompiled if headers change. +$(OBJECTS): *.h + +$(LIBRARY): $(OBJECTS) + LIB.EXE /NOLOGO /WX /OUT:$@ $** + +clean: + -DEL $(OBJECTS) + -DEL $(LIBRARY) diff --git a/crypto_kem/babybear/clean/api.h b/crypto_kem/babybear/clean/api.h new file mode 100644 index 00000000..d513843f --- /dev/null +++ b/crypto_kem/babybear/clean/api.h @@ -0,0 +1,18 @@ +#ifndef PQCLEAN_BABYBEAR_CLEAN_API_H +#define PQCLEAN_BABYBEAR_CLEAN_API_H + +#include +#include +#include + +#define PQCLEAN_BABYBEAR_CLEAN_CRYPTO_SECRETKEYBYTES 40 +#define PQCLEAN_BABYBEAR_CLEAN_CRYPTO_PUBLICKEYBYTES 804 +#define PQCLEAN_BABYBEAR_CLEAN_CRYPTO_BYTES 32 +#define PQCLEAN_BABYBEAR_CLEAN_CRYPTO_CIPHERTEXTBYTES 917 +#define PQCLEAN_BABYBEAR_CLEAN_CRYPTO_ALGNAME "BabyBear" + +int PQCLEAN_BABYBEAR_CLEAN_crypto_kem_keypair(uint8_t *pk, uint8_t *sk); +int PQCLEAN_BABYBEAR_CLEAN_crypto_kem_enc(uint8_t *ct, uint8_t *ss, const uint8_t *pk); +int PQCLEAN_BABYBEAR_CLEAN_crypto_kem_dec(uint8_t *ss, const uint8_t *ct, const uint8_t *sk); + +#endif diff --git a/crypto_kem/babybear/clean/kem.c b/crypto_kem/babybear/clean/kem.c new file mode 100644 index 00000000..5b9e7ad1 --- /dev/null +++ b/crypto_kem/babybear/clean/kem.c @@ -0,0 +1,22 @@ +#include "api.h" +#include "params.h" +#include "randombytes.h" +#include "threebears.h" + +int PQCLEAN_BABYBEAR_CLEAN_crypto_kem_keypair(uint8_t *pk, uint8_t *sk) { + randombytes(sk, PRIVATE_KEY_BYTES); + PQCLEAN_BABYBEAR_CLEAN_get_pubkey(pk, sk); + return 0; +} + +int PQCLEAN_BABYBEAR_CLEAN_crypto_kem_enc(uint8_t *ct, uint8_t *ss, const uint8_t *pk) { + uint8_t seed[ENC_SEED_BYTES + IV_BYTES]; + randombytes(seed, sizeof(seed)); + encapsulate(ss, ct, pk, seed); + return 0; +} + +int PQCLEAN_BABYBEAR_CLEAN_crypto_kem_dec(uint8_t *ss, const uint8_t *ct, const uint8_t *sk) { + PQCLEAN_BABYBEAR_CLEAN_decapsulate(ss, ct, sk); + return 0; +} diff --git a/crypto_kem/babybear/clean/melas_fec.c b/crypto_kem/babybear/clean/melas_fec.c new file mode 100644 index 00000000..ace39b9c --- /dev/null +++ b/crypto_kem/babybear/clean/melas_fec.c @@ -0,0 +1,87 @@ +/* Melas forward error correction, reference code (as implemented in the paper) */ +#include "melas_fec.h" + +/* Return s/2^n mod R */ +static fec_gf_t step(size_t n, fec_gf_t R, fec_gf_t s) { + for (; n; n--) { + s = (s ^ ((s & 1) * R)) >> 1; + } + return s; +} + +/* Compute syndrome(data), where data has length len */ +#define syndrome18(data,len) s18update(0,data,len) +static fec_gf_t s18update(fec_gf_t r, const uint8_t *data, size_t len) { + for (size_t i = 0; i < len; i++) { + r = step(8, 0x46231, r ^ data[i]); + } + return r; +} + +/* Append 3 bytes of FEC(data) to data, so that the FEC becomes 0 */ +void PQCLEAN_BABYBEAR_CLEAN_melas_fec_set( + uint8_t out[MELAS_FEC_BYTES], + const uint8_t *data, + size_t len +) { + fec_gf_t fec = syndrome18(data, len); + for (size_t i = 0; i < MELAS_FEC_BYTES; i++, fec >>= 8) { + out[i] = (uint8_t)fec; + } +} + +/* Return a*b mod Q */ +static fec_gf_t mul(fec_gf_t a, fec_gf_t b) { + fec_gf_t r = 0; + for (size_t i = 0; i < 9; i++) { + r ^= ((b >> (8 - i)) & 1) * a; + a = step(1, Q, a); + } + return r; +} + +/* Reverse an 18-bit number x */ +static fec_gf_t reverse18(fec_gf_t x) { + fec_gf_t ret = 0; + for (size_t i = 0; i < 18; i++) { + ret ^= ((x >> i) & 1) << (17 - i); + } + return ret; +} + +/* Correct data to have the given FEC */ +void PQCLEAN_BABYBEAR_CLEAN_melas_fec_correct ( + uint8_t *data, + size_t len, + const uint8_t fec[MELAS_FEC_BYTES] +) { + + fec_gf_t a = s18update(syndrome18(data, len), fec, MELAS_FEC_BYTES); + fec_gf_t c, r, htr; + size_t i; + const uint8_t table[9] = {36, 10, 43, 215, 52, 11, 116, 244, 0}; + fec_gf_t e0, e1; + + /* Form a quadratic equation from the syndrome */ + c = mul(step(9, Q, a), step(9, Q, reverse18(a))); + for (i = 0, r = 0x100; i < 510; i++) { + r = mul(r, c); + } + r = step(17, Q, r); + a = step(511 - (len + MELAS_FEC_BYTES) * 8, Q, a); + + /* Solve using the half trace */ + for (i = 0, htr = 0; i < 9; i++) { + htr ^= ((r >> i) & 1) * table[i]; + } + e0 = mul(a, htr); + e1 = e0 ^ a; + + /* Correct the errors using the locators */ + for (i = 0; i < len; i++) { + data[i] ^= (uint8_t)(e0 & (((e0 & (e0 - 1)) - 1) >> 9)); + data[i] ^= (uint8_t)(e1 & (((e1 & (e1 - 1)) - 1) >> 9)); + e0 = step(8, Q, e0); + e1 = step(8, Q, e1); + } +} diff --git a/crypto_kem/babybear/clean/melas_fec.h b/crypto_kem/babybear/clean/melas_fec.h new file mode 100644 index 00000000..c44638bc --- /dev/null +++ b/crypto_kem/babybear/clean/melas_fec.h @@ -0,0 +1,26 @@ +#ifndef __THREEBEARS_MELAS_FEC_H__ +#define __THREEBEARS_MELAS_FEC_H__ + +#include "api.h" + +#define MELAS_FEC_BYTES 3 +#define MELAS_FEC_BITS 18 + +typedef uint32_t fec_gf_t; +static const fec_gf_t Q = 0x211; + +/* Append 3 bytes of FEC(data) to data, so that the FEC becomes 0 */ +void PQCLEAN_BABYBEAR_CLEAN_melas_fec_set( + uint8_t out[MELAS_FEC_BYTES], + const uint8_t *data, + size_t len +); + +/* Append 3 bytes of FEC(data) to data, so that the FEC becomes 0 */ +void PQCLEAN_BABYBEAR_CLEAN_melas_fec_correct( + uint8_t *data, + size_t len, + const uint8_t fec[MELAS_FEC_BYTES] +); + +#endif diff --git a/crypto_kem/babybear/clean/params.h b/crypto_kem/babybear/clean/params.h new file mode 100644 index 00000000..b99c2fa3 --- /dev/null +++ b/crypto_kem/babybear/clean/params.h @@ -0,0 +1,29 @@ +#ifndef __THREEBEARS_PARAMS_H__ +#define __THREEBEARS_PARAMS_H__ + +#define VERSION 1 +#define MATRIX_SEED_BYTES 24 +#define ENC_SEED_BYTES 32 +#define IV_BYTES 0 +#define LGX 10 +#define DIGITS 312 +#define DIM 2 +#define VAR_TIMES_128 72 +#define LPR_BITS 4 +#define FEC_BITS 18 +#define CCA 1 +#define SHARED_SECRET_BYTES 32 +#define PRIVATE_KEY_BYTES 40 +#define PRF_KEY_BYTES PRIVATE_KEY_BYTES + +#define BEAR_NAME "BabyBear" +#define encapsulate PQCLEAN_BABYBEAR_CLEAN_encapsulate +#define decapsulate PQCLEAN_BABYBEAR_CLEAN_decapsulate +#define get_pubkey PQCLEAN_BABYBEAR_CLEAN_get_pubkey + +#define GF_BYTES ((LGX*DIGITS+7)/8) +#define PUBLIC_KEY_BYTES (MATRIX_SEED_BYTES + DIM*GF_BYTES) +#define CAPSULE_BYTES \ + (DIM*GF_BYTES + IV_BYTES + ((ENC_SEED_BYTES*8+FEC_BITS)*LPR_BITS+7)/8) + +#endif diff --git a/crypto_kem/babybear/clean/ring.c b/crypto_kem/babybear/clean/ring.c new file mode 100644 index 00000000..03c4ad64 --- /dev/null +++ b/crypto_kem/babybear/clean/ring.c @@ -0,0 +1,102 @@ +/** Ring arithmetic implementation */ +#include "ring.h" + +/** Multiply and accumulate c += a*b */ +void PQCLEAN_BABYBEAR_CLEAN_mac(gf_t c, const gf_t a, const gf_t b) { + /* Reference non-Karatsuba MAC */ + dslimb_t accum[2 * DIGITS] = {0}; + dslimb_t chain; + size_t i, j; + + /* Initialize accumulator = unclarify(c) */ + for (i = 0; i < DIGITS; i++) { + accum[i + DIGITS / 2] = c[i]; + } + + /* Multiply */ + for (i = 0; i < DIGITS; i++) { + for (j = 0; j < DIGITS; j++) { + accum[i + j] += (dslimb_t)a[i] * b[j]; + } + } + + /* Clarify and reduce */ + for (i = 0; i < DIGITS / 2; i++) { + accum[i + DIGITS / 2] -= accum[i]; + accum[i + DIGITS] += accum[i]; + accum[i + DIGITS / 2] += accum[i + 3 * DIGITS / 2]; + accum[i + DIGITS] += accum[i + 3 * DIGITS / 2]; + } + + /* Carry propagate */ + chain = accum[3 * DIGITS / 2 - 1]; + accum[3 * DIGITS / 2 - 1] = chain & LMASK; + chain >>= LGX; + accum[DIGITS] += chain; + for (i = DIGITS / 2; i < 3 * DIGITS / 2; i++) { + chain += accum[i]; + c[i - DIGITS / 2] = chain & LMASK; + chain >>= LGX; + } + c[0] = (limb_t) (c[0] + chain); + c[DIGITS / 2] = (limb_t) (c[DIGITS / 2] + chain); +} + +/** Reduce a gf_t to canonical form, i.e. strictly less than N. */ +void PQCLEAN_BABYBEAR_CLEAN_canon(gf_t c) { + const limb_t DELTA = (limb_t)1 << (LGX - 1); + slimb_t hi; + dslimb_t scarry; + dlimb_t carry; + + /* Reduce to 0..2p */ + hi = (slimb_t) (c[DIGITS - 1] - DELTA); + c[DIGITS - 1] = (limb_t) ((hi & LMASK) + DELTA); + c[DIGITS / 2] = (limb_t) (c[DIGITS / 2] + (hi >> LGX)); + + /* Strong reduce. First subtract modulus */ + scarry = hi >> LGX; + for (size_t i = 0; i < DIGITS; i++) { + scarry = scarry + (slimb_t)c[i] - modulus(i); + c[i] = scarry & LMASK; + scarry >>= LGX; + } + + /* add it back */ + carry = 0; + for (size_t i = 0; i < DIGITS; i++) { + carry = carry + c[i] + ((dlimb_t)scarry & modulus(i)); + c[i] = carry & LMASK; + carry >>= LGX; + } +} + +/** Serialize a gf_t to bytes */ +void PQCLEAN_BABYBEAR_CLEAN_contract(uint8_t ch[GF_BYTES], gf_t a) { + size_t pos; + + PQCLEAN_BABYBEAR_CLEAN_canon(a); + for (size_t i = 0; i < GF_BYTES; i++) { + pos = (i * 8) / LGX; + ch[i] = (uint8_t)(a[pos] >> ((i * 8) % LGX)); + if (i < GF_BYTES - 1) { + ch[i] |= (uint8_t)(a[pos + 1] << (LGX - ((i * 8) % LGX))); + } + } +} + +/** Deserialize a gf_t from bytes */ +void PQCLEAN_BABYBEAR_CLEAN_expand(gf_t ll, const uint8_t ch[GF_BYTES]) { + limb_t tmp, buffer = 0; + + for (size_t i = 0, j = 0, bbits = 0; i < GF_BYTES; i++) { + tmp = ch[i]; + buffer |= (limb_t)(tmp << bbits); + bbits += 8; + if (bbits >= LGX) { + ll[j++] = buffer & LMASK; + buffer = (limb_t)(tmp >> (LGX - (bbits - 8))); + bbits = bbits - LGX; + } + } +} diff --git a/crypto_kem/babybear/clean/ring.h b/crypto_kem/babybear/clean/ring.h new file mode 100644 index 00000000..8cbc207c --- /dev/null +++ b/crypto_kem/babybear/clean/ring.h @@ -0,0 +1,31 @@ +#ifndef __THREEBEARS_RING_H__ +#define __THREEBEARS_RING_H__ + +#include "api.h" +#include "params.h" + +typedef uint16_t limb_t; +typedef int16_t slimb_t; +typedef uint32_t dlimb_t; +typedef int32_t dslimb_t; +#define LMASK (((limb_t)1<> 8, DIM, + VAR_TIMES_128 - 1, LPR_BITS, FEC_BITS, CCA, 0 /* padding */ + }; + + cshake256_inc_init(ctx, NULL, 0, (const uint8_t *)S, sizeof(S) - 1); + cshake256_inc_absorb(ctx, (const uint8_t *)pblock, sizeof(pblock)); + cshake256_inc_absorb(ctx, &purpose, 1); +} + +/** Sample n gf_t's uniformly from a seed */ +static void uniform(gf_t matrix, const uint8_t *seed, uint8_t iv) { + uint8_t c[GF_BYTES]; + shake256incctx ctx; + + threebears_hash_init(&ctx, HASH_PURPOSE_UNIFORM); + cshake256_inc_absorb(&ctx, seed, MATRIX_SEED_BYTES); + cshake256_inc_absorb(&ctx, &iv, 1); + cshake256_inc_finalize(&ctx); + cshake256_inc_squeeze(c, sizeof(c), &ctx); + PQCLEAN_BABYBEAR_CLEAN_expand(matrix, c); +} + +/** The ThreeBears error distribution */ +static slimb_t psi(uint8_t ci) { + int sample = 0, var = VAR_TIMES_128; + + for (; var > 64; var -= 64, ci = (uint8_t)(ci << 2)) { + sample += ((ci + 64) >> 8) + ((ci - 64) >> 8); + } + return (slimb_t)(sample + ((ci + var) >> 8) + ((ci - var) >> 8)); +} + +/** Sample a vector of n noise elements */ +static void noise(gf_t x, const shake256incctx *ctx, uint8_t iv) { + uint8_t c[DIGITS]; + shake256incctx ctx2; + + memcpy(&ctx2, ctx, sizeof(ctx2)); + cshake256_inc_absorb(&ctx2, &iv, 1); + cshake256_inc_finalize(&ctx2); + cshake256_inc_squeeze(c, DIGITS, &ctx2); + for (size_t i = 0; i < DIGITS; i++) { + x[i] = (limb_t)(psi(c[i]) + modulus(i)); + } +} + +/* Expand public key from private key */ +void PQCLEAN_BABYBEAR_CLEAN_get_pubkey(uint8_t *pk, const uint8_t *sk) { + shake256incctx ctx; + shake256incctx ctx2; + gf_t sk_expanded[DIM], b, c; + + threebears_hash_init(&ctx, HASH_PURPOSE_KEYGEN); + cshake256_inc_absorb(&ctx, sk, PRIVATE_KEY_BYTES); + + memcpy(&ctx2, &ctx, sizeof(ctx2)); + cshake256_inc_finalize(&ctx2); + cshake256_inc_squeeze(pk, MATRIX_SEED_BYTES, &ctx2); + + for (uint8_t i = 0; i < DIM; i++) { + noise(sk_expanded[i], &ctx, i); + } + for (uint8_t i = 0; i < DIM; i++) { + noise(c, &ctx, (uint8_t)(i + DIM)); + for (uint8_t j = 0; j < DIM; j++) { + uniform(b, pk, (uint8_t) (i + DIM * j)); + PQCLEAN_BABYBEAR_CLEAN_mac(c, b, sk_expanded[j]); + } + PQCLEAN_BABYBEAR_CLEAN_contract(&pk[MATRIX_SEED_BYTES + i * GF_BYTES], c); + } +} + +/* Encapsulate a shared secret and return it */ +void PQCLEAN_BABYBEAR_CLEAN_encapsulate( + uint8_t *shared_secret, + uint8_t *capsule, + const uint8_t *pk, + const uint8_t *seed +) { + uint8_t *lpr_data = &capsule[GF_BYTES * DIM]; + shake256incctx ctx; + gf_t sk_expanded[DIM], b, c; + uint8_t tbi[ENC_SEED_BYTES + FEC_BYTES]; + dlimb_t rlimb0, rlimb1; + limb_t h; + uint8_t *iv = &lpr_data[(ENC_BITS * LPR_BITS + 7) / 8]; + + memcpy(iv, &seed[ENC_SEED_BYTES], IV_BYTES); + + threebears_hash_init(&ctx, HASH_PURPOSE_ENCAPS); + cshake256_inc_absorb(&ctx, pk, MATRIX_SEED_BYTES); + cshake256_inc_absorb(&ctx, seed, ENC_SEED_BYTES + IV_BYTES); + + for (uint8_t i = 0; i < DIM; i++) { + noise(sk_expanded[i], &ctx, i); + } + for (uint8_t i = 0; i < DIM; i++) { + noise(c, &ctx, (uint8_t)(i + DIM)); + for (uint8_t j = 0; j < DIM; j++) { + uniform(b, pk, (uint8_t)(j + DIM * i)); + PQCLEAN_BABYBEAR_CLEAN_mac(c, b, sk_expanded[j]); + } + PQCLEAN_BABYBEAR_CLEAN_contract(&capsule[i * GF_BYTES], c); + } + noise(c, &ctx, (uint8_t)(2 * DIM)); + + /* Calculate approximate shared secret */ + for (uint8_t i = 0; i < DIM; i++) { + PQCLEAN_BABYBEAR_CLEAN_expand(b, &pk[MATRIX_SEED_BYTES + i * GF_BYTES]); + PQCLEAN_BABYBEAR_CLEAN_mac(c, b, sk_expanded[i]); + } + PQCLEAN_BABYBEAR_CLEAN_canon(c); + + + memcpy(tbi, seed, ENC_SEED_BYTES); + + PQCLEAN_BABYBEAR_CLEAN_melas_fec_set(&tbi[ENC_SEED_BYTES], tbi, ENC_SEED_BYTES); + + /* Export with rounding */ + for (size_t i = 0; i < ENC_BITS; i += 2) { + h = (limb_t)(tbi[i / 8] >> (i % 8)); + rlimb0 = (dlimb_t)((c[i / 2] >> (LGX - LPR_BITS)) + (h << 3)); + rlimb1 = (dlimb_t)((c[DIGITS - i / 2 - 1] >> (LGX - LPR_BITS)) + ((h >> 1) << 3)); + lpr_data[i / 2] = (uint8_t)((rlimb0 & 0xF) | rlimb1 << 4); + } + + cshake256_inc_finalize(&ctx); + cshake256_inc_squeeze(shared_secret, SHARED_SECRET_BYTES, &ctx); +} + +/* Decapsulate a shared secret and return it */ +void PQCLEAN_BABYBEAR_CLEAN_decapsulate( + uint8_t shared_secret[SHARED_SECRET_BYTES], + const uint8_t capsule[CAPSULE_BYTES], + const uint8_t sk[PRIVATE_KEY_BYTES] +) { + const uint8_t *lpr_data = &capsule[GF_BYTES * DIM]; + shake256incctx ctx; + gf_t ska, b, c = {0}; + uint8_t seed[ENC_SEED_BYTES + FEC_BYTES + IV_BYTES]; + limb_t rounding, out; + size_t j; + limb_t our_rlimb, their_rlimb, delta; + uint8_t pk[PUBLIC_KEY_BYTES], capsule2[CAPSULE_BYTES]; + uint8_t ret, ok, sep, prfk[PRF_KEY_BYTES]; + uint8_t prfout[SHARED_SECRET_BYTES]; + + /* Calculate approximate shared secret */ + threebears_hash_init(&ctx, HASH_PURPOSE_KEYGEN); + cshake256_inc_absorb(&ctx, sk, PRIVATE_KEY_BYTES); + + for (uint8_t i = 0; i < DIM; i++) { + PQCLEAN_BABYBEAR_CLEAN_expand(b, &capsule[i * GF_BYTES]); + noise(ska, &ctx, i); + PQCLEAN_BABYBEAR_CLEAN_mac(c, ska, b); + } + + /* Recover seed from LPR data */ + PQCLEAN_BABYBEAR_CLEAN_canon(c); + rounding = 1 << (LPR_BITS - 1); + out = 0; + for (int32_t i = ENC_BITS - 1; i >= 0; i--) { + j = (size_t) ((i & 1) ? DIGITS - i / 2 - 1 : i / 2); + our_rlimb = (limb_t)(c[j] >> (LGX - LPR_BITS - 1)); + their_rlimb = (limb_t)(lpr_data[i * LPR_BITS / 8] >> ((i * LPR_BITS) % 8)); + delta = (limb_t)(their_rlimb * 2 - our_rlimb + rounding); + out |= (limb_t)(((delta >> LPR_BITS) & 1) << (i % 8)); + if (i % 8 == 0) { + seed[i / 8] = (uint8_t)out; + out = 0; + } + } + PQCLEAN_BABYBEAR_CLEAN_melas_fec_correct(seed, ENC_SEED_BYTES, &seed[ENC_SEED_BYTES]); + + /* Re-encapsulate and check; encapsulate will compute the shared secret */ + //pk[PUBLIC_KEY_BYTES], capsule2[CAPSULE_BYTES]; + PQCLEAN_BABYBEAR_CLEAN_get_pubkey(pk, sk); + memcpy(&seed[ENC_SEED_BYTES], &lpr_data[(ENC_BITS * LPR_BITS + 7) / 8], IV_BYTES); + PQCLEAN_BABYBEAR_CLEAN_encapsulate(shared_secret, capsule2, pk, seed); + + /* Check capsule == capsule2 in constant time */ + ret = 0; + for (size_t i = 0; i < CAPSULE_BYTES; i++) { + ret |= capsule[i] ^ capsule2[i]; + } + ok = (uint8_t)(((int)ret - 1) >> 8); + + /* Calculate PRF key */ + sep = 0xFF; + cshake256_inc_absorb(&ctx, &sep, 1); + cshake256_inc_finalize(&ctx); + cshake256_inc_squeeze(prfk, PRF_KEY_BYTES, &ctx); + + /* Calculate PRF */ + threebears_hash_init(&ctx, HASH_PURPOSE_PRF); + cshake256_inc_absorb(&ctx, prfk, PRF_KEY_BYTES); + cshake256_inc_absorb(&ctx, capsule, CAPSULE_BYTES); + cshake256_inc_finalize(&ctx); + cshake256_inc_squeeze(prfout, SHARED_SECRET_BYTES, &ctx); + + for (size_t i = 0; i < SHARED_SECRET_BYTES; i++) { + shared_secret[i] = (uint8_t)((shared_secret[i] & ok) | (prfout[i] & ~ok)); + } +} diff --git a/crypto_kem/babybear/clean/threebears.h b/crypto_kem/babybear/clean/threebears.h new file mode 100644 index 00000000..1f345b28 --- /dev/null +++ b/crypto_kem/babybear/clean/threebears.h @@ -0,0 +1,60 @@ +#ifndef __THREE_BEARS_BABYBEAR_H__ +#define __THREE_BEARS_BABYBEAR_H__ + +#include /* for size_t */ +#include + +#define BABYBEAR_KEYGEN_SEED_BYTES 40 +#define BABYBEAR_PRIVATE_KEY_BYTES BABYBEAR_KEYGEN_SEED_BYTES +#define BABYBEAR_SHARED_SECRET_BYTES 32 +#define BABYBEAR_ENC_SEED_AND_IV_BYTES 32 +#define BABYBEAR_PUBLIC_KEY_BYTES 804 +#define BABYBEAR_CAPSULE_BYTES 917 + +/** + * Expand a secret seed to a public/private keypair. + * + * @param[out] pk The public key. + * @param[in] sk The private key, which must be uniformly random. + */ +void PQCLEAN_BABYBEAR_CLEAN_get_pubkey ( + uint8_t pk[BABYBEAR_PUBLIC_KEY_BYTES], + const uint8_t sk[BABYBEAR_PRIVATE_KEY_BYTES] +); + +/** + * Create a shared secret using a random seed and another party's public key. + * + * Input and output parameters may not alias. + * + * @param[out] shared_secret The shared secret key. + * @param[out] capsule A ciphertext to send to the other party. + * @param[in] pk The other party's public key. + * @param[in] seed A random seed. + */ +void PQCLEAN_BABYBEAR_CLEAN_encapsulate ( + uint8_t shared_secret[BABYBEAR_SHARED_SECRET_BYTES], + uint8_t capsule[BABYBEAR_CAPSULE_BYTES], + const uint8_t pk[BABYBEAR_PUBLIC_KEY_BYTES], + const uint8_t seed[BABYBEAR_ENC_SEED_AND_IV_BYTES] +); + +/** + * Extract the shared secret from a capsule using the private key. + * Has a negligible but nonzero probability of failure. + * + * Input and output parameters may not alias. + * + * @param[out] shared_secret The shared secret. + * @param[in] capsule The capsule produced by encapsulate_cca2. + * @param[in] sk The private key. + */ +void PQCLEAN_BABYBEAR_CLEAN_decapsulate ( + uint8_t shared_secret[BABYBEAR_SHARED_SECRET_BYTES], + const uint8_t capsule[BABYBEAR_CAPSULE_BYTES], + const uint8_t sk[BABYBEAR_PRIVATE_KEY_BYTES] +); + +void PQCLEAN_BABYBEAR_CLEAN_secure_bzero (void *s, size_t size); + +#endif diff --git a/crypto_kem/mamabear/META.yml b/crypto_kem/mamabear/META.yml new file mode 100644 index 00000000..c9200c4f --- /dev/null +++ b/crypto_kem/mamabear/META.yml @@ -0,0 +1,15 @@ +name: MamaBear +type: kem +claimed-nist-level: 3 +claimed-security: IND-CCA2 +length-public-key: 1194 +length-ciphertext: 1307 +length-secret-key: 40 +length-shared-secret: 32 +nistkat-sha256: 2161de5015dc0477106b71ba17498982f77fae127fce724496c8a587803b1839 +principal-submitters: + - Mike Hamburg +implementations: + - name: clean + version: round2 + diff --git a/crypto_kem/mamabear/clean/LICENSE b/crypto_kem/mamabear/clean/LICENSE new file mode 100644 index 00000000..5fb15a7c --- /dev/null +++ b/crypto_kem/mamabear/clean/LICENSE @@ -0,0 +1,24 @@ +Copyright (c) 2016-2019 Rambus, Inc. +and licensed under the following MIT license. + +The MIT License (MIT) + +Copyright (c) 2016-2019 Rambus Inc. + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. diff --git a/crypto_kem/mamabear/clean/Makefile b/crypto_kem/mamabear/clean/Makefile new file mode 100644 index 00000000..917c0c2e --- /dev/null +++ b/crypto_kem/mamabear/clean/Makefile @@ -0,0 +1,21 @@ +# This Makefile can be used with GNU Make or BSD Make + +LIB=libmamabear_clean.a + +HEADERS = api.h melas_fec.h params.h ring.h threebears.h +OBJECTS = kem.o melas_fec.o ring.o threebears.o + + +CFLAGS=-O3 -Wcast-align=strict -Wall -Wconversion -Wextra -Wpedantic -Wvla -Wmissing-prototypes -Wredundant-decls -std=c99 -I../../../common $(EXTRAFLAGS) + +all: $(LIB) + +%.o: %.c $(HEADERS) + $(CC) $(CFLAGS) -c -o $@ $< + +$(LIB): $(OBJECTS) + $(AR) -r $@ $(OBJECTS) + +clean: + $(RM) $(OBJECTS) + $(RM) $(LIB) diff --git a/crypto_kem/mamabear/clean/Makefile.Microsoft_nmake b/crypto_kem/mamabear/clean/Makefile.Microsoft_nmake new file mode 100644 index 00000000..164de00d --- /dev/null +++ b/crypto_kem/mamabear/clean/Makefile.Microsoft_nmake @@ -0,0 +1,19 @@ +# This Makefile can be used with Microsoft Visual Studio's nmake using the command: +# nmake /f Makefile.Microsoft_nmake + +LIBRARY=libmamabear_clean.lib +OBJECTS = kem.obj melas_fec.obj ring.obj threebears.obj + +CFLAGS=/nologo /I ..\..\..\common /W4 /WX + +all: $(LIBRARY) + +# Make sure objects are recompiled if headers change. +$(OBJECTS): *.h + +$(LIBRARY): $(OBJECTS) + LIB.EXE /NOLOGO /WX /OUT:$@ $** + +clean: + -DEL $(OBJECTS) + -DEL $(LIBRARY) diff --git a/crypto_kem/mamabear/clean/api.h b/crypto_kem/mamabear/clean/api.h new file mode 100644 index 00000000..135b3607 --- /dev/null +++ b/crypto_kem/mamabear/clean/api.h @@ -0,0 +1,18 @@ +#ifndef PQCLEAN_MAMABEAR_CLEAN_API_H +#define PQCLEAN_MAMABEAR_CLEAN_API_H + +#include +#include +#include + +#define PQCLEAN_MAMABEAR_CLEAN_CRYPTO_SECRETKEYBYTES 40 +#define PQCLEAN_MAMABEAR_CLEAN_CRYPTO_PUBLICKEYBYTES 1194 +#define PQCLEAN_MAMABEAR_CLEAN_CRYPTO_BYTES 32 +#define PQCLEAN_MAMABEAR_CLEAN_CRYPTO_CIPHERTEXTBYTES 1307 +#define PQCLEAN_MAMABEAR_CLEAN_CRYPTO_ALGNAME "MamaBear" + +int PQCLEAN_MAMABEAR_CLEAN_crypto_kem_keypair(uint8_t *pk, uint8_t *sk); +int PQCLEAN_MAMABEAR_CLEAN_crypto_kem_enc(uint8_t *ct, uint8_t *ss, const uint8_t *pk); +int PQCLEAN_MAMABEAR_CLEAN_crypto_kem_dec(uint8_t *ss, const uint8_t *ct, const uint8_t *sk); + +#endif diff --git a/crypto_kem/mamabear/clean/kem.c b/crypto_kem/mamabear/clean/kem.c new file mode 100644 index 00000000..9e40065f --- /dev/null +++ b/crypto_kem/mamabear/clean/kem.c @@ -0,0 +1,22 @@ +#include "api.h" +#include "params.h" +#include "randombytes.h" +#include "threebears.h" + +int PQCLEAN_MAMABEAR_CLEAN_crypto_kem_keypair(uint8_t *pk, uint8_t *sk) { + randombytes(sk, PRIVATE_KEY_BYTES); + PQCLEAN_MAMABEAR_CLEAN_get_pubkey(pk, sk); + return 0; +} + +int PQCLEAN_MAMABEAR_CLEAN_crypto_kem_enc(uint8_t *ct, uint8_t *ss, const uint8_t *pk) { + uint8_t seed[ENC_SEED_BYTES + IV_BYTES]; + randombytes(seed, sizeof(seed)); + encapsulate(ss, ct, pk, seed); + return 0; +} + +int PQCLEAN_MAMABEAR_CLEAN_crypto_kem_dec(uint8_t *ss, const uint8_t *ct, const uint8_t *sk) { + PQCLEAN_MAMABEAR_CLEAN_decapsulate(ss, ct, sk); + return 0; +} diff --git a/crypto_kem/mamabear/clean/melas_fec.c b/crypto_kem/mamabear/clean/melas_fec.c new file mode 100644 index 00000000..80e33604 --- /dev/null +++ b/crypto_kem/mamabear/clean/melas_fec.c @@ -0,0 +1,87 @@ +/* Melas forward error correction, reference code (as implemented in the paper) */ +#include "melas_fec.h" + +/* Return s/2^n mod R */ +static fec_gf_t step(size_t n, fec_gf_t R, fec_gf_t s) { + for (; n; n--) { + s = (s ^ ((s & 1) * R)) >> 1; + } + return s; +} + +/* Compute syndrome(data), where data has length len */ +#define syndrome18(data,len) s18update(0,data,len) +static fec_gf_t s18update(fec_gf_t r, const uint8_t *data, size_t len) { + for (size_t i = 0; i < len; i++) { + r = step(8, 0x46231, r ^ data[i]); + } + return r; +} + +/* Append 3 bytes of FEC(data) to data, so that the FEC becomes 0 */ +void PQCLEAN_MAMABEAR_CLEAN_melas_fec_set( + uint8_t out[MELAS_FEC_BYTES], + const uint8_t *data, + size_t len +) { + fec_gf_t fec = syndrome18(data, len); + for (size_t i = 0; i < MELAS_FEC_BYTES; i++, fec >>= 8) { + out[i] = (uint8_t)fec; + } +} + +/* Return a*b mod Q */ +static fec_gf_t mul(fec_gf_t a, fec_gf_t b) { + fec_gf_t r = 0; + for (size_t i = 0; i < 9; i++) { + r ^= ((b >> (8 - i)) & 1) * a; + a = step(1, Q, a); + } + return r; +} + +/* Reverse an 18-bit number x */ +static fec_gf_t reverse18(fec_gf_t x) { + fec_gf_t ret = 0; + for (size_t i = 0; i < 18; i++) { + ret ^= ((x >> i) & 1) << (17 - i); + } + return ret; +} + +/* Correct data to have the given FEC */ +void PQCLEAN_MAMABEAR_CLEAN_melas_fec_correct ( + uint8_t *data, + size_t len, + const uint8_t fec[MELAS_FEC_BYTES] +) { + + fec_gf_t a = s18update(syndrome18(data, len), fec, MELAS_FEC_BYTES); + fec_gf_t c, r, htr; + size_t i; + const uint8_t table[9] = {36, 10, 43, 215, 52, 11, 116, 244, 0}; + fec_gf_t e0, e1; + + /* Form a quadratic equation from the syndrome */ + c = mul(step(9, Q, a), step(9, Q, reverse18(a))); + for (i = 0, r = 0x100; i < 510; i++) { + r = mul(r, c); + } + r = step(17, Q, r); + a = step(511 - (len + MELAS_FEC_BYTES) * 8, Q, a); + + /* Solve using the half trace */ + for (i = 0, htr = 0; i < 9; i++) { + htr ^= ((r >> i) & 1) * table[i]; + } + e0 = mul(a, htr); + e1 = e0 ^ a; + + /* Correct the errors using the locators */ + for (i = 0; i < len; i++) { + data[i] ^= (uint8_t)(e0 & (((e0 & (e0 - 1)) - 1) >> 9)); + data[i] ^= (uint8_t)(e1 & (((e1 & (e1 - 1)) - 1) >> 9)); + e0 = step(8, Q, e0); + e1 = step(8, Q, e1); + } +} diff --git a/crypto_kem/mamabear/clean/melas_fec.h b/crypto_kem/mamabear/clean/melas_fec.h new file mode 100644 index 00000000..410ec288 --- /dev/null +++ b/crypto_kem/mamabear/clean/melas_fec.h @@ -0,0 +1,26 @@ +#ifndef __THREEBEARS_MELAS_FEC_H__ +#define __THREEBEARS_MELAS_FEC_H__ + +#include "api.h" + +#define MELAS_FEC_BYTES 3 +#define MELAS_FEC_BITS 18 + +typedef uint32_t fec_gf_t; +static const fec_gf_t Q = 0x211; + +/* Append 3 bytes of FEC(data) to data, so that the FEC becomes 0 */ +void PQCLEAN_MAMABEAR_CLEAN_melas_fec_set( + uint8_t out[MELAS_FEC_BYTES], + const uint8_t *data, + size_t len +); + +/* Append 3 bytes of FEC(data) to data, so that the FEC becomes 0 */ +void PQCLEAN_MAMABEAR_CLEAN_melas_fec_correct( + uint8_t *data, + size_t len, + const uint8_t fec[MELAS_FEC_BYTES] +); + +#endif diff --git a/crypto_kem/mamabear/clean/params.h b/crypto_kem/mamabear/clean/params.h new file mode 100644 index 00000000..7b875af9 --- /dev/null +++ b/crypto_kem/mamabear/clean/params.h @@ -0,0 +1,29 @@ +#ifndef __THREEBEARS_PARAMS_H__ +#define __THREEBEARS_PARAMS_H__ + +#define VERSION 1 +#define MATRIX_SEED_BYTES 24 +#define ENC_SEED_BYTES 32 +#define IV_BYTES 0 +#define LGX 10 +#define DIGITS 312 +#define DIM 3 +#define VAR_TIMES_128 52 +#define LPR_BITS 4 +#define FEC_BITS 18 +#define CCA 1 +#define SHARED_SECRET_BYTES 32 +#define PRIVATE_KEY_BYTES 40 +#define PRF_KEY_BYTES PRIVATE_KEY_BYTES + +#define BEAR_NAME "MamaBear" +#define encapsulate MamaBear_encapsulate +#define decapsulate MamaBear_decapsulate +#define get_pubkey MamaBear_get_pubkey + +#define GF_BYTES ((LGX*DIGITS+7)/8) +#define PUBLIC_KEY_BYTES (MATRIX_SEED_BYTES + DIM*GF_BYTES) +#define CAPSULE_BYTES \ + (DIM*GF_BYTES + IV_BYTES + ((ENC_SEED_BYTES*8+FEC_BITS)*LPR_BITS+7)/8) + +#endif diff --git a/crypto_kem/mamabear/clean/ring.c b/crypto_kem/mamabear/clean/ring.c new file mode 100644 index 00000000..d744b09e --- /dev/null +++ b/crypto_kem/mamabear/clean/ring.c @@ -0,0 +1,102 @@ +/** Ring arithmetic implementation */ +#include "ring.h" + +/** Multiply and accumulate c += a*b */ +void PQCLEAN_MAMABEAR_CLEAN_mac(gf_t c, const gf_t a, const gf_t b) { + /* Reference non-Karatsuba MAC */ + dslimb_t accum[2 * DIGITS] = {0}; + dslimb_t chain; + size_t i, j; + + /* Initialize accumulator = unclarify(c) */ + for (i = 0; i < DIGITS; i++) { + accum[i + DIGITS / 2] = c[i]; + } + + /* Multiply */ + for (i = 0; i < DIGITS; i++) { + for (j = 0; j < DIGITS; j++) { + accum[i + j] += (dslimb_t)a[i] * b[j]; + } + } + + /* Clarify and reduce */ + for (i = 0; i < DIGITS / 2; i++) { + accum[i + DIGITS / 2] -= accum[i]; + accum[i + DIGITS] += accum[i]; + accum[i + DIGITS / 2] += accum[i + 3 * DIGITS / 2]; + accum[i + DIGITS] += accum[i + 3 * DIGITS / 2]; + } + + /* Carry propagate */ + chain = accum[3 * DIGITS / 2 - 1]; + accum[3 * DIGITS / 2 - 1] = chain & LMASK; + chain >>= LGX; + accum[DIGITS] += chain; + for (i = DIGITS / 2; i < 3 * DIGITS / 2; i++) { + chain += accum[i]; + c[i - DIGITS / 2] = chain & LMASK; + chain >>= LGX; + } + c[0] = (limb_t) (c[0] + chain); + c[DIGITS / 2] = (limb_t) (c[DIGITS / 2] + chain); +} + +/** Reduce a gf_t to canonical form, i.e. strictly less than N. */ +void PQCLEAN_MAMABEAR_CLEAN_canon(gf_t c) { + const limb_t DELTA = (limb_t)1 << (LGX - 1); + slimb_t hi; + dslimb_t scarry; + dlimb_t carry; + + /* Reduce to 0..2p */ + hi = (slimb_t) (c[DIGITS - 1] - DELTA); + c[DIGITS - 1] = (limb_t) ((hi & LMASK) + DELTA); + c[DIGITS / 2] = (limb_t) (c[DIGITS / 2] + (hi >> LGX)); + + /* Strong reduce. First subtract modulus */ + scarry = hi >> LGX; + for (size_t i = 0; i < DIGITS; i++) { + scarry = scarry + (slimb_t)c[i] - modulus(i); + c[i] = scarry & LMASK; + scarry >>= LGX; + } + + /* add it back */ + carry = 0; + for (size_t i = 0; i < DIGITS; i++) { + carry = carry + c[i] + ((dlimb_t)scarry & modulus(i)); + c[i] = carry & LMASK; + carry >>= LGX; + } +} + +/** Serialize a gf_t to bytes */ +void PQCLEAN_MAMABEAR_CLEAN_contract(uint8_t ch[GF_BYTES], gf_t a) { + size_t pos; + + PQCLEAN_MAMABEAR_CLEAN_canon(a); + for (size_t i = 0; i < GF_BYTES; i++) { + pos = (i * 8) / LGX; + ch[i] = (uint8_t)(a[pos] >> ((i * 8) % LGX)); + if (i < GF_BYTES - 1) { + ch[i] |= (uint8_t)(a[pos + 1] << (LGX - ((i * 8) % LGX))); + } + } +} + +/** Deserialize a gf_t from bytes */ +void PQCLEAN_MAMABEAR_CLEAN_expand(gf_t ll, const uint8_t ch[GF_BYTES]) { + limb_t tmp, buffer = 0; + + for (size_t i = 0, j = 0, bbits = 0; i < GF_BYTES; i++) { + tmp = ch[i]; + buffer |= (limb_t)(tmp << bbits); + bbits += 8; + if (bbits >= LGX) { + ll[j++] = buffer & LMASK; + buffer = (limb_t)(tmp >> (LGX - (bbits - 8))); + bbits = bbits - LGX; + } + } +} diff --git a/crypto_kem/mamabear/clean/ring.h b/crypto_kem/mamabear/clean/ring.h new file mode 100644 index 00000000..3e8b1dd1 --- /dev/null +++ b/crypto_kem/mamabear/clean/ring.h @@ -0,0 +1,31 @@ +#ifndef __THREEBEARS_RING_H__ +#define __THREEBEARS_RING_H__ + +#include "api.h" +#include "params.h" + +typedef uint16_t limb_t; +typedef int16_t slimb_t; +typedef uint32_t dlimb_t; +typedef int32_t dslimb_t; +#define LMASK (((limb_t)1<> 8, DIM, + VAR_TIMES_128 - 1, LPR_BITS, FEC_BITS, CCA, 0 /* padding */ + }; + + cshake256_inc_init(ctx, NULL, 0, (const uint8_t *)S, sizeof(S) - 1); + cshake256_inc_absorb(ctx, (const uint8_t *)pblock, sizeof(pblock)); + cshake256_inc_absorb(ctx, &purpose, 1); +} + +/** Sample n gf_t's uniformly from a seed */ +static void uniform(gf_t matrix, const uint8_t *seed, uint8_t iv) { + uint8_t c[GF_BYTES]; + shake256incctx ctx; + + threebears_hash_init(&ctx, HASH_PURPOSE_UNIFORM); + cshake256_inc_absorb(&ctx, seed, MATRIX_SEED_BYTES); + cshake256_inc_absorb(&ctx, &iv, 1); + cshake256_inc_finalize(&ctx); + cshake256_inc_squeeze(c, sizeof(c), &ctx); + PQCLEAN_MAMABEAR_CLEAN_expand(matrix, c); +} + +/** The ThreeBears error distribution */ +static slimb_t psi(uint8_t ci) { + int sample = 0, var = VAR_TIMES_128; + + for (; var > 64; var -= 64, ci = (uint8_t)(ci << 2)) { + sample += ((ci + 64) >> 8) + ((ci - 64) >> 8); + } + return (slimb_t)(sample + ((ci + var) >> 8) + ((ci - var) >> 8)); +} + +/** Sample a vector of n noise elements */ +static void noise(gf_t x, const shake256incctx *ctx, uint8_t iv) { + uint8_t c[DIGITS]; + shake256incctx ctx2; + + memcpy(&ctx2, ctx, sizeof(ctx2)); + cshake256_inc_absorb(&ctx2, &iv, 1); + cshake256_inc_finalize(&ctx2); + cshake256_inc_squeeze(c, DIGITS, &ctx2); + for (size_t i = 0; i < DIGITS; i++) { + x[i] = (limb_t)(psi(c[i]) + modulus(i)); + } +} + +/* Expand public key from private key */ +void PQCLEAN_MAMABEAR_CLEAN_get_pubkey(uint8_t *pk, const uint8_t *sk) { + shake256incctx ctx; + shake256incctx ctx2; + gf_t sk_expanded[DIM], b, c; + + threebears_hash_init(&ctx, HASH_PURPOSE_KEYGEN); + cshake256_inc_absorb(&ctx, sk, PRIVATE_KEY_BYTES); + + memcpy(&ctx2, &ctx, sizeof(ctx2)); + cshake256_inc_finalize(&ctx2); + cshake256_inc_squeeze(pk, MATRIX_SEED_BYTES, &ctx2); + + for (uint8_t i = 0; i < DIM; i++) { + noise(sk_expanded[i], &ctx, i); + } + for (uint8_t i = 0; i < DIM; i++) { + noise(c, &ctx, (uint8_t)(i + DIM)); + for (uint8_t j = 0; j < DIM; j++) { + uniform(b, pk, (uint8_t) (i + DIM * j)); + PQCLEAN_MAMABEAR_CLEAN_mac(c, b, sk_expanded[j]); + } + PQCLEAN_MAMABEAR_CLEAN_contract(&pk[MATRIX_SEED_BYTES + i * GF_BYTES], c); + } +} + +/* Encapsulate a shared secret and return it */ +void PQCLEAN_MAMABEAR_CLEAN_encapsulate( + uint8_t *shared_secret, + uint8_t *capsule, + const uint8_t *pk, + const uint8_t *seed +) { + uint8_t *lpr_data = &capsule[GF_BYTES * DIM]; + shake256incctx ctx; + gf_t sk_expanded[DIM], b, c; + uint8_t tbi[ENC_SEED_BYTES + FEC_BYTES]; + dlimb_t rlimb0, rlimb1; + limb_t h; + uint8_t *iv = &lpr_data[(ENC_BITS * LPR_BITS + 7) / 8]; + + memcpy(iv, &seed[ENC_SEED_BYTES], IV_BYTES); + + threebears_hash_init(&ctx, HASH_PURPOSE_ENCAPS); + cshake256_inc_absorb(&ctx, pk, MATRIX_SEED_BYTES); + cshake256_inc_absorb(&ctx, seed, ENC_SEED_BYTES + IV_BYTES); + + for (uint8_t i = 0; i < DIM; i++) { + noise(sk_expanded[i], &ctx, i); + } + for (uint8_t i = 0; i < DIM; i++) { + noise(c, &ctx, (uint8_t)(i + DIM)); + for (uint8_t j = 0; j < DIM; j++) { + uniform(b, pk, (uint8_t)(j + DIM * i)); + PQCLEAN_MAMABEAR_CLEAN_mac(c, b, sk_expanded[j]); + } + PQCLEAN_MAMABEAR_CLEAN_contract(&capsule[i * GF_BYTES], c); + } + noise(c, &ctx, (uint8_t)(2 * DIM)); + + /* Calculate approximate shared secret */ + for (uint8_t i = 0; i < DIM; i++) { + PQCLEAN_MAMABEAR_CLEAN_expand(b, &pk[MATRIX_SEED_BYTES + i * GF_BYTES]); + PQCLEAN_MAMABEAR_CLEAN_mac(c, b, sk_expanded[i]); + } + PQCLEAN_MAMABEAR_CLEAN_canon(c); + + + memcpy(tbi, seed, ENC_SEED_BYTES); + + PQCLEAN_MAMABEAR_CLEAN_melas_fec_set(&tbi[ENC_SEED_BYTES], tbi, ENC_SEED_BYTES); + + /* Export with rounding */ + for (size_t i = 0; i < ENC_BITS; i += 2) { + h = (limb_t)(tbi[i / 8] >> (i % 8)); + rlimb0 = (dlimb_t)((c[i / 2] >> (LGX - LPR_BITS)) + (h << 3)); + rlimb1 = (dlimb_t)((c[DIGITS - i / 2 - 1] >> (LGX - LPR_BITS)) + ((h >> 1) << 3)); + lpr_data[i / 2] = (uint8_t)((rlimb0 & 0xF) | rlimb1 << 4); + } + + cshake256_inc_finalize(&ctx); + cshake256_inc_squeeze(shared_secret, SHARED_SECRET_BYTES, &ctx); +} + +/* Decapsulate a shared secret and return it */ +void PQCLEAN_MAMABEAR_CLEAN_decapsulate( + uint8_t shared_secret[SHARED_SECRET_BYTES], + const uint8_t capsule[CAPSULE_BYTES], + const uint8_t sk[PRIVATE_KEY_BYTES] +) { + const uint8_t *lpr_data = &capsule[GF_BYTES * DIM]; + shake256incctx ctx; + gf_t ska, b, c = {0}; + uint8_t seed[ENC_SEED_BYTES + FEC_BYTES + IV_BYTES]; + limb_t rounding, out; + size_t j; + limb_t our_rlimb, their_rlimb, delta; + uint8_t pk[PUBLIC_KEY_BYTES], capsule2[CAPSULE_BYTES]; + uint8_t ret, ok, sep, prfk[PRF_KEY_BYTES]; + uint8_t prfout[SHARED_SECRET_BYTES]; + + /* Calculate approximate shared secret */ + threebears_hash_init(&ctx, HASH_PURPOSE_KEYGEN); + cshake256_inc_absorb(&ctx, sk, PRIVATE_KEY_BYTES); + + for (uint8_t i = 0; i < DIM; i++) { + PQCLEAN_MAMABEAR_CLEAN_expand(b, &capsule[i * GF_BYTES]); + noise(ska, &ctx, i); + PQCLEAN_MAMABEAR_CLEAN_mac(c, ska, b); + } + + /* Recover seed from LPR data */ + PQCLEAN_MAMABEAR_CLEAN_canon(c); + rounding = 1 << (LPR_BITS - 1); + out = 0; + for (int32_t i = ENC_BITS - 1; i >= 0; i--) { + j = (size_t) ((i & 1) ? DIGITS - i / 2 - 1 : i / 2); + our_rlimb = (limb_t)(c[j] >> (LGX - LPR_BITS - 1)); + their_rlimb = (limb_t)(lpr_data[i * LPR_BITS / 8] >> ((i * LPR_BITS) % 8)); + delta = (limb_t)(their_rlimb * 2 - our_rlimb + rounding); + out |= (limb_t)(((delta >> LPR_BITS) & 1) << (i % 8)); + if (i % 8 == 0) { + seed[i / 8] = (uint8_t)out; + out = 0; + } + } + PQCLEAN_MAMABEAR_CLEAN_melas_fec_correct(seed, ENC_SEED_BYTES, &seed[ENC_SEED_BYTES]); + + /* Re-encapsulate and check; encapsulate will compute the shared secret */ + //pk[PUBLIC_KEY_BYTES], capsule2[CAPSULE_BYTES]; + PQCLEAN_MAMABEAR_CLEAN_get_pubkey(pk, sk); + memcpy(&seed[ENC_SEED_BYTES], &lpr_data[(ENC_BITS * LPR_BITS + 7) / 8], IV_BYTES); + PQCLEAN_MAMABEAR_CLEAN_encapsulate(shared_secret, capsule2, pk, seed); + + /* Check capsule == capsule2 in constant time */ + ret = 0; + for (size_t i = 0; i < CAPSULE_BYTES; i++) { + ret |= capsule[i] ^ capsule2[i]; + } + ok = (uint8_t)(((int)ret - 1) >> 8); + + /* Calculate PRF key */ + sep = 0xFF; + cshake256_inc_absorb(&ctx, &sep, 1); + cshake256_inc_finalize(&ctx); + cshake256_inc_squeeze(prfk, PRF_KEY_BYTES, &ctx); + + /* Calculate PRF */ + threebears_hash_init(&ctx, HASH_PURPOSE_PRF); + cshake256_inc_absorb(&ctx, prfk, PRF_KEY_BYTES); + cshake256_inc_absorb(&ctx, capsule, CAPSULE_BYTES); + cshake256_inc_finalize(&ctx); + cshake256_inc_squeeze(prfout, SHARED_SECRET_BYTES, &ctx); + + for (size_t i = 0; i < SHARED_SECRET_BYTES; i++) { + shared_secret[i] = (uint8_t)((shared_secret[i] & ok) | (prfout[i] & ~ok)); + } +} diff --git a/crypto_kem/mamabear/clean/threebears.h b/crypto_kem/mamabear/clean/threebears.h new file mode 100644 index 00000000..0d979068 --- /dev/null +++ b/crypto_kem/mamabear/clean/threebears.h @@ -0,0 +1,60 @@ +#ifndef __THREE_BEARS_MAMABEAR_H__ +#define __THREE_BEARS_MAMABEAR_H__ + +#include /* for size_t */ +#include + +#define MAMABEAR_KEYGEN_SEED_BYTES 40 +#define MAMABEAR_PRIVATE_KEY_BYTES MAMABEAR_KEYGEN_SEED_BYTES +#define MAMABEAR_SHARED_SECRET_BYTES 32 +#define MAMABEAR_ENC_SEED_AND_IV_BYTES 32 +#define MAMABEAR_PUBLIC_KEY_BYTES 1194 +#define MAMABEAR_CAPSULE_BYTES 1307 + +/** + * Expand a secret seed to a public/private keypair. + * + * @param[out] pk The public key. + * @param[in] sk The private key, which must be uniformly random. + */ +void MamaBear_get_pubkey ( + uint8_t pk[MAMABEAR_PUBLIC_KEY_BYTES], + const uint8_t sk[MAMABEAR_PRIVATE_KEY_BYTES] +); + +/** + * Create a shared secret using a random seed and another party's public key. + * + * Input and output parameters may not alias. + * + * @param[out] shared_secret The shared secret key. + * @param[out] capsule A ciphertext to send to the other party. + * @param[in] pk The other party's public key. + * @param[in] seed A random seed. + */ +void MamaBear_encapsulate ( + uint8_t shared_secret[MAMABEAR_SHARED_SECRET_BYTES], + uint8_t capsule[MAMABEAR_CAPSULE_BYTES], + const uint8_t pk[MAMABEAR_PUBLIC_KEY_BYTES], + const uint8_t seed[MAMABEAR_ENC_SEED_AND_IV_BYTES] +); + +/** + * Extract the shared secret from a capsule using the private key. + * Has a negligible but nonzero probability of failure. + * + * Input and output parameters may not alias. + * + * @param[out] shared_secret The shared secret. + * @param[in] capsule The capsule produced by encapsulate_cca2. + * @param[in] sk The private key. + */ +void MamaBear_decapsulate ( + uint8_t shared_secret[MAMABEAR_SHARED_SECRET_BYTES], + const uint8_t capsule[MAMABEAR_CAPSULE_BYTES], + const uint8_t sk[MAMABEAR_PRIVATE_KEY_BYTES] +); + +void PQCLEAN_MAMABEAR_CLEAN_secure_bzero (void *s, size_t size); + +#endif diff --git a/crypto_kem/papabear/META.yml b/crypto_kem/papabear/META.yml new file mode 100644 index 00000000..8dbe6cf3 --- /dev/null +++ b/crypto_kem/papabear/META.yml @@ -0,0 +1,15 @@ +name: PapaBear +type: kem +claimed-nist-level: 5 +claimed-security: IND-CCA2 +length-public-key: 1584 +length-ciphertext: 1697 +length-secret-key: 40 +length-shared-secret: 32 +nistkat-sha256: 977429507fa60a24a608193a507e39f4d9deaf4c6359f94f36b2d9ca0897c997 +principal-submitters: + - Mike Hamburg +implementations: + - name: clean + version: round2 + diff --git a/crypto_kem/papabear/clean/LICENSE b/crypto_kem/papabear/clean/LICENSE new file mode 100644 index 00000000..5fb15a7c --- /dev/null +++ b/crypto_kem/papabear/clean/LICENSE @@ -0,0 +1,24 @@ +Copyright (c) 2016-2019 Rambus, Inc. +and licensed under the following MIT license. + +The MIT License (MIT) + +Copyright (c) 2016-2019 Rambus Inc. + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. diff --git a/crypto_kem/papabear/clean/Makefile b/crypto_kem/papabear/clean/Makefile new file mode 100644 index 00000000..f11aefe7 --- /dev/null +++ b/crypto_kem/papabear/clean/Makefile @@ -0,0 +1,21 @@ +# This Makefile can be used with GNU Make or BSD Make + +LIB=libpapabear_clean.a + +HEADERS = api.h melas_fec.h params.h ring.h threebears.h +OBJECTS = kem.o melas_fec.o ring.o threebears.o + + +CFLAGS=-O3 -Wcast-align=strict -Wall -Wconversion -Wextra -Wpedantic -Wvla -Wmissing-prototypes -Wredundant-decls -std=c99 -I../../../common $(EXTRAFLAGS) + +all: $(LIB) + +%.o: %.c $(HEADERS) + $(CC) $(CFLAGS) -c -o $@ $< + +$(LIB): $(OBJECTS) + $(AR) -r $@ $(OBJECTS) + +clean: + $(RM) $(OBJECTS) + $(RM) $(LIB) diff --git a/crypto_kem/papabear/clean/Makefile.Microsoft_nmake b/crypto_kem/papabear/clean/Makefile.Microsoft_nmake new file mode 100644 index 00000000..87462c40 --- /dev/null +++ b/crypto_kem/papabear/clean/Makefile.Microsoft_nmake @@ -0,0 +1,19 @@ +# This Makefile can be used with Microsoft Visual Studio's nmake using the command: +# nmake /f Makefile.Microsoft_nmake + +LIBRARY=libpapabear_clean.lib +OBJECTS = kem.obj melas_fec.obj ring.obj threebears.obj + +CFLAGS=/nologo /I ..\..\..\common /W4 /WX + +all: $(LIBRARY) + +# Make sure objects are recompiled if headers change. +$(OBJECTS): *.h + +$(LIBRARY): $(OBJECTS) + LIB.EXE /NOLOGO /WX /OUT:$@ $** + +clean: + -DEL $(OBJECTS) + -DEL $(LIBRARY) diff --git a/crypto_kem/papabear/clean/api.h b/crypto_kem/papabear/clean/api.h new file mode 100644 index 00000000..c872e00c --- /dev/null +++ b/crypto_kem/papabear/clean/api.h @@ -0,0 +1,18 @@ +#ifndef PQCLEAN_PAPABEAR_CLEAN_API_H +#define PQCLEAN_PAPABEAR_CLEAN_API_H + +#include +#include +#include + +#define PQCLEAN_PAPABEAR_CLEAN_CRYPTO_SECRETKEYBYTES 40 +#define PQCLEAN_PAPABEAR_CLEAN_CRYPTO_PUBLICKEYBYTES 1584 +#define PQCLEAN_PAPABEAR_CLEAN_CRYPTO_BYTES 32 +#define PQCLEAN_PAPABEAR_CLEAN_CRYPTO_CIPHERTEXTBYTES 1697 +#define PQCLEAN_PAPABEAR_CLEAN_CRYPTO_ALGNAME "PapaBear" + +int PQCLEAN_PAPABEAR_CLEAN_crypto_kem_keypair(uint8_t *pk, uint8_t *sk); +int PQCLEAN_PAPABEAR_CLEAN_crypto_kem_enc(uint8_t *ct, uint8_t *ss, const uint8_t *pk); +int PQCLEAN_PAPABEAR_CLEAN_crypto_kem_dec(uint8_t *ss, const uint8_t *ct, const uint8_t *sk); + +#endif diff --git a/crypto_kem/papabear/clean/kem.c b/crypto_kem/papabear/clean/kem.c new file mode 100644 index 00000000..908d3118 --- /dev/null +++ b/crypto_kem/papabear/clean/kem.c @@ -0,0 +1,22 @@ +#include "api.h" +#include "params.h" +#include "randombytes.h" +#include "threebears.h" + +int PQCLEAN_PAPABEAR_CLEAN_crypto_kem_keypair(uint8_t *pk, uint8_t *sk) { + randombytes(sk, PRIVATE_KEY_BYTES); + PQCLEAN_PAPABEAR_CLEAN_get_pubkey(pk, sk); + return 0; +} + +int PQCLEAN_PAPABEAR_CLEAN_crypto_kem_enc(uint8_t *ct, uint8_t *ss, const uint8_t *pk) { + uint8_t seed[ENC_SEED_BYTES + IV_BYTES]; + randombytes(seed, sizeof(seed)); + encapsulate(ss, ct, pk, seed); + return 0; +} + +int PQCLEAN_PAPABEAR_CLEAN_crypto_kem_dec(uint8_t *ss, const uint8_t *ct, const uint8_t *sk) { + PQCLEAN_PAPABEAR_CLEAN_decapsulate(ss, ct, sk); + return 0; +} diff --git a/crypto_kem/papabear/clean/melas_fec.c b/crypto_kem/papabear/clean/melas_fec.c new file mode 100644 index 00000000..2e271067 --- /dev/null +++ b/crypto_kem/papabear/clean/melas_fec.c @@ -0,0 +1,87 @@ +/* Melas forward error correction, reference code (as implemented in the paper) */ +#include "melas_fec.h" + +/* Return s/2^n mod R */ +static fec_gf_t step(size_t n, fec_gf_t R, fec_gf_t s) { + for (; n; n--) { + s = (s ^ ((s & 1) * R)) >> 1; + } + return s; +} + +/* Compute syndrome(data), where data has length len */ +#define syndrome18(data,len) s18update(0,data,len) +static fec_gf_t s18update(fec_gf_t r, const uint8_t *data, size_t len) { + for (size_t i = 0; i < len; i++) { + r = step(8, 0x46231, r ^ data[i]); + } + return r; +} + +/* Append 3 bytes of FEC(data) to data, so that the FEC becomes 0 */ +void PQCLEAN_PAPABEAR_CLEAN_melas_fec_set( + uint8_t out[MELAS_FEC_BYTES], + const uint8_t *data, + size_t len +) { + fec_gf_t fec = syndrome18(data, len); + for (size_t i = 0; i < MELAS_FEC_BYTES; i++, fec >>= 8) { + out[i] = (uint8_t)fec; + } +} + +/* Return a*b mod Q */ +static fec_gf_t mul(fec_gf_t a, fec_gf_t b) { + fec_gf_t r = 0; + for (size_t i = 0; i < 9; i++) { + r ^= ((b >> (8 - i)) & 1) * a; + a = step(1, Q, a); + } + return r; +} + +/* Reverse an 18-bit number x */ +static fec_gf_t reverse18(fec_gf_t x) { + fec_gf_t ret = 0; + for (size_t i = 0; i < 18; i++) { + ret ^= ((x >> i) & 1) << (17 - i); + } + return ret; +} + +/* Correct data to have the given FEC */ +void PQCLEAN_PAPABEAR_CLEAN_melas_fec_correct ( + uint8_t *data, + size_t len, + const uint8_t fec[MELAS_FEC_BYTES] +) { + + fec_gf_t a = s18update(syndrome18(data, len), fec, MELAS_FEC_BYTES); + fec_gf_t c, r, htr; + size_t i; + const uint8_t table[9] = {36, 10, 43, 215, 52, 11, 116, 244, 0}; + fec_gf_t e0, e1; + + /* Form a quadratic equation from the syndrome */ + c = mul(step(9, Q, a), step(9, Q, reverse18(a))); + for (i = 0, r = 0x100; i < 510; i++) { + r = mul(r, c); + } + r = step(17, Q, r); + a = step(511 - (len + MELAS_FEC_BYTES) * 8, Q, a); + + /* Solve using the half trace */ + for (i = 0, htr = 0; i < 9; i++) { + htr ^= ((r >> i) & 1) * table[i]; + } + e0 = mul(a, htr); + e1 = e0 ^ a; + + /* Correct the errors using the locators */ + for (i = 0; i < len; i++) { + data[i] ^= (uint8_t)(e0 & (((e0 & (e0 - 1)) - 1) >> 9)); + data[i] ^= (uint8_t)(e1 & (((e1 & (e1 - 1)) - 1) >> 9)); + e0 = step(8, Q, e0); + e1 = step(8, Q, e1); + } +} diff --git a/crypto_kem/papabear/clean/melas_fec.h b/crypto_kem/papabear/clean/melas_fec.h new file mode 100644 index 00000000..6d2ca1ee --- /dev/null +++ b/crypto_kem/papabear/clean/melas_fec.h @@ -0,0 +1,26 @@ +#ifndef __THREEBEARS_MELAS_FEC_H__ +#define __THREEBEARS_MELAS_FEC_H__ + +#include "api.h" + +#define MELAS_FEC_BYTES 3 +#define MELAS_FEC_BITS 18 + +typedef uint32_t fec_gf_t; +static const fec_gf_t Q = 0x211; + +/* Append 3 bytes of FEC(data) to data, so that the FEC becomes 0 */ +void PQCLEAN_PAPABEAR_CLEAN_melas_fec_set( + uint8_t out[MELAS_FEC_BYTES], + const uint8_t *data, + size_t len +); + +/* Append 3 bytes of FEC(data) to data, so that the FEC becomes 0 */ +void PQCLEAN_PAPABEAR_CLEAN_melas_fec_correct( + uint8_t *data, + size_t len, + const uint8_t fec[MELAS_FEC_BYTES] +); + +#endif diff --git a/crypto_kem/papabear/clean/params.h b/crypto_kem/papabear/clean/params.h new file mode 100644 index 00000000..7d601cc5 --- /dev/null +++ b/crypto_kem/papabear/clean/params.h @@ -0,0 +1,29 @@ +#ifndef __THREEBEARS_PARAMS_H__ +#define __THREEBEARS_PARAMS_H__ + +#define VERSION 1 +#define MATRIX_SEED_BYTES 24 +#define ENC_SEED_BYTES 32 +#define IV_BYTES 0 +#define LGX 10 +#define DIGITS 312 +#define DIM 4 +#define VAR_TIMES_128 40 +#define LPR_BITS 4 +#define FEC_BITS 18 +#define CCA 1 +#define SHARED_SECRET_BYTES 32 +#define PRIVATE_KEY_BYTES 40 +#define PRF_KEY_BYTES PRIVATE_KEY_BYTES + +#define BEAR_NAME "PapaBear" +#define encapsulate PapaBear_encapsulate +#define decapsulate PapaBear_decapsulate +#define get_pubkey PapaBear_get_pubkey + +#define GF_BYTES ((LGX*DIGITS+7)/8) +#define PUBLIC_KEY_BYTES (MATRIX_SEED_BYTES + DIM*GF_BYTES) +#define CAPSULE_BYTES \ + (DIM*GF_BYTES + IV_BYTES + ((ENC_SEED_BYTES*8+FEC_BITS)*LPR_BITS+7)/8) + +#endif diff --git a/crypto_kem/papabear/clean/ring.c b/crypto_kem/papabear/clean/ring.c new file mode 100644 index 00000000..a770260b --- /dev/null +++ b/crypto_kem/papabear/clean/ring.c @@ -0,0 +1,102 @@ +/** Ring arithmetic implementation */ +#include "ring.h" + +/** Multiply and accumulate c += a*b */ +void PQCLEAN_PAPABEAR_CLEAN_mac(gf_t c, const gf_t a, const gf_t b) { + /* Reference non-Karatsuba MAC */ + dslimb_t accum[2 * DIGITS] = {0}; + dslimb_t chain; + size_t i, j; + + /* Initialize accumulator = unclarify(c) */ + for (i = 0; i < DIGITS; i++) { + accum[i + DIGITS / 2] = c[i]; + } + + /* Multiply */ + for (i = 0; i < DIGITS; i++) { + for (j = 0; j < DIGITS; j++) { + accum[i + j] += (dslimb_t)a[i] * b[j]; + } + } + + /* Clarify and reduce */ + for (i = 0; i < DIGITS / 2; i++) { + accum[i + DIGITS / 2] -= accum[i]; + accum[i + DIGITS] += accum[i]; + accum[i + DIGITS / 2] += accum[i + 3 * DIGITS / 2]; + accum[i + DIGITS] += accum[i + 3 * DIGITS / 2]; + } + + /* Carry propagate */ + chain = accum[3 * DIGITS / 2 - 1]; + accum[3 * DIGITS / 2 - 1] = chain & LMASK; + chain >>= LGX; + accum[DIGITS] += chain; + for (i = DIGITS / 2; i < 3 * DIGITS / 2; i++) { + chain += accum[i]; + c[i - DIGITS / 2] = chain & LMASK; + chain >>= LGX; + } + c[0] = (limb_t) (c[0] + chain); + c[DIGITS / 2] = (limb_t) (c[DIGITS / 2] + chain); +} + +/** Reduce a gf_t to canonical form, i.e. strictly less than N. */ +void PQCLEAN_PAPABEAR_CLEAN_canon(gf_t c) { + const limb_t DELTA = (limb_t)1 << (LGX - 1); + slimb_t hi; + dslimb_t scarry; + dlimb_t carry; + + /* Reduce to 0..2p */ + hi = (slimb_t) (c[DIGITS - 1] - DELTA); + c[DIGITS - 1] = (limb_t) ((hi & LMASK) + DELTA); + c[DIGITS / 2] = (limb_t) (c[DIGITS / 2] + (hi >> LGX)); + + /* Strong reduce. First subtract modulus */ + scarry = hi >> LGX; + for (size_t i = 0; i < DIGITS; i++) { + scarry = scarry + (slimb_t)c[i] - modulus(i); + c[i] = scarry & LMASK; + scarry >>= LGX; + } + + /* add it back */ + carry = 0; + for (size_t i = 0; i < DIGITS; i++) { + carry = carry + c[i] + ((dlimb_t)scarry & modulus(i)); + c[i] = carry & LMASK; + carry >>= LGX; + } +} + +/** Serialize a gf_t to bytes */ +void PQCLEAN_PAPABEAR_CLEAN_contract(uint8_t ch[GF_BYTES], gf_t a) { + size_t pos; + + PQCLEAN_PAPABEAR_CLEAN_canon(a); + for (size_t i = 0; i < GF_BYTES; i++) { + pos = (i * 8) / LGX; + ch[i] = (uint8_t)(a[pos] >> ((i * 8) % LGX)); + if (i < GF_BYTES - 1) { + ch[i] |= (uint8_t)(a[pos + 1] << (LGX - ((i * 8) % LGX))); + } + } +} + +/** Deserialize a gf_t from bytes */ +void PQCLEAN_PAPABEAR_CLEAN_expand(gf_t ll, const uint8_t ch[GF_BYTES]) { + limb_t tmp, buffer = 0; + + for (size_t i = 0, j = 0, bbits = 0; i < GF_BYTES; i++) { + tmp = ch[i]; + buffer |= (limb_t)(tmp << bbits); + bbits += 8; + if (bbits >= LGX) { + ll[j++] = buffer & LMASK; + buffer = (limb_t)(tmp >> (LGX - (bbits - 8))); + bbits = bbits - LGX; + } + } +} diff --git a/crypto_kem/papabear/clean/ring.h b/crypto_kem/papabear/clean/ring.h new file mode 100644 index 00000000..aac7f750 --- /dev/null +++ b/crypto_kem/papabear/clean/ring.h @@ -0,0 +1,31 @@ +#ifndef __THREEBEARS_RING_H__ +#define __THREEBEARS_RING_H__ + +#include "api.h" +#include "params.h" + +typedef uint16_t limb_t; +typedef int16_t slimb_t; +typedef uint32_t dlimb_t; +typedef int32_t dslimb_t; +#define LMASK (((limb_t)1<> 8, DIM, + VAR_TIMES_128 - 1, LPR_BITS, FEC_BITS, CCA, 0 /* padding */ + }; + + cshake256_inc_init(ctx, NULL, 0, (const uint8_t *)S, sizeof(S) - 1); + cshake256_inc_absorb(ctx, (const uint8_t *)pblock, sizeof(pblock)); + cshake256_inc_absorb(ctx, &purpose, 1); +} + +/** Sample n gf_t's uniformly from a seed */ +static void uniform(gf_t matrix, const uint8_t *seed, uint8_t iv) { + uint8_t c[GF_BYTES]; + shake256incctx ctx; + + threebears_hash_init(&ctx, HASH_PURPOSE_UNIFORM); + cshake256_inc_absorb(&ctx, seed, MATRIX_SEED_BYTES); + cshake256_inc_absorb(&ctx, &iv, 1); + cshake256_inc_finalize(&ctx); + cshake256_inc_squeeze(c, sizeof(c), &ctx); + PQCLEAN_PAPABEAR_CLEAN_expand(matrix, c); +} + +/** The ThreeBears error distribution */ +static slimb_t psi(uint8_t ci) { + int sample = 0, var = VAR_TIMES_128; + + for (; var > 64; var -= 64, ci = (uint8_t)(ci << 2)) { + sample += ((ci + 64) >> 8) + ((ci - 64) >> 8); + } + return (slimb_t)(sample + ((ci + var) >> 8) + ((ci - var) >> 8)); +} + +/** Sample a vector of n noise elements */ +static void noise(gf_t x, const shake256incctx *ctx, uint8_t iv) { + uint8_t c[DIGITS]; + shake256incctx ctx2; + + memcpy(&ctx2, ctx, sizeof(ctx2)); + cshake256_inc_absorb(&ctx2, &iv, 1); + cshake256_inc_finalize(&ctx2); + cshake256_inc_squeeze(c, DIGITS, &ctx2); + for (size_t i = 0; i < DIGITS; i++) { + x[i] = (limb_t)(psi(c[i]) + modulus(i)); + } +} + +/* Expand public key from private key */ +void PQCLEAN_PAPABEAR_CLEAN_get_pubkey(uint8_t *pk, const uint8_t *sk) { + shake256incctx ctx; + shake256incctx ctx2; + gf_t sk_expanded[DIM], b, c; + + threebears_hash_init(&ctx, HASH_PURPOSE_KEYGEN); + cshake256_inc_absorb(&ctx, sk, PRIVATE_KEY_BYTES); + + memcpy(&ctx2, &ctx, sizeof(ctx2)); + cshake256_inc_finalize(&ctx2); + cshake256_inc_squeeze(pk, MATRIX_SEED_BYTES, &ctx2); + + for (uint8_t i = 0; i < DIM; i++) { + noise(sk_expanded[i], &ctx, i); + } + for (uint8_t i = 0; i < DIM; i++) { + noise(c, &ctx, (uint8_t)(i + DIM)); + for (uint8_t j = 0; j < DIM; j++) { + uniform(b, pk, (uint8_t) (i + DIM * j)); + PQCLEAN_PAPABEAR_CLEAN_mac(c, b, sk_expanded[j]); + } + PQCLEAN_PAPABEAR_CLEAN_contract(&pk[MATRIX_SEED_BYTES + i * GF_BYTES], c); + } +} + +/* Encapsulate a shared secret and return it */ +void PQCLEAN_PAPABEAR_CLEAN_encapsulate( + uint8_t *shared_secret, + uint8_t *capsule, + const uint8_t *pk, + const uint8_t *seed +) { + uint8_t *lpr_data = &capsule[GF_BYTES * DIM]; + shake256incctx ctx; + gf_t sk_expanded[DIM], b, c; + uint8_t tbi[ENC_SEED_BYTES + FEC_BYTES]; + dlimb_t rlimb0, rlimb1; + limb_t h; + uint8_t *iv = &lpr_data[(ENC_BITS * LPR_BITS + 7) / 8]; + + memcpy(iv, &seed[ENC_SEED_BYTES], IV_BYTES); + + threebears_hash_init(&ctx, HASH_PURPOSE_ENCAPS); + cshake256_inc_absorb(&ctx, pk, MATRIX_SEED_BYTES); + cshake256_inc_absorb(&ctx, seed, ENC_SEED_BYTES + IV_BYTES); + + for (uint8_t i = 0; i < DIM; i++) { + noise(sk_expanded[i], &ctx, i); + } + for (uint8_t i = 0; i < DIM; i++) { + noise(c, &ctx, (uint8_t)(i + DIM)); + for (uint8_t j = 0; j < DIM; j++) { + uniform(b, pk, (uint8_t)(j + DIM * i)); + PQCLEAN_PAPABEAR_CLEAN_mac(c, b, sk_expanded[j]); + } + PQCLEAN_PAPABEAR_CLEAN_contract(&capsule[i * GF_BYTES], c); + } + noise(c, &ctx, (uint8_t)(2 * DIM)); + + /* Calculate approximate shared secret */ + for (uint8_t i = 0; i < DIM; i++) { + PQCLEAN_PAPABEAR_CLEAN_expand(b, &pk[MATRIX_SEED_BYTES + i * GF_BYTES]); + PQCLEAN_PAPABEAR_CLEAN_mac(c, b, sk_expanded[i]); + } + PQCLEAN_PAPABEAR_CLEAN_canon(c); + + + memcpy(tbi, seed, ENC_SEED_BYTES); + + PQCLEAN_PAPABEAR_CLEAN_melas_fec_set(&tbi[ENC_SEED_BYTES], tbi, ENC_SEED_BYTES); + + /* Export with rounding */ + for (size_t i = 0; i < ENC_BITS; i += 2) { + h = (limb_t)(tbi[i / 8] >> (i % 8)); + rlimb0 = (dlimb_t)((c[i / 2] >> (LGX - LPR_BITS)) + (h << 3)); + rlimb1 = (dlimb_t)((c[DIGITS - i / 2 - 1] >> (LGX - LPR_BITS)) + ((h >> 1) << 3)); + lpr_data[i / 2] = (uint8_t)((rlimb0 & 0xF) | rlimb1 << 4); + } + + cshake256_inc_finalize(&ctx); + cshake256_inc_squeeze(shared_secret, SHARED_SECRET_BYTES, &ctx); +} + +/* Decapsulate a shared secret and return it */ +void PQCLEAN_PAPABEAR_CLEAN_decapsulate( + uint8_t shared_secret[SHARED_SECRET_BYTES], + const uint8_t capsule[CAPSULE_BYTES], + const uint8_t sk[PRIVATE_KEY_BYTES] +) { + const uint8_t *lpr_data = &capsule[GF_BYTES * DIM]; + shake256incctx ctx; + gf_t ska, b, c = {0}; + uint8_t seed[ENC_SEED_BYTES + FEC_BYTES + IV_BYTES]; + limb_t rounding, out; + size_t j; + limb_t our_rlimb, their_rlimb, delta; + uint8_t pk[PUBLIC_KEY_BYTES], capsule2[CAPSULE_BYTES]; + uint8_t ret, ok, sep, prfk[PRF_KEY_BYTES]; + uint8_t prfout[SHARED_SECRET_BYTES]; + + /* Calculate approximate shared secret */ + threebears_hash_init(&ctx, HASH_PURPOSE_KEYGEN); + cshake256_inc_absorb(&ctx, sk, PRIVATE_KEY_BYTES); + + for (uint8_t i = 0; i < DIM; i++) { + PQCLEAN_PAPABEAR_CLEAN_expand(b, &capsule[i * GF_BYTES]); + noise(ska, &ctx, i); + PQCLEAN_PAPABEAR_CLEAN_mac(c, ska, b); + } + + /* Recover seed from LPR data */ + PQCLEAN_PAPABEAR_CLEAN_canon(c); + rounding = 1 << (LPR_BITS - 1); + out = 0; + for (int32_t i = ENC_BITS - 1; i >= 0; i--) { + j = (size_t) ((i & 1) ? DIGITS - i / 2 - 1 : i / 2); + our_rlimb = (limb_t)(c[j] >> (LGX - LPR_BITS - 1)); + their_rlimb = (limb_t)(lpr_data[i * LPR_BITS / 8] >> ((i * LPR_BITS) % 8)); + delta = (limb_t)(their_rlimb * 2 - our_rlimb + rounding); + out |= (limb_t)(((delta >> LPR_BITS) & 1) << (i % 8)); + if (i % 8 == 0) { + seed[i / 8] = (uint8_t)out; + out = 0; + } + } + PQCLEAN_PAPABEAR_CLEAN_melas_fec_correct(seed, ENC_SEED_BYTES, &seed[ENC_SEED_BYTES]); + + /* Re-encapsulate and check; encapsulate will compute the shared secret */ + //pk[PUBLIC_KEY_BYTES], capsule2[CAPSULE_BYTES]; + PQCLEAN_PAPABEAR_CLEAN_get_pubkey(pk, sk); + memcpy(&seed[ENC_SEED_BYTES], &lpr_data[(ENC_BITS * LPR_BITS + 7) / 8], IV_BYTES); + PQCLEAN_PAPABEAR_CLEAN_encapsulate(shared_secret, capsule2, pk, seed); + + /* Check capsule == capsule2 in constant time */ + ret = 0; + for (size_t i = 0; i < CAPSULE_BYTES; i++) { + ret |= capsule[i] ^ capsule2[i]; + } + ok = (uint8_t)(((int)ret - 1) >> 8); + + /* Calculate PRF key */ + sep = 0xFF; + cshake256_inc_absorb(&ctx, &sep, 1); + cshake256_inc_finalize(&ctx); + cshake256_inc_squeeze(prfk, PRF_KEY_BYTES, &ctx); + + /* Calculate PRF */ + threebears_hash_init(&ctx, HASH_PURPOSE_PRF); + cshake256_inc_absorb(&ctx, prfk, PRF_KEY_BYTES); + cshake256_inc_absorb(&ctx, capsule, CAPSULE_BYTES); + cshake256_inc_finalize(&ctx); + cshake256_inc_squeeze(prfout, SHARED_SECRET_BYTES, &ctx); + + for (size_t i = 0; i < SHARED_SECRET_BYTES; i++) { + shared_secret[i] = (uint8_t)((shared_secret[i] & ok) | (prfout[i] & ~ok)); + } +} diff --git a/crypto_kem/papabear/clean/threebears.h b/crypto_kem/papabear/clean/threebears.h new file mode 100644 index 00000000..d4378696 --- /dev/null +++ b/crypto_kem/papabear/clean/threebears.h @@ -0,0 +1,60 @@ +#ifndef __THREE_BEARS_PAPABEAR_H__ +#define __THREE_BEARS_PAPABEAR_H__ + +#include /* for size_t */ +#include + +#define PAPABEAR_KEYGEN_SEED_BYTES 40 +#define PAPABEAR_PRIVATE_KEY_BYTES PAPABEAR_KEYGEN_SEED_BYTES +#define PAPABEAR_SHARED_SECRET_BYTES 32 +#define PAPABEAR_ENC_SEED_AND_IV_BYTES 32 +#define PAPABEAR_PUBLIC_KEY_BYTES 1584 +#define PAPABEAR_CAPSULE_BYTES 1697 + +/** + * Expand a secret seed to a public/private keypair. + * + * @param[out] pk The public key. + * @param[in] sk The private key, which must be uniformly random. + */ +void PapaBear_get_pubkey ( + uint8_t pk[PAPABEAR_PUBLIC_KEY_BYTES], + const uint8_t sk[PAPABEAR_PRIVATE_KEY_BYTES] +); + +/** + * Create a shared secret using a random seed and another party's public key. + * + * Input and output parameters may not alias. + * + * @param[out] shared_secret The shared secret key. + * @param[out] capsule A ciphertext to send to the other party. + * @param[in] pk The other party's public key. + * @param[in] seed A random seed. + */ +void PapaBear_encapsulate ( + uint8_t shared_secret[PAPABEAR_SHARED_SECRET_BYTES], + uint8_t capsule[PAPABEAR_CAPSULE_BYTES], + const uint8_t pk[PAPABEAR_PUBLIC_KEY_BYTES], + const uint8_t seed[PAPABEAR_ENC_SEED_AND_IV_BYTES] +); + +/** + * Extract the shared secret from a capsule using the private key. + * Has a negligible but nonzero probability of failure. + * + * Input and output parameters may not alias. + * + * @param[out] shared_secret The shared secret. + * @param[in] capsule The capsule produced by encapsulate_cca2. + * @param[in] sk The private key. + */ +void PapaBear_decapsulate ( + uint8_t shared_secret[PAPABEAR_SHARED_SECRET_BYTES], + const uint8_t capsule[PAPABEAR_CAPSULE_BYTES], + const uint8_t sk[PAPABEAR_PRIVATE_KEY_BYTES] +); + +void PQCLEAN_PAPABEAR_CLEAN_secure_bzero (void *s, size_t size); + +#endif