@@ -0,0 +1,14 @@ | |||
name: BabyBearEphem | |||
type: kem | |||
claimed-nist-level: 1 | |||
claimed-security: IND-CPA | |||
length-public-key: 804 | |||
length-ciphertext: 917 | |||
length-secret-key: 40 | |||
length-shared-secret: 32 | |||
nistkat-sha256: 1caf1dc65c7b2923c936ed464574694a8983ed5508dadfc554fd98e1095652e9 | |||
principal-submitters: | |||
- Mike Hamburg | |||
implementations: | |||
- name: clean | |||
version: https://sourceforge.net/p/threebears/code/ci/f4ce0ebfc84a5e01a75bfc8297b6d175e993cfa4/ |
@@ -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. |
@@ -0,0 +1,21 @@ | |||
# This Makefile can be used with GNU Make or BSD Make | |||
LIB=libbabybear-ephem_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 -Wall -Wextra -Wpedantic -Werror -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) |
@@ -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-ephem_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) |
@@ -0,0 +1,18 @@ | |||
#ifndef PQCLEAN_BABYBEAREPHEM_CLEAN_API_H | |||
#define PQCLEAN_BABYBEAREPHEM_CLEAN_API_H | |||
#include <stddef.h> | |||
#include <stdint.h> | |||
#include <string.h> | |||
#define PQCLEAN_BABYBEAREPHEM_CLEAN_CRYPTO_SECRETKEYBYTES 40 | |||
#define PQCLEAN_BABYBEAREPHEM_CLEAN_CRYPTO_PUBLICKEYBYTES 804 | |||
#define PQCLEAN_BABYBEAREPHEM_CLEAN_CRYPTO_BYTES 32 | |||
#define PQCLEAN_BABYBEAREPHEM_CLEAN_CRYPTO_CIPHERTEXTBYTES 917 | |||
#define PQCLEAN_BABYBEAREPHEM_CLEAN_CRYPTO_ALGNAME "BabyBearEphem" | |||
int PQCLEAN_BABYBEAREPHEM_CLEAN_crypto_kem_keypair(uint8_t *pk, uint8_t *sk); | |||
int PQCLEAN_BABYBEAREPHEM_CLEAN_crypto_kem_enc(uint8_t *ct, uint8_t *ss, const uint8_t *pk); | |||
int PQCLEAN_BABYBEAREPHEM_CLEAN_crypto_kem_dec(uint8_t *ss, const uint8_t *ct, const uint8_t *sk); | |||
#endif |
@@ -0,0 +1,22 @@ | |||
#include "api.h" | |||
#include "params.h" | |||
#include "randombytes.h" | |||
#include "threebears.h" | |||
int PQCLEAN_BABYBEAREPHEM_CLEAN_crypto_kem_keypair(uint8_t *pk, uint8_t *sk) { | |||
randombytes(sk, PRIVATE_KEY_BYTES); | |||
PQCLEAN_BABYBEAREPHEM_CLEAN_get_pubkey(pk, sk); | |||
return 0; | |||
} | |||
int PQCLEAN_BABYBEAREPHEM_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_BABYBEAREPHEM_CLEAN_crypto_kem_dec(uint8_t *ss, const uint8_t *ct, const uint8_t *sk) { | |||
PQCLEAN_BABYBEAREPHEM_CLEAN_decapsulate(ss, ct, sk); | |||
return 0; | |||
} |
@@ -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_BABYBEAREPHEM_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_BABYBEAREPHEM_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); | |||
} | |||
} |
@@ -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_BABYBEAREPHEM_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_BABYBEAREPHEM_CLEAN_melas_fec_correct( | |||
uint8_t *data, | |||
size_t len, | |||
const uint8_t fec[MELAS_FEC_BYTES] | |||
); | |||
#endif |
@@ -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 128 | |||
#define LPR_BITS 4 | |||
#define FEC_BITS 18 | |||
#define CCA 0 | |||
#define SHARED_SECRET_BYTES 32 | |||
#define PRIVATE_KEY_BYTES 40 | |||
#define PRF_KEY_BYTES PRIVATE_KEY_BYTES | |||
#define BEAR_NAME "BabyBearEphem" | |||
#define encapsulate PQCLEAN_BABYBEAREPHEM_CLEAN_encapsulate | |||
#define decapsulate PQCLEAN_BABYBEAREPHEM_CLEAN_decapsulate | |||
#define get_pubkey PQCLEAN_BABYBEAREPHEM_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 |
@@ -0,0 +1,107 @@ | |||
/** Ring arithmetic implementation */ | |||
#include "ring.h" | |||
/** Return the i'th limb of the modulus */ | |||
limb_t PQCLEAN_BABYBEAREPHEM_CLEAN_modulus(size_t i) { | |||
return (i == DIGITS / 2) ? LMASK - 1 : LMASK; | |||
} | |||
/** Multiply and accumulate c += a*b */ | |||
void PQCLEAN_BABYBEAREPHEM_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_BABYBEAREPHEM_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] - PQCLEAN_BABYBEAREPHEM_CLEAN_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 & PQCLEAN_BABYBEAREPHEM_CLEAN_modulus(i)); | |||
c[i] = carry & LMASK; | |||
carry >>= LGX; | |||
} | |||
} | |||
/** Serialize a gf_t to bytes */ | |||
void PQCLEAN_BABYBEAREPHEM_CLEAN_contract(uint8_t ch[GF_BYTES], gf_t a) { | |||
size_t pos; | |||
PQCLEAN_BABYBEAREPHEM_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_BABYBEAREPHEM_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; | |||
} | |||
} | |||
} |
@@ -0,0 +1,29 @@ | |||
#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<<LGX)-1) | |||
typedef limb_t gf_t[DIGITS]; | |||
/* Serialize a gf_t */ | |||
void PQCLEAN_BABYBEAREPHEM_CLEAN_contract(uint8_t ch[GF_BYTES], gf_t a); | |||
/* Deserialize a gf_t */ | |||
void PQCLEAN_BABYBEAREPHEM_CLEAN_expand(gf_t ll, const uint8_t ch[GF_BYTES]); | |||
/* Multiply and accumulate c = c + a*b */ | |||
void PQCLEAN_BABYBEAREPHEM_CLEAN_mac(gf_t c, const gf_t a, const gf_t b); | |||
/* Reduce ring element to canonical form */ | |||
void PQCLEAN_BABYBEAREPHEM_CLEAN_canon(gf_t c); | |||
/** Return the i'th limb of the modulus */ | |||
limb_t PQCLEAN_BABYBEAREPHEM_CLEAN_modulus(size_t i); | |||
#endif |
@@ -0,0 +1,210 @@ | |||
/** ThreeBears reference implementation */ | |||
#include "api.h" | |||
#include "melas_fec.h" | |||
#include "params.h" | |||
#include "ring.h" | |||
#include "sp800-185.h" | |||
#include "threebears.h" | |||
#define FEC_BYTES ((FEC_BITS+7)/8) | |||
#define ENC_BITS (ENC_SEED_BYTES*8 + FEC_BITS) | |||
enum { HASH_PURPOSE_UNIFORM = 0, HASH_PURPOSE_KEYGEN = 1, HASH_PURPOSE_ENCAPS = 2, HASH_PURPOSE_PRF = 3 }; | |||
/** Initialize the hash function with a given purpose */ | |||
static void threebears_hash_init( | |||
shake256incctx *ctx, | |||
uint8_t purpose | |||
) { | |||
const unsigned char S[] = "ThreeBears"; | |||
const uint8_t pblock[15] = { | |||
VERSION, PRIVATE_KEY_BYTES, MATRIX_SEED_BYTES, ENC_SEED_BYTES, | |||
IV_BYTES, SHARED_SECRET_BYTES, LGX, DIGITS & 0xFF, DIGITS >> 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_BABYBEAREPHEM_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]) + PQCLEAN_BABYBEAREPHEM_CLEAN_modulus(i)); | |||
} | |||
} | |||
/* Expand public key from private key */ | |||
void PQCLEAN_BABYBEAREPHEM_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_BABYBEAREPHEM_CLEAN_mac(c, b, sk_expanded[j]); | |||
} | |||
PQCLEAN_BABYBEAREPHEM_CLEAN_contract(&pk[MATRIX_SEED_BYTES + i * GF_BYTES], c); | |||
} | |||
} | |||
/* Encapsulate a shared secret and return it */ | |||
void PQCLEAN_BABYBEAREPHEM_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_BABYBEAREPHEM_CLEAN_mac(c, b, sk_expanded[j]); | |||
} | |||
PQCLEAN_BABYBEAREPHEM_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_BABYBEAREPHEM_CLEAN_expand(b, &pk[MATRIX_SEED_BYTES + i * GF_BYTES]); | |||
PQCLEAN_BABYBEAREPHEM_CLEAN_mac(c, b, sk_expanded[i]); | |||
} | |||
PQCLEAN_BABYBEAREPHEM_CLEAN_canon(c); | |||
cshake256_inc_finalize(&ctx); | |||
cshake256_inc_squeeze(tbi, ENC_SEED_BYTES, &ctx); | |||
threebears_hash_init(&ctx, HASH_PURPOSE_ENCAPS); | |||
cshake256_inc_absorb(&ctx, pk, MATRIX_SEED_BYTES); | |||
cshake256_inc_absorb(&ctx, tbi, ENC_SEED_BYTES); | |||
cshake256_inc_absorb(&ctx, iv, IV_BYTES); | |||
PQCLEAN_BABYBEAREPHEM_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_BABYBEAREPHEM_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 matrix_seed[MATRIX_SEED_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_BABYBEAREPHEM_CLEAN_expand(b, &capsule[i * GF_BYTES]); | |||
noise(ska, &ctx, i); | |||
PQCLEAN_BABYBEAREPHEM_CLEAN_mac(c, ska, b); | |||
} | |||
/* Recover seed from LPR data */ | |||
PQCLEAN_BABYBEAREPHEM_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_BABYBEAREPHEM_CLEAN_melas_fec_correct(seed, ENC_SEED_BYTES, &seed[ENC_SEED_BYTES]); | |||
/* Recalculate matrix seed */ | |||
cshake256_inc_finalize(&ctx); | |||
cshake256_inc_squeeze(matrix_seed, MATRIX_SEED_BYTES, &ctx); | |||
/* Re-run the key derivation from encaps */ | |||
threebears_hash_init(&ctx, HASH_PURPOSE_ENCAPS); | |||
cshake256_inc_absorb(&ctx, matrix_seed, MATRIX_SEED_BYTES); | |||
cshake256_inc_absorb(&ctx, seed, ENC_SEED_BYTES); | |||
cshake256_inc_absorb(&ctx, &lpr_data[(ENC_BITS * LPR_BITS + 7) / 8], IV_BYTES); | |||
cshake256_inc_finalize(&ctx); | |||
cshake256_inc_squeeze(shared_secret, SHARED_SECRET_BYTES, &ctx); | |||
} |
@@ -0,0 +1,60 @@ | |||
#ifndef __THREE_BEARS_BABYBEAREPHEM_H__ | |||
#define __THREE_BEARS_BABYBEAREPHEM_H__ | |||
#include <stddef.h> /* for size_t */ | |||
#include <stdint.h> | |||
#define BABYBEAREPHEM_KEYGEN_SEED_BYTES 40 | |||
#define BABYBEAREPHEM_PRIVATE_KEY_BYTES BABYBEAREPHEM_KEYGEN_SEED_BYTES | |||
#define BABYBEAREPHEM_SHARED_SECRET_BYTES 32 | |||
#define BABYBEAREPHEM_ENC_SEED_AND_IV_BYTES 32 | |||
#define BABYBEAREPHEM_PUBLIC_KEY_BYTES 804 | |||
#define BABYBEAREPHEM_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_BABYBEAREPHEM_CLEAN_get_pubkey ( | |||
uint8_t pk[BABYBEAREPHEM_PUBLIC_KEY_BYTES], | |||
const uint8_t sk[BABYBEAREPHEM_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_BABYBEAREPHEM_CLEAN_encapsulate ( | |||
uint8_t shared_secret[BABYBEAREPHEM_SHARED_SECRET_BYTES], | |||
uint8_t capsule[BABYBEAREPHEM_CAPSULE_BYTES], | |||
const uint8_t pk[BABYBEAREPHEM_PUBLIC_KEY_BYTES], | |||
const uint8_t seed[BABYBEAREPHEM_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. | |||
* @return -1 on failure, 0 on success. | |||
* @warning The value of shared_secret must not be used on failure | |||
*/ | |||
void PQCLEAN_BABYBEAREPHEM_CLEAN_decapsulate ( | |||
uint8_t shared_secret[BABYBEAREPHEM_SHARED_SECRET_BYTES], | |||
const uint8_t capsule[BABYBEAREPHEM_CAPSULE_BYTES], | |||
const uint8_t sk[BABYBEAREPHEM_PRIVATE_KEY_BYTES] | |||
); | |||
#endif |
@@ -55,6 +55,4 @@ void PQCLEAN_BABYBEAR_CLEAN_decapsulate ( | |||
const uint8_t sk[BABYBEAR_PRIVATE_KEY_BYTES] | |||
); | |||
void PQCLEAN_BABYBEAR_CLEAN_secure_bzero (void *s, size_t size); | |||
#endif |
@@ -0,0 +1,14 @@ | |||
name: MamaBearEphem | |||
type: kem | |||
claimed-nist-level: 3 | |||
claimed-security: IND-CPA | |||
length-public-key: 1194 | |||
length-ciphertext: 1307 | |||
length-secret-key: 40 | |||
length-shared-secret: 32 | |||
nistkat-sha256: ef94f0f6471a1276efd9e019195489661c2356027fc2e8163e3718a1df027123 | |||
principal-submitters: | |||
- Mike Hamburg | |||
implementations: | |||
- name: clean | |||
version: https://sourceforge.net/p/threebears/code/ci/f4ce0ebfc84a5e01a75bfc8297b6d175e993cfa4/ |
@@ -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. |
@@ -0,0 +1,21 @@ | |||
# This Makefile can be used with GNU Make or BSD Make | |||
LIB=libmamabear-ephem_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 -Wall -Wextra -Wpedantic -Werror -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) |
@@ -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-ephem_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) |
@@ -0,0 +1,18 @@ | |||
#ifndef PQCLEAN_MAMABEAREPHEM_CLEAN_API_H | |||
#define PQCLEAN_MAMABEAREPHEM_CLEAN_API_H | |||
#include <stddef.h> | |||
#include <stdint.h> | |||
#include <string.h> | |||
#define PQCLEAN_MAMABEAREPHEM_CLEAN_CRYPTO_SECRETKEYBYTES 40 | |||
#define PQCLEAN_MAMABEAREPHEM_CLEAN_CRYPTO_PUBLICKEYBYTES 1194 | |||
#define PQCLEAN_MAMABEAREPHEM_CLEAN_CRYPTO_BYTES 32 | |||
#define PQCLEAN_MAMABEAREPHEM_CLEAN_CRYPTO_CIPHERTEXTBYTES 1307 | |||
#define PQCLEAN_MAMABEAREPHEM_CLEAN_CRYPTO_ALGNAME "MamaBearEphem" | |||
int PQCLEAN_MAMABEAREPHEM_CLEAN_crypto_kem_keypair(uint8_t *pk, uint8_t *sk); | |||
int PQCLEAN_MAMABEAREPHEM_CLEAN_crypto_kem_enc(uint8_t *ct, uint8_t *ss, const uint8_t *pk); | |||
int PQCLEAN_MAMABEAREPHEM_CLEAN_crypto_kem_dec(uint8_t *ss, const uint8_t *ct, const uint8_t *sk); | |||
#endif |
@@ -0,0 +1,22 @@ | |||
#include "api.h" | |||
#include "params.h" | |||
#include "randombytes.h" | |||
#include "threebears.h" | |||
int PQCLEAN_MAMABEAREPHEM_CLEAN_crypto_kem_keypair(uint8_t *pk, uint8_t *sk) { | |||
randombytes(sk, PRIVATE_KEY_BYTES); | |||
PQCLEAN_MAMABEAREPHEM_CLEAN_get_pubkey(pk, sk); | |||
return 0; | |||
} | |||
int PQCLEAN_MAMABEAREPHEM_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_MAMABEAREPHEM_CLEAN_crypto_kem_dec(uint8_t *ss, const uint8_t *ct, const uint8_t *sk) { | |||
PQCLEAN_MAMABEAREPHEM_CLEAN_decapsulate(ss, ct, sk); | |||
return 0; | |||
} |
@@ -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_MAMABEAREPHEM_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_MAMABEAREPHEM_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); | |||
} | |||
} |
@@ -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_MAMABEAREPHEM_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_MAMABEAREPHEM_CLEAN_melas_fec_correct( | |||
uint8_t *data, | |||
size_t len, | |||
const uint8_t fec[MELAS_FEC_BYTES] | |||
); | |||
#endif |
@@ -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 112 | |||
#define LPR_BITS 4 | |||
#define FEC_BITS 18 | |||
#define CCA 0 | |||
#define SHARED_SECRET_BYTES 32 | |||
#define PRIVATE_KEY_BYTES 40 | |||
#define PRF_KEY_BYTES PRIVATE_KEY_BYTES | |||
#define BEAR_NAME "MamaBearEphem" | |||
#define encapsulate PQCLEAN_MAMABEAREPHEM_CLEAN_encapsulate | |||
#define decapsulate PQCLEAN_MAMABEAREPHEM_CLEAN_decapsulate | |||
#define get_pubkey PQCLEAN_MAMABEAREPHEM_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 |
@@ -0,0 +1,107 @@ | |||
/** Ring arithmetic implementation */ | |||
#include "ring.h" | |||
/** Return the i'th limb of the modulus */ | |||
limb_t PQCLEAN_MAMABEAREPHEM_CLEAN_modulus(size_t i) { | |||
return (i == DIGITS / 2) ? LMASK - 1 : LMASK; | |||
} | |||
/** Multiply and accumulate c += a*b */ | |||
void PQCLEAN_MAMABEAREPHEM_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_MAMABEAREPHEM_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] - PQCLEAN_MAMABEAREPHEM_CLEAN_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 & PQCLEAN_MAMABEAREPHEM_CLEAN_modulus(i)); | |||
c[i] = carry & LMASK; | |||
carry >>= LGX; | |||
} | |||
} | |||
/** Serialize a gf_t to bytes */ | |||
void PQCLEAN_MAMABEAREPHEM_CLEAN_contract(uint8_t ch[GF_BYTES], gf_t a) { | |||
size_t pos; | |||
PQCLEAN_MAMABEAREPHEM_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_MAMABEAREPHEM_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; | |||
} | |||
} | |||
} |
@@ -0,0 +1,29 @@ | |||
#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<<LGX)-1) | |||
typedef limb_t gf_t[DIGITS]; | |||
/* Serialize a gf_t */ | |||
void PQCLEAN_MAMABEAREPHEM_CLEAN_contract(uint8_t ch[GF_BYTES], gf_t a); | |||
/* Deserialize a gf_t */ | |||
void PQCLEAN_MAMABEAREPHEM_CLEAN_expand(gf_t ll, const uint8_t ch[GF_BYTES]); | |||
/* Multiply and accumulate c = c + a*b */ | |||
void PQCLEAN_MAMABEAREPHEM_CLEAN_mac(gf_t c, const gf_t a, const gf_t b); | |||
/* Reduce ring element to canonical form */ | |||
void PQCLEAN_MAMABEAREPHEM_CLEAN_canon(gf_t c); | |||
/** Return the i'th limb of the modulus */ | |||
limb_t PQCLEAN_MAMABEAREPHEM_CLEAN_modulus(size_t i); | |||
#endif |
@@ -0,0 +1,210 @@ | |||
/** ThreeBears reference implementation */ | |||
#include "api.h" | |||
#include "melas_fec.h" | |||
#include "params.h" | |||
#include "ring.h" | |||
#include "sp800-185.h" | |||
#include "threebears.h" | |||
#define FEC_BYTES ((FEC_BITS+7)/8) | |||
#define ENC_BITS (ENC_SEED_BYTES*8 + FEC_BITS) | |||
enum { HASH_PURPOSE_UNIFORM = 0, HASH_PURPOSE_KEYGEN = 1, HASH_PURPOSE_ENCAPS = 2, HASH_PURPOSE_PRF = 3 }; | |||
/** Initialize the hash function with a given purpose */ | |||
static void threebears_hash_init( | |||
shake256incctx *ctx, | |||
uint8_t purpose | |||
) { | |||
const unsigned char S[] = "ThreeBears"; | |||
const uint8_t pblock[15] = { | |||
VERSION, PRIVATE_KEY_BYTES, MATRIX_SEED_BYTES, ENC_SEED_BYTES, | |||
IV_BYTES, SHARED_SECRET_BYTES, LGX, DIGITS & 0xFF, DIGITS >> 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_MAMABEAREPHEM_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]) + PQCLEAN_MAMABEAREPHEM_CLEAN_modulus(i)); | |||
} | |||
} | |||
/* Expand public key from private key */ | |||
void PQCLEAN_MAMABEAREPHEM_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_MAMABEAREPHEM_CLEAN_mac(c, b, sk_expanded[j]); | |||
} | |||
PQCLEAN_MAMABEAREPHEM_CLEAN_contract(&pk[MATRIX_SEED_BYTES + i * GF_BYTES], c); | |||
} | |||
} | |||
/* Encapsulate a shared secret and return it */ | |||
void PQCLEAN_MAMABEAREPHEM_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_MAMABEAREPHEM_CLEAN_mac(c, b, sk_expanded[j]); | |||
} | |||
PQCLEAN_MAMABEAREPHEM_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_MAMABEAREPHEM_CLEAN_expand(b, &pk[MATRIX_SEED_BYTES + i * GF_BYTES]); | |||
PQCLEAN_MAMABEAREPHEM_CLEAN_mac(c, b, sk_expanded[i]); | |||
} | |||
PQCLEAN_MAMABEAREPHEM_CLEAN_canon(c); | |||
cshake256_inc_finalize(&ctx); | |||
cshake256_inc_squeeze(tbi, ENC_SEED_BYTES, &ctx); | |||
threebears_hash_init(&ctx, HASH_PURPOSE_ENCAPS); | |||
cshake256_inc_absorb(&ctx, pk, MATRIX_SEED_BYTES); | |||
cshake256_inc_absorb(&ctx, tbi, ENC_SEED_BYTES); | |||
cshake256_inc_absorb(&ctx, iv, IV_BYTES); | |||
PQCLEAN_MAMABEAREPHEM_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_MAMABEAREPHEM_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 matrix_seed[MATRIX_SEED_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_MAMABEAREPHEM_CLEAN_expand(b, &capsule[i * GF_BYTES]); | |||
noise(ska, &ctx, i); | |||
PQCLEAN_MAMABEAREPHEM_CLEAN_mac(c, ska, b); | |||
} | |||
/* Recover seed from LPR data */ | |||
PQCLEAN_MAMABEAREPHEM_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_MAMABEAREPHEM_CLEAN_melas_fec_correct(seed, ENC_SEED_BYTES, &seed[ENC_SEED_BYTES]); | |||
/* Recalculate matrix seed */ | |||
cshake256_inc_finalize(&ctx); | |||
cshake256_inc_squeeze(matrix_seed, MATRIX_SEED_BYTES, &ctx); | |||
/* Re-run the key derivation from encaps */ | |||
threebears_hash_init(&ctx, HASH_PURPOSE_ENCAPS); | |||
cshake256_inc_absorb(&ctx, matrix_seed, MATRIX_SEED_BYTES); | |||
cshake256_inc_absorb(&ctx, seed, ENC_SEED_BYTES); | |||
cshake256_inc_absorb(&ctx, &lpr_data[(ENC_BITS * LPR_BITS + 7) / 8], IV_BYTES); | |||
cshake256_inc_finalize(&ctx); | |||
cshake256_inc_squeeze(shared_secret, SHARED_SECRET_BYTES, &ctx); | |||
} |
@@ -0,0 +1,60 @@ | |||
#ifndef __THREE_BEARS_MAMABEAREPHEM_H__ | |||
#define __THREE_BEARS_MAMABEAREPHEM_H__ | |||
#include <stddef.h> /* for size_t */ | |||
#include <stdint.h> | |||
#define MAMABEAREPHEM_KEYGEN_SEED_BYTES 40 | |||
#define MAMABEAREPHEM_PRIVATE_KEY_BYTES MAMABEAREPHEM_KEYGEN_SEED_BYTES | |||
#define MAMABEAREPHEM_SHARED_SECRET_BYTES 32 | |||
#define MAMABEAREPHEM_ENC_SEED_AND_IV_BYTES 32 | |||
#define MAMABEAREPHEM_PUBLIC_KEY_BYTES 1194 | |||
#define MAMABEAREPHEM_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 PQCLEAN_MAMABEAREPHEM_CLEAN_get_pubkey ( | |||
uint8_t pk[MAMABEAREPHEM_PUBLIC_KEY_BYTES], | |||
const uint8_t sk[MAMABEAREPHEM_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_MAMABEAREPHEM_CLEAN_encapsulate ( | |||
uint8_t shared_secret[MAMABEAREPHEM_SHARED_SECRET_BYTES], | |||
uint8_t capsule[MAMABEAREPHEM_CAPSULE_BYTES], | |||
const uint8_t pk[MAMABEAREPHEM_PUBLIC_KEY_BYTES], | |||
const uint8_t seed[MAMABEAREPHEM_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. | |||
* @return -1 on failure, 0 on success. | |||
* @warning The value of shared_secret must not be used on failure | |||
*/ | |||
void PQCLEAN_MAMABEAREPHEM_CLEAN_decapsulate ( | |||
uint8_t shared_secret[MAMABEAREPHEM_SHARED_SECRET_BYTES], | |||
const uint8_t capsule[MAMABEAREPHEM_CAPSULE_BYTES], | |||
const uint8_t sk[MAMABEAREPHEM_PRIVATE_KEY_BYTES] | |||
); | |||
#endif |
@@ -55,6 +55,4 @@ void PQCLEAN_MAMABEAR_CLEAN_decapsulate ( | |||
const uint8_t sk[MAMABEAR_PRIVATE_KEY_BYTES] | |||
); | |||
void PQCLEAN_MAMABEAR_CLEAN_secure_bzero (void *s, size_t size); | |||
#endif |
@@ -0,0 +1,14 @@ | |||
name: PapaBearEphem | |||
type: kem | |||
claimed-nist-level: 5 | |||
claimed-security: IND-CPA | |||
length-public-key: 1584 | |||
length-ciphertext: 1697 | |||
length-secret-key: 40 | |||
length-shared-secret: 32 | |||
nistkat-sha256: afe40a1172ab5f4f87135297e0a7c67047d21c87f33ab518864c030820c3674d | |||
principal-submitters: | |||
- Mike Hamburg | |||
implementations: | |||
- name: clean | |||
version: https://sourceforge.net/p/threebears/code/ci/f4ce0ebfc84a5e01a75bfc8297b6d175e993cfa4/ |
@@ -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. |
@@ -0,0 +1,21 @@ | |||
# This Makefile can be used with GNU Make or BSD Make | |||
LIB=libpapabear-ephem_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 -Wall -Wextra -Wpedantic -Werror -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) |
@@ -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-ephem_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) |
@@ -0,0 +1,18 @@ | |||
#ifndef PQCLEAN_PAPABEAREPHEM_CLEAN_API_H | |||
#define PQCLEAN_PAPABEAREPHEM_CLEAN_API_H | |||
#include <stddef.h> | |||
#include <stdint.h> | |||
#include <string.h> | |||
#define PQCLEAN_PAPABEAREPHEM_CLEAN_CRYPTO_SECRETKEYBYTES 40 | |||
#define PQCLEAN_PAPABEAREPHEM_CLEAN_CRYPTO_PUBLICKEYBYTES 1584 | |||
#define PQCLEAN_PAPABEAREPHEM_CLEAN_CRYPTO_BYTES 32 | |||
#define PQCLEAN_PAPABEAREPHEM_CLEAN_CRYPTO_CIPHERTEXTBYTES 1697 | |||
#define PQCLEAN_PAPABEAREPHEM_CLEAN_CRYPTO_ALGNAME "PapaBearEphem" | |||
int PQCLEAN_PAPABEAREPHEM_CLEAN_crypto_kem_keypair(uint8_t *pk, uint8_t *sk); | |||
int PQCLEAN_PAPABEAREPHEM_CLEAN_crypto_kem_enc(uint8_t *ct, uint8_t *ss, const uint8_t *pk); | |||
int PQCLEAN_PAPABEAREPHEM_CLEAN_crypto_kem_dec(uint8_t *ss, const uint8_t *ct, const uint8_t *sk); | |||
#endif |
@@ -0,0 +1,22 @@ | |||
#include "api.h" | |||
#include "params.h" | |||
#include "randombytes.h" | |||
#include "threebears.h" | |||
int PQCLEAN_PAPABEAREPHEM_CLEAN_crypto_kem_keypair(uint8_t *pk, uint8_t *sk) { | |||
randombytes(sk, PRIVATE_KEY_BYTES); | |||
PQCLEAN_PAPABEAREPHEM_CLEAN_get_pubkey(pk, sk); | |||
return 0; | |||
} | |||
int PQCLEAN_PAPABEAREPHEM_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_PAPABEAREPHEM_CLEAN_crypto_kem_dec(uint8_t *ss, const uint8_t *ct, const uint8_t *sk) { | |||
PQCLEAN_PAPABEAREPHEM_CLEAN_decapsulate(ss, ct, sk); | |||
return 0; | |||
} |
@@ -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_PAPABEAREPHEM_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_PAPABEAREPHEM_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); | |||
} | |||
} |
@@ -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_PAPABEAREPHEM_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_PAPABEAREPHEM_CLEAN_melas_fec_correct( | |||
uint8_t *data, | |||
size_t len, | |||
const uint8_t fec[MELAS_FEC_BYTES] | |||
); | |||
#endif |
@@ -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 96 | |||
#define LPR_BITS 4 | |||
#define FEC_BITS 18 | |||
#define CCA 0 | |||
#define SHARED_SECRET_BYTES 32 | |||
#define PRIVATE_KEY_BYTES 40 | |||
#define PRF_KEY_BYTES PRIVATE_KEY_BYTES | |||
#define BEAR_NAME "PapaBearEphem" | |||
#define encapsulate PQCLEAN_PAPABEAREPHEM_CLEAN_encapsulate | |||
#define decapsulate PQCLEAN_PAPABEAREPHEM_CLEAN_decapsulate | |||
#define get_pubkey PQCLEAN_PAPABEAREPHEM_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 |
@@ -0,0 +1,107 @@ | |||
/** Ring arithmetic implementation */ | |||
#include "ring.h" | |||
/** Return the i'th limb of the modulus */ | |||
limb_t PQCLEAN_PAPABEAREPHEM_CLEAN_modulus(size_t i) { | |||
return (i == DIGITS / 2) ? LMASK - 1 : LMASK; | |||
} | |||
/** Multiply and accumulate c += a*b */ | |||
void PQCLEAN_PAPABEAREPHEM_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_PAPABEAREPHEM_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] - PQCLEAN_PAPABEAREPHEM_CLEAN_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 & PQCLEAN_PAPABEAREPHEM_CLEAN_modulus(i)); | |||
c[i] = carry & LMASK; | |||
carry >>= LGX; | |||
} | |||
} | |||
/** Serialize a gf_t to bytes */ | |||
void PQCLEAN_PAPABEAREPHEM_CLEAN_contract(uint8_t ch[GF_BYTES], gf_t a) { | |||
size_t pos; | |||
PQCLEAN_PAPABEAREPHEM_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_PAPABEAREPHEM_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; | |||
} | |||
} | |||
} |
@@ -0,0 +1,29 @@ | |||
#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<<LGX)-1) | |||
typedef limb_t gf_t[DIGITS]; | |||
/* Serialize a gf_t */ | |||
void PQCLEAN_PAPABEAREPHEM_CLEAN_contract(uint8_t ch[GF_BYTES], gf_t a); | |||
/* Deserialize a gf_t */ | |||
void PQCLEAN_PAPABEAREPHEM_CLEAN_expand(gf_t ll, const uint8_t ch[GF_BYTES]); | |||
/* Multiply and accumulate c = c + a*b */ | |||
void PQCLEAN_PAPABEAREPHEM_CLEAN_mac(gf_t c, const gf_t a, const gf_t b); | |||
/* Reduce ring element to canonical form */ | |||
void PQCLEAN_PAPABEAREPHEM_CLEAN_canon(gf_t c); | |||
/** Return the i'th limb of the modulus */ | |||
limb_t PQCLEAN_PAPABEAREPHEM_CLEAN_modulus(size_t i); | |||
#endif |
@@ -0,0 +1,210 @@ | |||
/** ThreeBears reference implementation */ | |||
#include "api.h" | |||
#include "melas_fec.h" | |||
#include "params.h" | |||
#include "ring.h" | |||
#include "sp800-185.h" | |||
#include "threebears.h" | |||
#define FEC_BYTES ((FEC_BITS+7)/8) | |||
#define ENC_BITS (ENC_SEED_BYTES*8 + FEC_BITS) | |||
enum { HASH_PURPOSE_UNIFORM = 0, HASH_PURPOSE_KEYGEN = 1, HASH_PURPOSE_ENCAPS = 2, HASH_PURPOSE_PRF = 3 }; | |||
/** Initialize the hash function with a given purpose */ | |||
static void threebears_hash_init( | |||
shake256incctx *ctx, | |||
uint8_t purpose | |||
) { | |||
const unsigned char S[] = "ThreeBears"; | |||
const uint8_t pblock[15] = { | |||
VERSION, PRIVATE_KEY_BYTES, MATRIX_SEED_BYTES, ENC_SEED_BYTES, | |||
IV_BYTES, SHARED_SECRET_BYTES, LGX, DIGITS & 0xFF, DIGITS >> 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_PAPABEAREPHEM_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]) + PQCLEAN_PAPABEAREPHEM_CLEAN_modulus(i)); | |||
} | |||
} | |||
/* Expand public key from private key */ | |||
void PQCLEAN_PAPABEAREPHEM_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_PAPABEAREPHEM_CLEAN_mac(c, b, sk_expanded[j]); | |||
} | |||
PQCLEAN_PAPABEAREPHEM_CLEAN_contract(&pk[MATRIX_SEED_BYTES + i * GF_BYTES], c); | |||
} | |||
} | |||
/* Encapsulate a shared secret and return it */ | |||
void PQCLEAN_PAPABEAREPHEM_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_PAPABEAREPHEM_CLEAN_mac(c, b, sk_expanded[j]); | |||
} | |||
PQCLEAN_PAPABEAREPHEM_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_PAPABEAREPHEM_CLEAN_expand(b, &pk[MATRIX_SEED_BYTES + i * GF_BYTES]); | |||
PQCLEAN_PAPABEAREPHEM_CLEAN_mac(c, b, sk_expanded[i]); | |||
} | |||
PQCLEAN_PAPABEAREPHEM_CLEAN_canon(c); | |||
cshake256_inc_finalize(&ctx); | |||
cshake256_inc_squeeze(tbi, ENC_SEED_BYTES, &ctx); | |||
threebears_hash_init(&ctx, HASH_PURPOSE_ENCAPS); | |||
cshake256_inc_absorb(&ctx, pk, MATRIX_SEED_BYTES); | |||
cshake256_inc_absorb(&ctx, tbi, ENC_SEED_BYTES); | |||
cshake256_inc_absorb(&ctx, iv, IV_BYTES); | |||
PQCLEAN_PAPABEAREPHEM_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_PAPABEAREPHEM_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 matrix_seed[MATRIX_SEED_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_PAPABEAREPHEM_CLEAN_expand(b, &capsule[i * GF_BYTES]); | |||
noise(ska, &ctx, i); | |||
PQCLEAN_PAPABEAREPHEM_CLEAN_mac(c, ska, b); | |||
} | |||
/* Recover seed from LPR data */ | |||
PQCLEAN_PAPABEAREPHEM_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_PAPABEAREPHEM_CLEAN_melas_fec_correct(seed, ENC_SEED_BYTES, &seed[ENC_SEED_BYTES]); | |||
/* Recalculate matrix seed */ | |||
cshake256_inc_finalize(&ctx); | |||
cshake256_inc_squeeze(matrix_seed, MATRIX_SEED_BYTES, &ctx); | |||
/* Re-run the key derivation from encaps */ | |||
threebears_hash_init(&ctx, HASH_PURPOSE_ENCAPS); | |||
cshake256_inc_absorb(&ctx, matrix_seed, MATRIX_SEED_BYTES); | |||
cshake256_inc_absorb(&ctx, seed, ENC_SEED_BYTES); | |||
cshake256_inc_absorb(&ctx, &lpr_data[(ENC_BITS * LPR_BITS + 7) / 8], IV_BYTES); | |||
cshake256_inc_finalize(&ctx); | |||
cshake256_inc_squeeze(shared_secret, SHARED_SECRET_BYTES, &ctx); | |||
} |
@@ -0,0 +1,60 @@ | |||
#ifndef __THREE_BEARS_PAPABEAREPHEM_H__ | |||
#define __THREE_BEARS_PAPABEAREPHEM_H__ | |||
#include <stddef.h> /* for size_t */ | |||
#include <stdint.h> | |||
#define PAPABEAREPHEM_KEYGEN_SEED_BYTES 40 | |||
#define PAPABEAREPHEM_PRIVATE_KEY_BYTES PAPABEAREPHEM_KEYGEN_SEED_BYTES | |||
#define PAPABEAREPHEM_SHARED_SECRET_BYTES 32 | |||
#define PAPABEAREPHEM_ENC_SEED_AND_IV_BYTES 32 | |||
#define PAPABEAREPHEM_PUBLIC_KEY_BYTES 1584 | |||
#define PAPABEAREPHEM_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 PQCLEAN_PAPABEAREPHEM_CLEAN_get_pubkey ( | |||
uint8_t pk[PAPABEAREPHEM_PUBLIC_KEY_BYTES], | |||
const uint8_t sk[PAPABEAREPHEM_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_PAPABEAREPHEM_CLEAN_encapsulate ( | |||
uint8_t shared_secret[PAPABEAREPHEM_SHARED_SECRET_BYTES], | |||
uint8_t capsule[PAPABEAREPHEM_CAPSULE_BYTES], | |||
const uint8_t pk[PAPABEAREPHEM_PUBLIC_KEY_BYTES], | |||
const uint8_t seed[PAPABEAREPHEM_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. | |||
* @return -1 on failure, 0 on success. | |||
* @warning The value of shared_secret must not be used on failure | |||
*/ | |||
void PQCLEAN_PAPABEAREPHEM_CLEAN_decapsulate ( | |||
uint8_t shared_secret[PAPABEAREPHEM_SHARED_SECRET_BYTES], | |||
const uint8_t capsule[PAPABEAREPHEM_CAPSULE_BYTES], | |||
const uint8_t sk[PAPABEAREPHEM_PRIVATE_KEY_BYTES] | |||
); | |||
#endif |
@@ -55,6 +55,4 @@ void PQCLEAN_PAPABEAR_CLEAN_decapsulate ( | |||
const uint8_t sk[PAPABEAR_PRIVATE_KEY_BYTES] | |||
); | |||
void PQCLEAN_PAPABEAR_CLEAN_secure_bzero (void *s, size_t size); | |||
#endif |
@@ -0,0 +1,21 @@ | |||
consistency_checks: | |||
- source: | |||
scheme: mamabear-ephem | |||
implementation: clean | |||
files: | |||
- kem.c | |||
- melas_fec.c | |||
- melas_fec.h | |||
- ring.c | |||
- ring.h | |||
- threebears.c | |||
- source: | |||
scheme: papabear-ephem | |||
implementation: clean | |||
files: | |||
- kem.c | |||
- melas_fec.c | |||
- melas_fec.h | |||
- ring.c | |||
- ring.h | |||
- threebears.c |
@@ -0,0 +1,21 @@ | |||
consistency_checks: | |||
- source: | |||
scheme: babybear-ephem | |||
implementation: clean | |||
files: | |||
- kem.c | |||
- melas_fec.c | |||
- melas_fec.h | |||
- ring.c | |||
- ring.h | |||
- threebears.c | |||
- source: | |||
scheme: papabear-ephem | |||
implementation: clean | |||
files: | |||
- kem.c | |||
- melas_fec.c | |||
- melas_fec.h | |||
- ring.c | |||
- ring.h | |||
- threebears.c |
@@ -0,0 +1,21 @@ | |||
consistency_checks: | |||
- source: | |||
scheme: babybear-ephem | |||
implementation: clean | |||
files: | |||
- kem.c | |||
- melas_fec.c | |||
- melas_fec.h | |||
- ring.c | |||
- ring.h | |||
- threebears.c | |||
- source: | |||
scheme: mamabear-ephem | |||
implementation: clean | |||
files: | |||
- kem.c | |||
- melas_fec.c | |||
- melas_fec.h | |||
- ring.c | |||
- ring.h | |||
- threebears.c |