Merge pull request #262 from PQClean/threebears

Add ephemeral versions of ThreeBears (Duplicate of #260)
This commit is contained in:
Thom Wiggers 2020-01-17 11:06:09 +01:00 committed by GitHub
commit 8eca85fcd2
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
45 changed files with 2061 additions and 6 deletions

View File

@ -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/

View File

@ -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.

View File

@ -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)

View File

@ -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)

View File

@ -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

View File

@ -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;
}

View File

@ -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);
}
}

View File

@ -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

View File

@ -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

View File

@ -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;
}
}
}

View File

@ -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

View File

@ -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);
}

View File

@ -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

View File

@ -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

View File

@ -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/

View File

@ -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.

View File

@ -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)

View File

@ -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)

View File

@ -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

View File

@ -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;
}

View File

@ -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);
}
}

View File

@ -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

View File

@ -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

View File

@ -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;
}
}
}

View File

@ -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

View File

@ -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);
}

View File

@ -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

View File

@ -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

View File

@ -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/

View File

@ -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.

View File

@ -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)

View File

@ -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)

View File

@ -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

View File

@ -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;
}

View File

@ -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);
}
}

View File

@ -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

View File

@ -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

View File

@ -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;
}
}
}

View File

@ -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

View File

@ -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);
}

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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