1
1
mirror of https://github.com/henrydcase/pqc.git synced 2024-11-26 09:21:28 +00:00

add ThreeBears

This commit is contained in:
Leon Botros 2019-10-24 18:24:45 +02:00 committed by Kris Kwiatkowski
parent d1f2d16f46
commit 3b740033ef
39 changed files with 2037 additions and 0 deletions

View File

@ -0,0 +1,15 @@
name: BabyBear
type: kem
claimed-nist-level: 1
claimed-security: IND-CCA2
length-public-key: 804
length-ciphertext: 917
length-secret-key: 40
length-shared-secret: 32
nistkat-sha256: b8442ffaad8e74c6ebfd75d02e13f8db017a7a6dd8458f5d1a5011de6057d775
principal-submitters:
- Mike Hamburg
implementations:
- name: clean
version: round2

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_clean.a
HEADERS = api.h melas_fec.h params.h ring.h threebears.h
OBJECTS = kem.o melas_fec.o ring.o threebears.o
CFLAGS=-O3 -Wcast-align=strict -Wall -Wconversion -Wextra -Wpedantic -Wvla -Wmissing-prototypes -Wredundant-decls -std=c99 -I../../../common $(EXTRAFLAGS)
all: $(LIB)
%.o: %.c $(HEADERS)
$(CC) $(CFLAGS) -c -o $@ $<
$(LIB): $(OBJECTS)
$(AR) -r $@ $(OBJECTS)
clean:
$(RM) $(OBJECTS)
$(RM) $(LIB)

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_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_BABYBEAR_CLEAN_API_H
#define PQCLEAN_BABYBEAR_CLEAN_API_H
#include <stddef.h>
#include <stdint.h>
#include <string.h>
#define PQCLEAN_BABYBEAR_CLEAN_CRYPTO_SECRETKEYBYTES 40
#define PQCLEAN_BABYBEAR_CLEAN_CRYPTO_PUBLICKEYBYTES 804
#define PQCLEAN_BABYBEAR_CLEAN_CRYPTO_BYTES 32
#define PQCLEAN_BABYBEAR_CLEAN_CRYPTO_CIPHERTEXTBYTES 917
#define PQCLEAN_BABYBEAR_CLEAN_CRYPTO_ALGNAME "BabyBear"
int PQCLEAN_BABYBEAR_CLEAN_crypto_kem_keypair(uint8_t *pk, uint8_t *sk);
int PQCLEAN_BABYBEAR_CLEAN_crypto_kem_enc(uint8_t *ct, uint8_t *ss, const uint8_t *pk);
int PQCLEAN_BABYBEAR_CLEAN_crypto_kem_dec(uint8_t *ss, const uint8_t *ct, const uint8_t *sk);
#endif

View File

@ -0,0 +1,22 @@
#include "api.h"
#include "params.h"
#include "randombytes.h"
#include "threebears.h"
int PQCLEAN_BABYBEAR_CLEAN_crypto_kem_keypair(uint8_t *pk, uint8_t *sk) {
randombytes(sk, PRIVATE_KEY_BYTES);
PQCLEAN_BABYBEAR_CLEAN_get_pubkey(pk, sk);
return 0;
}
int PQCLEAN_BABYBEAR_CLEAN_crypto_kem_enc(uint8_t *ct, uint8_t *ss, const uint8_t *pk) {
uint8_t seed[ENC_SEED_BYTES + IV_BYTES];
randombytes(seed, sizeof(seed));
encapsulate(ss, ct, pk, seed);
return 0;
}
int PQCLEAN_BABYBEAR_CLEAN_crypto_kem_dec(uint8_t *ss, const uint8_t *ct, const uint8_t *sk) {
PQCLEAN_BABYBEAR_CLEAN_decapsulate(ss, ct, sk);
return 0;
}

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_BABYBEAR_CLEAN_melas_fec_set(
uint8_t out[MELAS_FEC_BYTES],
const uint8_t *data,
size_t len
) {
fec_gf_t fec = syndrome18(data, len);
for (size_t i = 0; i < MELAS_FEC_BYTES; i++, fec >>= 8) {
out[i] = (uint8_t)fec;
}
}
/* Return a*b mod Q */
static fec_gf_t mul(fec_gf_t a, fec_gf_t b) {
fec_gf_t r = 0;
for (size_t i = 0; i < 9; i++) {
r ^= ((b >> (8 - i)) & 1) * a;
a = step(1, Q, a);
}
return r;
}
/* Reverse an 18-bit number x */
static fec_gf_t reverse18(fec_gf_t x) {
fec_gf_t ret = 0;
for (size_t i = 0; i < 18; i++) {
ret ^= ((x >> i) & 1) << (17 - i);
}
return ret;
}
/* Correct data to have the given FEC */
void PQCLEAN_BABYBEAR_CLEAN_melas_fec_correct (
uint8_t *data,
size_t len,
const uint8_t fec[MELAS_FEC_BYTES]
) {
fec_gf_t a = s18update(syndrome18(data, len), fec, MELAS_FEC_BYTES);
fec_gf_t c, r, htr;
size_t i;
const uint8_t table[9] = {36, 10, 43, 215, 52, 11, 116, 244, 0};
fec_gf_t e0, e1;
/* Form a quadratic equation from the syndrome */
c = mul(step(9, Q, a), step(9, Q, reverse18(a)));
for (i = 0, r = 0x100; i < 510; i++) {
r = mul(r, c);
}
r = step(17, Q, r);
a = step(511 - (len + MELAS_FEC_BYTES) * 8, Q, a);
/* Solve using the half trace */
for (i = 0, htr = 0; i < 9; i++) {
htr ^= ((r >> i) & 1) * table[i];
}
e0 = mul(a, htr);
e1 = e0 ^ a;
/* Correct the errors using the locators */
for (i = 0; i < len; i++) {
data[i] ^= (uint8_t)(e0 & (((e0 & (e0 - 1)) - 1) >> 9));
data[i] ^= (uint8_t)(e1 & (((e1 & (e1 - 1)) - 1) >> 9));
e0 = step(8, Q, e0);
e1 = step(8, Q, e1);
}
}

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_BABYBEAR_CLEAN_melas_fec_set(
uint8_t out[MELAS_FEC_BYTES],
const uint8_t *data,
size_t len
);
/* Append 3 bytes of FEC(data) to data, so that the FEC becomes 0 */
void PQCLEAN_BABYBEAR_CLEAN_melas_fec_correct(
uint8_t *data,
size_t len,
const uint8_t fec[MELAS_FEC_BYTES]
);
#endif

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 72
#define LPR_BITS 4
#define FEC_BITS 18
#define CCA 1
#define SHARED_SECRET_BYTES 32
#define PRIVATE_KEY_BYTES 40
#define PRF_KEY_BYTES PRIVATE_KEY_BYTES
#define BEAR_NAME "BabyBear"
#define encapsulate PQCLEAN_BABYBEAR_CLEAN_encapsulate
#define decapsulate PQCLEAN_BABYBEAR_CLEAN_decapsulate
#define get_pubkey PQCLEAN_BABYBEAR_CLEAN_get_pubkey
#define GF_BYTES ((LGX*DIGITS+7)/8)
#define PUBLIC_KEY_BYTES (MATRIX_SEED_BYTES + DIM*GF_BYTES)
#define CAPSULE_BYTES \
(DIM*GF_BYTES + IV_BYTES + ((ENC_SEED_BYTES*8+FEC_BITS)*LPR_BITS+7)/8)
#endif

View File

@ -0,0 +1,102 @@
/** Ring arithmetic implementation */
#include "ring.h"
/** Multiply and accumulate c += a*b */
void PQCLEAN_BABYBEAR_CLEAN_mac(gf_t c, const gf_t a, const gf_t b) {
/* Reference non-Karatsuba MAC */
dslimb_t accum[2 * DIGITS] = {0};
dslimb_t chain;
size_t i, j;
/* Initialize accumulator = unclarify(c) */
for (i = 0; i < DIGITS; i++) {
accum[i + DIGITS / 2] = c[i];
}
/* Multiply */
for (i = 0; i < DIGITS; i++) {
for (j = 0; j < DIGITS; j++) {
accum[i + j] += (dslimb_t)a[i] * b[j];
}
}
/* Clarify and reduce */
for (i = 0; i < DIGITS / 2; i++) {
accum[i + DIGITS / 2] -= accum[i];
accum[i + DIGITS] += accum[i];
accum[i + DIGITS / 2] += accum[i + 3 * DIGITS / 2];
accum[i + DIGITS] += accum[i + 3 * DIGITS / 2];
}
/* Carry propagate */
chain = accum[3 * DIGITS / 2 - 1];
accum[3 * DIGITS / 2 - 1] = chain & LMASK;
chain >>= LGX;
accum[DIGITS] += chain;
for (i = DIGITS / 2; i < 3 * DIGITS / 2; i++) {
chain += accum[i];
c[i - DIGITS / 2] = chain & LMASK;
chain >>= LGX;
}
c[0] = (limb_t) (c[0] + chain);
c[DIGITS / 2] = (limb_t) (c[DIGITS / 2] + chain);
}
/** Reduce a gf_t to canonical form, i.e. strictly less than N. */
void PQCLEAN_BABYBEAR_CLEAN_canon(gf_t c) {
const limb_t DELTA = (limb_t)1 << (LGX - 1);
slimb_t hi;
dslimb_t scarry;
dlimb_t carry;
/* Reduce to 0..2p */
hi = (slimb_t) (c[DIGITS - 1] - DELTA);
c[DIGITS - 1] = (limb_t) ((hi & LMASK) + DELTA);
c[DIGITS / 2] = (limb_t) (c[DIGITS / 2] + (hi >> LGX));
/* Strong reduce. First subtract modulus */
scarry = hi >> LGX;
for (size_t i = 0; i < DIGITS; i++) {
scarry = scarry + (slimb_t)c[i] - modulus(i);
c[i] = scarry & LMASK;
scarry >>= LGX;
}
/* add it back */
carry = 0;
for (size_t i = 0; i < DIGITS; i++) {
carry = carry + c[i] + ((dlimb_t)scarry & modulus(i));
c[i] = carry & LMASK;
carry >>= LGX;
}
}
/** Serialize a gf_t to bytes */
void PQCLEAN_BABYBEAR_CLEAN_contract(uint8_t ch[GF_BYTES], gf_t a) {
size_t pos;
PQCLEAN_BABYBEAR_CLEAN_canon(a);
for (size_t i = 0; i < GF_BYTES; i++) {
pos = (i * 8) / LGX;
ch[i] = (uint8_t)(a[pos] >> ((i * 8) % LGX));
if (i < GF_BYTES - 1) {
ch[i] |= (uint8_t)(a[pos + 1] << (LGX - ((i * 8) % LGX)));
}
}
}
/** Deserialize a gf_t from bytes */
void PQCLEAN_BABYBEAR_CLEAN_expand(gf_t ll, const uint8_t ch[GF_BYTES]) {
limb_t tmp, buffer = 0;
for (size_t i = 0, j = 0, bbits = 0; i < GF_BYTES; i++) {
tmp = ch[i];
buffer |= (limb_t)(tmp << bbits);
bbits += 8;
if (bbits >= LGX) {
ll[j++] = buffer & LMASK;
buffer = (limb_t)(tmp >> (LGX - (bbits - 8)));
bbits = bbits - LGX;
}
}
}

View File

@ -0,0 +1,31 @@
#ifndef __THREEBEARS_RING_H__
#define __THREEBEARS_RING_H__
#include "api.h"
#include "params.h"
typedef uint16_t limb_t;
typedef int16_t slimb_t;
typedef uint32_t dlimb_t;
typedef int32_t dslimb_t;
#define LMASK (((limb_t)1<<LGX)-1)
typedef limb_t gf_t[DIGITS];
/* Serialize a gf_t */
void PQCLEAN_BABYBEAR_CLEAN_contract(uint8_t ch[GF_BYTES], gf_t a);
/* Deserialize a gf_t */
void PQCLEAN_BABYBEAR_CLEAN_expand(gf_t ll, const uint8_t ch[GF_BYTES]);
/* Multiply and accumulate c = c + a*b */
void PQCLEAN_BABYBEAR_CLEAN_mac(gf_t c, const gf_t a, const gf_t b);
/* Reduce ring element to canonical form */
void PQCLEAN_BABYBEAR_CLEAN_canon(gf_t c);
/** Return the i'th limb of the modulus */
static inline limb_t modulus(size_t i) {
return (i == DIGITS / 2) ? LMASK - 1 : LMASK;
}
#endif

View File

@ -0,0 +1,225 @@
/** 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_BABYBEAR_CLEAN_expand(matrix, c);
}
/** The ThreeBears error distribution */
static slimb_t psi(uint8_t ci) {
int sample = 0, var = VAR_TIMES_128;
for (; var > 64; var -= 64, ci = (uint8_t)(ci << 2)) {
sample += ((ci + 64) >> 8) + ((ci - 64) >> 8);
}
return (slimb_t)(sample + ((ci + var) >> 8) + ((ci - var) >> 8));
}
/** Sample a vector of n noise elements */
static void noise(gf_t x, const shake256incctx *ctx, uint8_t iv) {
uint8_t c[DIGITS];
shake256incctx ctx2;
memcpy(&ctx2, ctx, sizeof(ctx2));
cshake256_inc_absorb(&ctx2, &iv, 1);
cshake256_inc_finalize(&ctx2);
cshake256_inc_squeeze(c, DIGITS, &ctx2);
for (size_t i = 0; i < DIGITS; i++) {
x[i] = (limb_t)(psi(c[i]) + modulus(i));
}
}
/* Expand public key from private key */
void PQCLEAN_BABYBEAR_CLEAN_get_pubkey(uint8_t *pk, const uint8_t *sk) {
shake256incctx ctx;
shake256incctx ctx2;
gf_t sk_expanded[DIM], b, c;
threebears_hash_init(&ctx, HASH_PURPOSE_KEYGEN);
cshake256_inc_absorb(&ctx, sk, PRIVATE_KEY_BYTES);
memcpy(&ctx2, &ctx, sizeof(ctx2));
cshake256_inc_finalize(&ctx2);
cshake256_inc_squeeze(pk, MATRIX_SEED_BYTES, &ctx2);
for (uint8_t i = 0; i < DIM; i++) {
noise(sk_expanded[i], &ctx, i);
}
for (uint8_t i = 0; i < DIM; i++) {
noise(c, &ctx, (uint8_t)(i + DIM));
for (uint8_t j = 0; j < DIM; j++) {
uniform(b, pk, (uint8_t) (i + DIM * j));
PQCLEAN_BABYBEAR_CLEAN_mac(c, b, sk_expanded[j]);
}
PQCLEAN_BABYBEAR_CLEAN_contract(&pk[MATRIX_SEED_BYTES + i * GF_BYTES], c);
}
}
/* Encapsulate a shared secret and return it */
void PQCLEAN_BABYBEAR_CLEAN_encapsulate(
uint8_t *shared_secret,
uint8_t *capsule,
const uint8_t *pk,
const uint8_t *seed
) {
uint8_t *lpr_data = &capsule[GF_BYTES * DIM];
shake256incctx ctx;
gf_t sk_expanded[DIM], b, c;
uint8_t tbi[ENC_SEED_BYTES + FEC_BYTES];
dlimb_t rlimb0, rlimb1;
limb_t h;
uint8_t *iv = &lpr_data[(ENC_BITS * LPR_BITS + 7) / 8];
memcpy(iv, &seed[ENC_SEED_BYTES], IV_BYTES);
threebears_hash_init(&ctx, HASH_PURPOSE_ENCAPS);
cshake256_inc_absorb(&ctx, pk, MATRIX_SEED_BYTES);
cshake256_inc_absorb(&ctx, seed, ENC_SEED_BYTES + IV_BYTES);
for (uint8_t i = 0; i < DIM; i++) {
noise(sk_expanded[i], &ctx, i);
}
for (uint8_t i = 0; i < DIM; i++) {
noise(c, &ctx, (uint8_t)(i + DIM));
for (uint8_t j = 0; j < DIM; j++) {
uniform(b, pk, (uint8_t)(j + DIM * i));
PQCLEAN_BABYBEAR_CLEAN_mac(c, b, sk_expanded[j]);
}
PQCLEAN_BABYBEAR_CLEAN_contract(&capsule[i * GF_BYTES], c);
}
noise(c, &ctx, (uint8_t)(2 * DIM));
/* Calculate approximate shared secret */
for (uint8_t i = 0; i < DIM; i++) {
PQCLEAN_BABYBEAR_CLEAN_expand(b, &pk[MATRIX_SEED_BYTES + i * GF_BYTES]);
PQCLEAN_BABYBEAR_CLEAN_mac(c, b, sk_expanded[i]);
}
PQCLEAN_BABYBEAR_CLEAN_canon(c);
memcpy(tbi, seed, ENC_SEED_BYTES);
PQCLEAN_BABYBEAR_CLEAN_melas_fec_set(&tbi[ENC_SEED_BYTES], tbi, ENC_SEED_BYTES);
/* Export with rounding */
for (size_t i = 0; i < ENC_BITS; i += 2) {
h = (limb_t)(tbi[i / 8] >> (i % 8));
rlimb0 = (dlimb_t)((c[i / 2] >> (LGX - LPR_BITS)) + (h << 3));
rlimb1 = (dlimb_t)((c[DIGITS - i / 2 - 1] >> (LGX - LPR_BITS)) + ((h >> 1) << 3));
lpr_data[i / 2] = (uint8_t)((rlimb0 & 0xF) | rlimb1 << 4);
}
cshake256_inc_finalize(&ctx);
cshake256_inc_squeeze(shared_secret, SHARED_SECRET_BYTES, &ctx);
}
/* Decapsulate a shared secret and return it */
void PQCLEAN_BABYBEAR_CLEAN_decapsulate(
uint8_t shared_secret[SHARED_SECRET_BYTES],
const uint8_t capsule[CAPSULE_BYTES],
const uint8_t sk[PRIVATE_KEY_BYTES]
) {
const uint8_t *lpr_data = &capsule[GF_BYTES * DIM];
shake256incctx ctx;
gf_t ska, b, c = {0};
uint8_t seed[ENC_SEED_BYTES + FEC_BYTES + IV_BYTES];
limb_t rounding, out;
size_t j;
limb_t our_rlimb, their_rlimb, delta;
uint8_t pk[PUBLIC_KEY_BYTES], capsule2[CAPSULE_BYTES];
uint8_t ret, ok, sep, prfk[PRF_KEY_BYTES];
uint8_t prfout[SHARED_SECRET_BYTES];
/* Calculate approximate shared secret */
threebears_hash_init(&ctx, HASH_PURPOSE_KEYGEN);
cshake256_inc_absorb(&ctx, sk, PRIVATE_KEY_BYTES);
for (uint8_t i = 0; i < DIM; i++) {
PQCLEAN_BABYBEAR_CLEAN_expand(b, &capsule[i * GF_BYTES]);
noise(ska, &ctx, i);
PQCLEAN_BABYBEAR_CLEAN_mac(c, ska, b);
}
/* Recover seed from LPR data */
PQCLEAN_BABYBEAR_CLEAN_canon(c);
rounding = 1 << (LPR_BITS - 1);
out = 0;
for (int32_t i = ENC_BITS - 1; i >= 0; i--) {
j = (size_t) ((i & 1) ? DIGITS - i / 2 - 1 : i / 2);
our_rlimb = (limb_t)(c[j] >> (LGX - LPR_BITS - 1));
their_rlimb = (limb_t)(lpr_data[i * LPR_BITS / 8] >> ((i * LPR_BITS) % 8));
delta = (limb_t)(their_rlimb * 2 - our_rlimb + rounding);
out |= (limb_t)(((delta >> LPR_BITS) & 1) << (i % 8));
if (i % 8 == 0) {
seed[i / 8] = (uint8_t)out;
out = 0;
}
}
PQCLEAN_BABYBEAR_CLEAN_melas_fec_correct(seed, ENC_SEED_BYTES, &seed[ENC_SEED_BYTES]);
/* Re-encapsulate and check; encapsulate will compute the shared secret */
//pk[PUBLIC_KEY_BYTES], capsule2[CAPSULE_BYTES];
PQCLEAN_BABYBEAR_CLEAN_get_pubkey(pk, sk);
memcpy(&seed[ENC_SEED_BYTES], &lpr_data[(ENC_BITS * LPR_BITS + 7) / 8], IV_BYTES);
PQCLEAN_BABYBEAR_CLEAN_encapsulate(shared_secret, capsule2, pk, seed);
/* Check capsule == capsule2 in constant time */
ret = 0;
for (size_t i = 0; i < CAPSULE_BYTES; i++) {
ret |= capsule[i] ^ capsule2[i];
}
ok = (uint8_t)(((int)ret - 1) >> 8);
/* Calculate PRF key */
sep = 0xFF;
cshake256_inc_absorb(&ctx, &sep, 1);
cshake256_inc_finalize(&ctx);
cshake256_inc_squeeze(prfk, PRF_KEY_BYTES, &ctx);
/* Calculate PRF */
threebears_hash_init(&ctx, HASH_PURPOSE_PRF);
cshake256_inc_absorb(&ctx, prfk, PRF_KEY_BYTES);
cshake256_inc_absorb(&ctx, capsule, CAPSULE_BYTES);
cshake256_inc_finalize(&ctx);
cshake256_inc_squeeze(prfout, SHARED_SECRET_BYTES, &ctx);
for (size_t i = 0; i < SHARED_SECRET_BYTES; i++) {
shared_secret[i] = (uint8_t)((shared_secret[i] & ok) | (prfout[i] & ~ok));
}
}

View File

@ -0,0 +1,60 @@
#ifndef __THREE_BEARS_BABYBEAR_H__
#define __THREE_BEARS_BABYBEAR_H__
#include <stddef.h> /* for size_t */
#include <stdint.h>
#define BABYBEAR_KEYGEN_SEED_BYTES 40
#define BABYBEAR_PRIVATE_KEY_BYTES BABYBEAR_KEYGEN_SEED_BYTES
#define BABYBEAR_SHARED_SECRET_BYTES 32
#define BABYBEAR_ENC_SEED_AND_IV_BYTES 32
#define BABYBEAR_PUBLIC_KEY_BYTES 804
#define BABYBEAR_CAPSULE_BYTES 917
/**
* Expand a secret seed to a public/private keypair.
*
* @param[out] pk The public key.
* @param[in] sk The private key, which must be uniformly random.
*/
void PQCLEAN_BABYBEAR_CLEAN_get_pubkey (
uint8_t pk[BABYBEAR_PUBLIC_KEY_BYTES],
const uint8_t sk[BABYBEAR_PRIVATE_KEY_BYTES]
);
/**
* Create a shared secret using a random seed and another party's public key.
*
* Input and output parameters may not alias.
*
* @param[out] shared_secret The shared secret key.
* @param[out] capsule A ciphertext to send to the other party.
* @param[in] pk The other party's public key.
* @param[in] seed A random seed.
*/
void PQCLEAN_BABYBEAR_CLEAN_encapsulate (
uint8_t shared_secret[BABYBEAR_SHARED_SECRET_BYTES],
uint8_t capsule[BABYBEAR_CAPSULE_BYTES],
const uint8_t pk[BABYBEAR_PUBLIC_KEY_BYTES],
const uint8_t seed[BABYBEAR_ENC_SEED_AND_IV_BYTES]
);
/**
* Extract the shared secret from a capsule using the private key.
* Has a negligible but nonzero probability of failure.
*
* Input and output parameters may not alias.
*
* @param[out] shared_secret The shared secret.
* @param[in] capsule The capsule produced by encapsulate_cca2.
* @param[in] sk The private key.
*/
void PQCLEAN_BABYBEAR_CLEAN_decapsulate (
uint8_t shared_secret[BABYBEAR_SHARED_SECRET_BYTES],
const uint8_t capsule[BABYBEAR_CAPSULE_BYTES],
const uint8_t sk[BABYBEAR_PRIVATE_KEY_BYTES]
);
void PQCLEAN_BABYBEAR_CLEAN_secure_bzero (void *s, size_t size);
#endif

View File

@ -0,0 +1,15 @@
name: MamaBear
type: kem
claimed-nist-level: 3
claimed-security: IND-CCA2
length-public-key: 1194
length-ciphertext: 1307
length-secret-key: 40
length-shared-secret: 32
nistkat-sha256: 2161de5015dc0477106b71ba17498982f77fae127fce724496c8a587803b1839
principal-submitters:
- Mike Hamburg
implementations:
- name: clean
version: round2

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_clean.a
HEADERS = api.h melas_fec.h params.h ring.h threebears.h
OBJECTS = kem.o melas_fec.o ring.o threebears.o
CFLAGS=-O3 -Wcast-align=strict -Wall -Wconversion -Wextra -Wpedantic -Wvla -Wmissing-prototypes -Wredundant-decls -std=c99 -I../../../common $(EXTRAFLAGS)
all: $(LIB)
%.o: %.c $(HEADERS)
$(CC) $(CFLAGS) -c -o $@ $<
$(LIB): $(OBJECTS)
$(AR) -r $@ $(OBJECTS)
clean:
$(RM) $(OBJECTS)
$(RM) $(LIB)

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_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_MAMABEAR_CLEAN_API_H
#define PQCLEAN_MAMABEAR_CLEAN_API_H
#include <stddef.h>
#include <stdint.h>
#include <string.h>
#define PQCLEAN_MAMABEAR_CLEAN_CRYPTO_SECRETKEYBYTES 40
#define PQCLEAN_MAMABEAR_CLEAN_CRYPTO_PUBLICKEYBYTES 1194
#define PQCLEAN_MAMABEAR_CLEAN_CRYPTO_BYTES 32
#define PQCLEAN_MAMABEAR_CLEAN_CRYPTO_CIPHERTEXTBYTES 1307
#define PQCLEAN_MAMABEAR_CLEAN_CRYPTO_ALGNAME "MamaBear"
int PQCLEAN_MAMABEAR_CLEAN_crypto_kem_keypair(uint8_t *pk, uint8_t *sk);
int PQCLEAN_MAMABEAR_CLEAN_crypto_kem_enc(uint8_t *ct, uint8_t *ss, const uint8_t *pk);
int PQCLEAN_MAMABEAR_CLEAN_crypto_kem_dec(uint8_t *ss, const uint8_t *ct, const uint8_t *sk);
#endif

View File

@ -0,0 +1,22 @@
#include "api.h"
#include "params.h"
#include "randombytes.h"
#include "threebears.h"
int PQCLEAN_MAMABEAR_CLEAN_crypto_kem_keypair(uint8_t *pk, uint8_t *sk) {
randombytes(sk, PRIVATE_KEY_BYTES);
PQCLEAN_MAMABEAR_CLEAN_get_pubkey(pk, sk);
return 0;
}
int PQCLEAN_MAMABEAR_CLEAN_crypto_kem_enc(uint8_t *ct, uint8_t *ss, const uint8_t *pk) {
uint8_t seed[ENC_SEED_BYTES + IV_BYTES];
randombytes(seed, sizeof(seed));
encapsulate(ss, ct, pk, seed);
return 0;
}
int PQCLEAN_MAMABEAR_CLEAN_crypto_kem_dec(uint8_t *ss, const uint8_t *ct, const uint8_t *sk) {
PQCLEAN_MAMABEAR_CLEAN_decapsulate(ss, ct, sk);
return 0;
}

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_MAMABEAR_CLEAN_melas_fec_set(
uint8_t out[MELAS_FEC_BYTES],
const uint8_t *data,
size_t len
) {
fec_gf_t fec = syndrome18(data, len);
for (size_t i = 0; i < MELAS_FEC_BYTES; i++, fec >>= 8) {
out[i] = (uint8_t)fec;
}
}
/* Return a*b mod Q */
static fec_gf_t mul(fec_gf_t a, fec_gf_t b) {
fec_gf_t r = 0;
for (size_t i = 0; i < 9; i++) {
r ^= ((b >> (8 - i)) & 1) * a;
a = step(1, Q, a);
}
return r;
}
/* Reverse an 18-bit number x */
static fec_gf_t reverse18(fec_gf_t x) {
fec_gf_t ret = 0;
for (size_t i = 0; i < 18; i++) {
ret ^= ((x >> i) & 1) << (17 - i);
}
return ret;
}
/* Correct data to have the given FEC */
void PQCLEAN_MAMABEAR_CLEAN_melas_fec_correct (
uint8_t *data,
size_t len,
const uint8_t fec[MELAS_FEC_BYTES]
) {
fec_gf_t a = s18update(syndrome18(data, len), fec, MELAS_FEC_BYTES);
fec_gf_t c, r, htr;
size_t i;
const uint8_t table[9] = {36, 10, 43, 215, 52, 11, 116, 244, 0};
fec_gf_t e0, e1;
/* Form a quadratic equation from the syndrome */
c = mul(step(9, Q, a), step(9, Q, reverse18(a)));
for (i = 0, r = 0x100; i < 510; i++) {
r = mul(r, c);
}
r = step(17, Q, r);
a = step(511 - (len + MELAS_FEC_BYTES) * 8, Q, a);
/* Solve using the half trace */
for (i = 0, htr = 0; i < 9; i++) {
htr ^= ((r >> i) & 1) * table[i];
}
e0 = mul(a, htr);
e1 = e0 ^ a;
/* Correct the errors using the locators */
for (i = 0; i < len; i++) {
data[i] ^= (uint8_t)(e0 & (((e0 & (e0 - 1)) - 1) >> 9));
data[i] ^= (uint8_t)(e1 & (((e1 & (e1 - 1)) - 1) >> 9));
e0 = step(8, Q, e0);
e1 = step(8, Q, e1);
}
}

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_MAMABEAR_CLEAN_melas_fec_set(
uint8_t out[MELAS_FEC_BYTES],
const uint8_t *data,
size_t len
);
/* Append 3 bytes of FEC(data) to data, so that the FEC becomes 0 */
void PQCLEAN_MAMABEAR_CLEAN_melas_fec_correct(
uint8_t *data,
size_t len,
const uint8_t fec[MELAS_FEC_BYTES]
);
#endif

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 52
#define LPR_BITS 4
#define FEC_BITS 18
#define CCA 1
#define SHARED_SECRET_BYTES 32
#define PRIVATE_KEY_BYTES 40
#define PRF_KEY_BYTES PRIVATE_KEY_BYTES
#define BEAR_NAME "MamaBear"
#define encapsulate MamaBear_encapsulate
#define decapsulate MamaBear_decapsulate
#define get_pubkey MamaBear_get_pubkey
#define GF_BYTES ((LGX*DIGITS+7)/8)
#define PUBLIC_KEY_BYTES (MATRIX_SEED_BYTES + DIM*GF_BYTES)
#define CAPSULE_BYTES \
(DIM*GF_BYTES + IV_BYTES + ((ENC_SEED_BYTES*8+FEC_BITS)*LPR_BITS+7)/8)
#endif

View File

@ -0,0 +1,102 @@
/** Ring arithmetic implementation */
#include "ring.h"
/** Multiply and accumulate c += a*b */
void PQCLEAN_MAMABEAR_CLEAN_mac(gf_t c, const gf_t a, const gf_t b) {
/* Reference non-Karatsuba MAC */
dslimb_t accum[2 * DIGITS] = {0};
dslimb_t chain;
size_t i, j;
/* Initialize accumulator = unclarify(c) */
for (i = 0; i < DIGITS; i++) {
accum[i + DIGITS / 2] = c[i];
}
/* Multiply */
for (i = 0; i < DIGITS; i++) {
for (j = 0; j < DIGITS; j++) {
accum[i + j] += (dslimb_t)a[i] * b[j];
}
}
/* Clarify and reduce */
for (i = 0; i < DIGITS / 2; i++) {
accum[i + DIGITS / 2] -= accum[i];
accum[i + DIGITS] += accum[i];
accum[i + DIGITS / 2] += accum[i + 3 * DIGITS / 2];
accum[i + DIGITS] += accum[i + 3 * DIGITS / 2];
}
/* Carry propagate */
chain = accum[3 * DIGITS / 2 - 1];
accum[3 * DIGITS / 2 - 1] = chain & LMASK;
chain >>= LGX;
accum[DIGITS] += chain;
for (i = DIGITS / 2; i < 3 * DIGITS / 2; i++) {
chain += accum[i];
c[i - DIGITS / 2] = chain & LMASK;
chain >>= LGX;
}
c[0] = (limb_t) (c[0] + chain);
c[DIGITS / 2] = (limb_t) (c[DIGITS / 2] + chain);
}
/** Reduce a gf_t to canonical form, i.e. strictly less than N. */
void PQCLEAN_MAMABEAR_CLEAN_canon(gf_t c) {
const limb_t DELTA = (limb_t)1 << (LGX - 1);
slimb_t hi;
dslimb_t scarry;
dlimb_t carry;
/* Reduce to 0..2p */
hi = (slimb_t) (c[DIGITS - 1] - DELTA);
c[DIGITS - 1] = (limb_t) ((hi & LMASK) + DELTA);
c[DIGITS / 2] = (limb_t) (c[DIGITS / 2] + (hi >> LGX));
/* Strong reduce. First subtract modulus */
scarry = hi >> LGX;
for (size_t i = 0; i < DIGITS; i++) {
scarry = scarry + (slimb_t)c[i] - modulus(i);
c[i] = scarry & LMASK;
scarry >>= LGX;
}
/* add it back */
carry = 0;
for (size_t i = 0; i < DIGITS; i++) {
carry = carry + c[i] + ((dlimb_t)scarry & modulus(i));
c[i] = carry & LMASK;
carry >>= LGX;
}
}
/** Serialize a gf_t to bytes */
void PQCLEAN_MAMABEAR_CLEAN_contract(uint8_t ch[GF_BYTES], gf_t a) {
size_t pos;
PQCLEAN_MAMABEAR_CLEAN_canon(a);
for (size_t i = 0; i < GF_BYTES; i++) {
pos = (i * 8) / LGX;
ch[i] = (uint8_t)(a[pos] >> ((i * 8) % LGX));
if (i < GF_BYTES - 1) {
ch[i] |= (uint8_t)(a[pos + 1] << (LGX - ((i * 8) % LGX)));
}
}
}
/** Deserialize a gf_t from bytes */
void PQCLEAN_MAMABEAR_CLEAN_expand(gf_t ll, const uint8_t ch[GF_BYTES]) {
limb_t tmp, buffer = 0;
for (size_t i = 0, j = 0, bbits = 0; i < GF_BYTES; i++) {
tmp = ch[i];
buffer |= (limb_t)(tmp << bbits);
bbits += 8;
if (bbits >= LGX) {
ll[j++] = buffer & LMASK;
buffer = (limb_t)(tmp >> (LGX - (bbits - 8)));
bbits = bbits - LGX;
}
}
}

View File

@ -0,0 +1,31 @@
#ifndef __THREEBEARS_RING_H__
#define __THREEBEARS_RING_H__
#include "api.h"
#include "params.h"
typedef uint16_t limb_t;
typedef int16_t slimb_t;
typedef uint32_t dlimb_t;
typedef int32_t dslimb_t;
#define LMASK (((limb_t)1<<LGX)-1)
typedef limb_t gf_t[DIGITS];
/* Serialize a gf_t */
void PQCLEAN_MAMABEAR_CLEAN_contract(uint8_t ch[GF_BYTES], gf_t a);
/* Deserialize a gf_t */
void PQCLEAN_MAMABEAR_CLEAN_expand(gf_t ll, const uint8_t ch[GF_BYTES]);
/* Multiply and accumulate c = c + a*b */
void PQCLEAN_MAMABEAR_CLEAN_mac(gf_t c, const gf_t a, const gf_t b);
/* Reduce ring element to canonical form */
void PQCLEAN_MAMABEAR_CLEAN_canon(gf_t c);
/** Return the i'th limb of the modulus */
static inline limb_t modulus(size_t i) {
return (i == DIGITS / 2) ? LMASK - 1 : LMASK;
}
#endif

View File

@ -0,0 +1,225 @@
/** 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_MAMABEAR_CLEAN_expand(matrix, c);
}
/** The ThreeBears error distribution */
static slimb_t psi(uint8_t ci) {
int sample = 0, var = VAR_TIMES_128;
for (; var > 64; var -= 64, ci = (uint8_t)(ci << 2)) {
sample += ((ci + 64) >> 8) + ((ci - 64) >> 8);
}
return (slimb_t)(sample + ((ci + var) >> 8) + ((ci - var) >> 8));
}
/** Sample a vector of n noise elements */
static void noise(gf_t x, const shake256incctx *ctx, uint8_t iv) {
uint8_t c[DIGITS];
shake256incctx ctx2;
memcpy(&ctx2, ctx, sizeof(ctx2));
cshake256_inc_absorb(&ctx2, &iv, 1);
cshake256_inc_finalize(&ctx2);
cshake256_inc_squeeze(c, DIGITS, &ctx2);
for (size_t i = 0; i < DIGITS; i++) {
x[i] = (limb_t)(psi(c[i]) + modulus(i));
}
}
/* Expand public key from private key */
void PQCLEAN_MAMABEAR_CLEAN_get_pubkey(uint8_t *pk, const uint8_t *sk) {
shake256incctx ctx;
shake256incctx ctx2;
gf_t sk_expanded[DIM], b, c;
threebears_hash_init(&ctx, HASH_PURPOSE_KEYGEN);
cshake256_inc_absorb(&ctx, sk, PRIVATE_KEY_BYTES);
memcpy(&ctx2, &ctx, sizeof(ctx2));
cshake256_inc_finalize(&ctx2);
cshake256_inc_squeeze(pk, MATRIX_SEED_BYTES, &ctx2);
for (uint8_t i = 0; i < DIM; i++) {
noise(sk_expanded[i], &ctx, i);
}
for (uint8_t i = 0; i < DIM; i++) {
noise(c, &ctx, (uint8_t)(i + DIM));
for (uint8_t j = 0; j < DIM; j++) {
uniform(b, pk, (uint8_t) (i + DIM * j));
PQCLEAN_MAMABEAR_CLEAN_mac(c, b, sk_expanded[j]);
}
PQCLEAN_MAMABEAR_CLEAN_contract(&pk[MATRIX_SEED_BYTES + i * GF_BYTES], c);
}
}
/* Encapsulate a shared secret and return it */
void PQCLEAN_MAMABEAR_CLEAN_encapsulate(
uint8_t *shared_secret,
uint8_t *capsule,
const uint8_t *pk,
const uint8_t *seed
) {
uint8_t *lpr_data = &capsule[GF_BYTES * DIM];
shake256incctx ctx;
gf_t sk_expanded[DIM], b, c;
uint8_t tbi[ENC_SEED_BYTES + FEC_BYTES];
dlimb_t rlimb0, rlimb1;
limb_t h;
uint8_t *iv = &lpr_data[(ENC_BITS * LPR_BITS + 7) / 8];
memcpy(iv, &seed[ENC_SEED_BYTES], IV_BYTES);
threebears_hash_init(&ctx, HASH_PURPOSE_ENCAPS);
cshake256_inc_absorb(&ctx, pk, MATRIX_SEED_BYTES);
cshake256_inc_absorb(&ctx, seed, ENC_SEED_BYTES + IV_BYTES);
for (uint8_t i = 0; i < DIM; i++) {
noise(sk_expanded[i], &ctx, i);
}
for (uint8_t i = 0; i < DIM; i++) {
noise(c, &ctx, (uint8_t)(i + DIM));
for (uint8_t j = 0; j < DIM; j++) {
uniform(b, pk, (uint8_t)(j + DIM * i));
PQCLEAN_MAMABEAR_CLEAN_mac(c, b, sk_expanded[j]);
}
PQCLEAN_MAMABEAR_CLEAN_contract(&capsule[i * GF_BYTES], c);
}
noise(c, &ctx, (uint8_t)(2 * DIM));
/* Calculate approximate shared secret */
for (uint8_t i = 0; i < DIM; i++) {
PQCLEAN_MAMABEAR_CLEAN_expand(b, &pk[MATRIX_SEED_BYTES + i * GF_BYTES]);
PQCLEAN_MAMABEAR_CLEAN_mac(c, b, sk_expanded[i]);
}
PQCLEAN_MAMABEAR_CLEAN_canon(c);
memcpy(tbi, seed, ENC_SEED_BYTES);
PQCLEAN_MAMABEAR_CLEAN_melas_fec_set(&tbi[ENC_SEED_BYTES], tbi, ENC_SEED_BYTES);
/* Export with rounding */
for (size_t i = 0; i < ENC_BITS; i += 2) {
h = (limb_t)(tbi[i / 8] >> (i % 8));
rlimb0 = (dlimb_t)((c[i / 2] >> (LGX - LPR_BITS)) + (h << 3));
rlimb1 = (dlimb_t)((c[DIGITS - i / 2 - 1] >> (LGX - LPR_BITS)) + ((h >> 1) << 3));
lpr_data[i / 2] = (uint8_t)((rlimb0 & 0xF) | rlimb1 << 4);
}
cshake256_inc_finalize(&ctx);
cshake256_inc_squeeze(shared_secret, SHARED_SECRET_BYTES, &ctx);
}
/* Decapsulate a shared secret and return it */
void PQCLEAN_MAMABEAR_CLEAN_decapsulate(
uint8_t shared_secret[SHARED_SECRET_BYTES],
const uint8_t capsule[CAPSULE_BYTES],
const uint8_t sk[PRIVATE_KEY_BYTES]
) {
const uint8_t *lpr_data = &capsule[GF_BYTES * DIM];
shake256incctx ctx;
gf_t ska, b, c = {0};
uint8_t seed[ENC_SEED_BYTES + FEC_BYTES + IV_BYTES];
limb_t rounding, out;
size_t j;
limb_t our_rlimb, their_rlimb, delta;
uint8_t pk[PUBLIC_KEY_BYTES], capsule2[CAPSULE_BYTES];
uint8_t ret, ok, sep, prfk[PRF_KEY_BYTES];
uint8_t prfout[SHARED_SECRET_BYTES];
/* Calculate approximate shared secret */
threebears_hash_init(&ctx, HASH_PURPOSE_KEYGEN);
cshake256_inc_absorb(&ctx, sk, PRIVATE_KEY_BYTES);
for (uint8_t i = 0; i < DIM; i++) {
PQCLEAN_MAMABEAR_CLEAN_expand(b, &capsule[i * GF_BYTES]);
noise(ska, &ctx, i);
PQCLEAN_MAMABEAR_CLEAN_mac(c, ska, b);
}
/* Recover seed from LPR data */
PQCLEAN_MAMABEAR_CLEAN_canon(c);
rounding = 1 << (LPR_BITS - 1);
out = 0;
for (int32_t i = ENC_BITS - 1; i >= 0; i--) {
j = (size_t) ((i & 1) ? DIGITS - i / 2 - 1 : i / 2);
our_rlimb = (limb_t)(c[j] >> (LGX - LPR_BITS - 1));
their_rlimb = (limb_t)(lpr_data[i * LPR_BITS / 8] >> ((i * LPR_BITS) % 8));
delta = (limb_t)(their_rlimb * 2 - our_rlimb + rounding);
out |= (limb_t)(((delta >> LPR_BITS) & 1) << (i % 8));
if (i % 8 == 0) {
seed[i / 8] = (uint8_t)out;
out = 0;
}
}
PQCLEAN_MAMABEAR_CLEAN_melas_fec_correct(seed, ENC_SEED_BYTES, &seed[ENC_SEED_BYTES]);
/* Re-encapsulate and check; encapsulate will compute the shared secret */
//pk[PUBLIC_KEY_BYTES], capsule2[CAPSULE_BYTES];
PQCLEAN_MAMABEAR_CLEAN_get_pubkey(pk, sk);
memcpy(&seed[ENC_SEED_BYTES], &lpr_data[(ENC_BITS * LPR_BITS + 7) / 8], IV_BYTES);
PQCLEAN_MAMABEAR_CLEAN_encapsulate(shared_secret, capsule2, pk, seed);
/* Check capsule == capsule2 in constant time */
ret = 0;
for (size_t i = 0; i < CAPSULE_BYTES; i++) {
ret |= capsule[i] ^ capsule2[i];
}
ok = (uint8_t)(((int)ret - 1) >> 8);
/* Calculate PRF key */
sep = 0xFF;
cshake256_inc_absorb(&ctx, &sep, 1);
cshake256_inc_finalize(&ctx);
cshake256_inc_squeeze(prfk, PRF_KEY_BYTES, &ctx);
/* Calculate PRF */
threebears_hash_init(&ctx, HASH_PURPOSE_PRF);
cshake256_inc_absorb(&ctx, prfk, PRF_KEY_BYTES);
cshake256_inc_absorb(&ctx, capsule, CAPSULE_BYTES);
cshake256_inc_finalize(&ctx);
cshake256_inc_squeeze(prfout, SHARED_SECRET_BYTES, &ctx);
for (size_t i = 0; i < SHARED_SECRET_BYTES; i++) {
shared_secret[i] = (uint8_t)((shared_secret[i] & ok) | (prfout[i] & ~ok));
}
}

View File

@ -0,0 +1,60 @@
#ifndef __THREE_BEARS_MAMABEAR_H__
#define __THREE_BEARS_MAMABEAR_H__
#include <stddef.h> /* for size_t */
#include <stdint.h>
#define MAMABEAR_KEYGEN_SEED_BYTES 40
#define MAMABEAR_PRIVATE_KEY_BYTES MAMABEAR_KEYGEN_SEED_BYTES
#define MAMABEAR_SHARED_SECRET_BYTES 32
#define MAMABEAR_ENC_SEED_AND_IV_BYTES 32
#define MAMABEAR_PUBLIC_KEY_BYTES 1194
#define MAMABEAR_CAPSULE_BYTES 1307
/**
* Expand a secret seed to a public/private keypair.
*
* @param[out] pk The public key.
* @param[in] sk The private key, which must be uniformly random.
*/
void MamaBear_get_pubkey (
uint8_t pk[MAMABEAR_PUBLIC_KEY_BYTES],
const uint8_t sk[MAMABEAR_PRIVATE_KEY_BYTES]
);
/**
* Create a shared secret using a random seed and another party's public key.
*
* Input and output parameters may not alias.
*
* @param[out] shared_secret The shared secret key.
* @param[out] capsule A ciphertext to send to the other party.
* @param[in] pk The other party's public key.
* @param[in] seed A random seed.
*/
void MamaBear_encapsulate (
uint8_t shared_secret[MAMABEAR_SHARED_SECRET_BYTES],
uint8_t capsule[MAMABEAR_CAPSULE_BYTES],
const uint8_t pk[MAMABEAR_PUBLIC_KEY_BYTES],
const uint8_t seed[MAMABEAR_ENC_SEED_AND_IV_BYTES]
);
/**
* Extract the shared secret from a capsule using the private key.
* Has a negligible but nonzero probability of failure.
*
* Input and output parameters may not alias.
*
* @param[out] shared_secret The shared secret.
* @param[in] capsule The capsule produced by encapsulate_cca2.
* @param[in] sk The private key.
*/
void MamaBear_decapsulate (
uint8_t shared_secret[MAMABEAR_SHARED_SECRET_BYTES],
const uint8_t capsule[MAMABEAR_CAPSULE_BYTES],
const uint8_t sk[MAMABEAR_PRIVATE_KEY_BYTES]
);
void PQCLEAN_MAMABEAR_CLEAN_secure_bzero (void *s, size_t size);
#endif

View File

@ -0,0 +1,15 @@
name: PapaBear
type: kem
claimed-nist-level: 5
claimed-security: IND-CCA2
length-public-key: 1584
length-ciphertext: 1697
length-secret-key: 40
length-shared-secret: 32
nistkat-sha256: 977429507fa60a24a608193a507e39f4d9deaf4c6359f94f36b2d9ca0897c997
principal-submitters:
- Mike Hamburg
implementations:
- name: clean
version: round2

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_clean.a
HEADERS = api.h melas_fec.h params.h ring.h threebears.h
OBJECTS = kem.o melas_fec.o ring.o threebears.o
CFLAGS=-O3 -Wcast-align=strict -Wall -Wconversion -Wextra -Wpedantic -Wvla -Wmissing-prototypes -Wredundant-decls -std=c99 -I../../../common $(EXTRAFLAGS)
all: $(LIB)
%.o: %.c $(HEADERS)
$(CC) $(CFLAGS) -c -o $@ $<
$(LIB): $(OBJECTS)
$(AR) -r $@ $(OBJECTS)
clean:
$(RM) $(OBJECTS)
$(RM) $(LIB)

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_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_PAPABEAR_CLEAN_API_H
#define PQCLEAN_PAPABEAR_CLEAN_API_H
#include <stddef.h>
#include <stdint.h>
#include <string.h>
#define PQCLEAN_PAPABEAR_CLEAN_CRYPTO_SECRETKEYBYTES 40
#define PQCLEAN_PAPABEAR_CLEAN_CRYPTO_PUBLICKEYBYTES 1584
#define PQCLEAN_PAPABEAR_CLEAN_CRYPTO_BYTES 32
#define PQCLEAN_PAPABEAR_CLEAN_CRYPTO_CIPHERTEXTBYTES 1697
#define PQCLEAN_PAPABEAR_CLEAN_CRYPTO_ALGNAME "PapaBear"
int PQCLEAN_PAPABEAR_CLEAN_crypto_kem_keypair(uint8_t *pk, uint8_t *sk);
int PQCLEAN_PAPABEAR_CLEAN_crypto_kem_enc(uint8_t *ct, uint8_t *ss, const uint8_t *pk);
int PQCLEAN_PAPABEAR_CLEAN_crypto_kem_dec(uint8_t *ss, const uint8_t *ct, const uint8_t *sk);
#endif

View File

@ -0,0 +1,22 @@
#include "api.h"
#include "params.h"
#include "randombytes.h"
#include "threebears.h"
int PQCLEAN_PAPABEAR_CLEAN_crypto_kem_keypair(uint8_t *pk, uint8_t *sk) {
randombytes(sk, PRIVATE_KEY_BYTES);
PQCLEAN_PAPABEAR_CLEAN_get_pubkey(pk, sk);
return 0;
}
int PQCLEAN_PAPABEAR_CLEAN_crypto_kem_enc(uint8_t *ct, uint8_t *ss, const uint8_t *pk) {
uint8_t seed[ENC_SEED_BYTES + IV_BYTES];
randombytes(seed, sizeof(seed));
encapsulate(ss, ct, pk, seed);
return 0;
}
int PQCLEAN_PAPABEAR_CLEAN_crypto_kem_dec(uint8_t *ss, const uint8_t *ct, const uint8_t *sk) {
PQCLEAN_PAPABEAR_CLEAN_decapsulate(ss, ct, sk);
return 0;
}

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_PAPABEAR_CLEAN_melas_fec_set(
uint8_t out[MELAS_FEC_BYTES],
const uint8_t *data,
size_t len
) {
fec_gf_t fec = syndrome18(data, len);
for (size_t i = 0; i < MELAS_FEC_BYTES; i++, fec >>= 8) {
out[i] = (uint8_t)fec;
}
}
/* Return a*b mod Q */
static fec_gf_t mul(fec_gf_t a, fec_gf_t b) {
fec_gf_t r = 0;
for (size_t i = 0; i < 9; i++) {
r ^= ((b >> (8 - i)) & 1) * a;
a = step(1, Q, a);
}
return r;
}
/* Reverse an 18-bit number x */
static fec_gf_t reverse18(fec_gf_t x) {
fec_gf_t ret = 0;
for (size_t i = 0; i < 18; i++) {
ret ^= ((x >> i) & 1) << (17 - i);
}
return ret;
}
/* Correct data to have the given FEC */
void PQCLEAN_PAPABEAR_CLEAN_melas_fec_correct (
uint8_t *data,
size_t len,
const uint8_t fec[MELAS_FEC_BYTES]
) {
fec_gf_t a = s18update(syndrome18(data, len), fec, MELAS_FEC_BYTES);
fec_gf_t c, r, htr;
size_t i;
const uint8_t table[9] = {36, 10, 43, 215, 52, 11, 116, 244, 0};
fec_gf_t e0, e1;
/* Form a quadratic equation from the syndrome */
c = mul(step(9, Q, a), step(9, Q, reverse18(a)));
for (i = 0, r = 0x100; i < 510; i++) {
r = mul(r, c);
}
r = step(17, Q, r);
a = step(511 - (len + MELAS_FEC_BYTES) * 8, Q, a);
/* Solve using the half trace */
for (i = 0, htr = 0; i < 9; i++) {
htr ^= ((r >> i) & 1) * table[i];
}
e0 = mul(a, htr);
e1 = e0 ^ a;
/* Correct the errors using the locators */
for (i = 0; i < len; i++) {
data[i] ^= (uint8_t)(e0 & (((e0 & (e0 - 1)) - 1) >> 9));
data[i] ^= (uint8_t)(e1 & (((e1 & (e1 - 1)) - 1) >> 9));
e0 = step(8, Q, e0);
e1 = step(8, Q, e1);
}
}

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_PAPABEAR_CLEAN_melas_fec_set(
uint8_t out[MELAS_FEC_BYTES],
const uint8_t *data,
size_t len
);
/* Append 3 bytes of FEC(data) to data, so that the FEC becomes 0 */
void PQCLEAN_PAPABEAR_CLEAN_melas_fec_correct(
uint8_t *data,
size_t len,
const uint8_t fec[MELAS_FEC_BYTES]
);
#endif

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 40
#define LPR_BITS 4
#define FEC_BITS 18
#define CCA 1
#define SHARED_SECRET_BYTES 32
#define PRIVATE_KEY_BYTES 40
#define PRF_KEY_BYTES PRIVATE_KEY_BYTES
#define BEAR_NAME "PapaBear"
#define encapsulate PapaBear_encapsulate
#define decapsulate PapaBear_decapsulate
#define get_pubkey PapaBear_get_pubkey
#define GF_BYTES ((LGX*DIGITS+7)/8)
#define PUBLIC_KEY_BYTES (MATRIX_SEED_BYTES + DIM*GF_BYTES)
#define CAPSULE_BYTES \
(DIM*GF_BYTES + IV_BYTES + ((ENC_SEED_BYTES*8+FEC_BITS)*LPR_BITS+7)/8)
#endif

View File

@ -0,0 +1,102 @@
/** Ring arithmetic implementation */
#include "ring.h"
/** Multiply and accumulate c += a*b */
void PQCLEAN_PAPABEAR_CLEAN_mac(gf_t c, const gf_t a, const gf_t b) {
/* Reference non-Karatsuba MAC */
dslimb_t accum[2 * DIGITS] = {0};
dslimb_t chain;
size_t i, j;
/* Initialize accumulator = unclarify(c) */
for (i = 0; i < DIGITS; i++) {
accum[i + DIGITS / 2] = c[i];
}
/* Multiply */
for (i = 0; i < DIGITS; i++) {
for (j = 0; j < DIGITS; j++) {
accum[i + j] += (dslimb_t)a[i] * b[j];
}
}
/* Clarify and reduce */
for (i = 0; i < DIGITS / 2; i++) {
accum[i + DIGITS / 2] -= accum[i];
accum[i + DIGITS] += accum[i];
accum[i + DIGITS / 2] += accum[i + 3 * DIGITS / 2];
accum[i + DIGITS] += accum[i + 3 * DIGITS / 2];
}
/* Carry propagate */
chain = accum[3 * DIGITS / 2 - 1];
accum[3 * DIGITS / 2 - 1] = chain & LMASK;
chain >>= LGX;
accum[DIGITS] += chain;
for (i = DIGITS / 2; i < 3 * DIGITS / 2; i++) {
chain += accum[i];
c[i - DIGITS / 2] = chain & LMASK;
chain >>= LGX;
}
c[0] = (limb_t) (c[0] + chain);
c[DIGITS / 2] = (limb_t) (c[DIGITS / 2] + chain);
}
/** Reduce a gf_t to canonical form, i.e. strictly less than N. */
void PQCLEAN_PAPABEAR_CLEAN_canon(gf_t c) {
const limb_t DELTA = (limb_t)1 << (LGX - 1);
slimb_t hi;
dslimb_t scarry;
dlimb_t carry;
/* Reduce to 0..2p */
hi = (slimb_t) (c[DIGITS - 1] - DELTA);
c[DIGITS - 1] = (limb_t) ((hi & LMASK) + DELTA);
c[DIGITS / 2] = (limb_t) (c[DIGITS / 2] + (hi >> LGX));
/* Strong reduce. First subtract modulus */
scarry = hi >> LGX;
for (size_t i = 0; i < DIGITS; i++) {
scarry = scarry + (slimb_t)c[i] - modulus(i);
c[i] = scarry & LMASK;
scarry >>= LGX;
}
/* add it back */
carry = 0;
for (size_t i = 0; i < DIGITS; i++) {
carry = carry + c[i] + ((dlimb_t)scarry & modulus(i));
c[i] = carry & LMASK;
carry >>= LGX;
}
}
/** Serialize a gf_t to bytes */
void PQCLEAN_PAPABEAR_CLEAN_contract(uint8_t ch[GF_BYTES], gf_t a) {
size_t pos;
PQCLEAN_PAPABEAR_CLEAN_canon(a);
for (size_t i = 0; i < GF_BYTES; i++) {
pos = (i * 8) / LGX;
ch[i] = (uint8_t)(a[pos] >> ((i * 8) % LGX));
if (i < GF_BYTES - 1) {
ch[i] |= (uint8_t)(a[pos + 1] << (LGX - ((i * 8) % LGX)));
}
}
}
/** Deserialize a gf_t from bytes */
void PQCLEAN_PAPABEAR_CLEAN_expand(gf_t ll, const uint8_t ch[GF_BYTES]) {
limb_t tmp, buffer = 0;
for (size_t i = 0, j = 0, bbits = 0; i < GF_BYTES; i++) {
tmp = ch[i];
buffer |= (limb_t)(tmp << bbits);
bbits += 8;
if (bbits >= LGX) {
ll[j++] = buffer & LMASK;
buffer = (limb_t)(tmp >> (LGX - (bbits - 8)));
bbits = bbits - LGX;
}
}
}

View File

@ -0,0 +1,31 @@
#ifndef __THREEBEARS_RING_H__
#define __THREEBEARS_RING_H__
#include "api.h"
#include "params.h"
typedef uint16_t limb_t;
typedef int16_t slimb_t;
typedef uint32_t dlimb_t;
typedef int32_t dslimb_t;
#define LMASK (((limb_t)1<<LGX)-1)
typedef limb_t gf_t[DIGITS];
/* Serialize a gf_t */
void PQCLEAN_PAPABEAR_CLEAN_contract(uint8_t ch[GF_BYTES], gf_t a);
/* Deserialize a gf_t */
void PQCLEAN_PAPABEAR_CLEAN_expand(gf_t ll, const uint8_t ch[GF_BYTES]);
/* Multiply and accumulate c = c + a*b */
void PQCLEAN_PAPABEAR_CLEAN_mac(gf_t c, const gf_t a, const gf_t b);
/* Reduce ring element to canonical form */
void PQCLEAN_PAPABEAR_CLEAN_canon(gf_t c);
/** Return the i'th limb of the modulus */
static inline limb_t modulus(size_t i) {
return (i == DIGITS / 2) ? LMASK - 1 : LMASK;
}
#endif

View File

@ -0,0 +1,225 @@
/** 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_PAPABEAR_CLEAN_expand(matrix, c);
}
/** The ThreeBears error distribution */
static slimb_t psi(uint8_t ci) {
int sample = 0, var = VAR_TIMES_128;
for (; var > 64; var -= 64, ci = (uint8_t)(ci << 2)) {
sample += ((ci + 64) >> 8) + ((ci - 64) >> 8);
}
return (slimb_t)(sample + ((ci + var) >> 8) + ((ci - var) >> 8));
}
/** Sample a vector of n noise elements */
static void noise(gf_t x, const shake256incctx *ctx, uint8_t iv) {
uint8_t c[DIGITS];
shake256incctx ctx2;
memcpy(&ctx2, ctx, sizeof(ctx2));
cshake256_inc_absorb(&ctx2, &iv, 1);
cshake256_inc_finalize(&ctx2);
cshake256_inc_squeeze(c, DIGITS, &ctx2);
for (size_t i = 0; i < DIGITS; i++) {
x[i] = (limb_t)(psi(c[i]) + modulus(i));
}
}
/* Expand public key from private key */
void PQCLEAN_PAPABEAR_CLEAN_get_pubkey(uint8_t *pk, const uint8_t *sk) {
shake256incctx ctx;
shake256incctx ctx2;
gf_t sk_expanded[DIM], b, c;
threebears_hash_init(&ctx, HASH_PURPOSE_KEYGEN);
cshake256_inc_absorb(&ctx, sk, PRIVATE_KEY_BYTES);
memcpy(&ctx2, &ctx, sizeof(ctx2));
cshake256_inc_finalize(&ctx2);
cshake256_inc_squeeze(pk, MATRIX_SEED_BYTES, &ctx2);
for (uint8_t i = 0; i < DIM; i++) {
noise(sk_expanded[i], &ctx, i);
}
for (uint8_t i = 0; i < DIM; i++) {
noise(c, &ctx, (uint8_t)(i + DIM));
for (uint8_t j = 0; j < DIM; j++) {
uniform(b, pk, (uint8_t) (i + DIM * j));
PQCLEAN_PAPABEAR_CLEAN_mac(c, b, sk_expanded[j]);
}
PQCLEAN_PAPABEAR_CLEAN_contract(&pk[MATRIX_SEED_BYTES + i * GF_BYTES], c);
}
}
/* Encapsulate a shared secret and return it */
void PQCLEAN_PAPABEAR_CLEAN_encapsulate(
uint8_t *shared_secret,
uint8_t *capsule,
const uint8_t *pk,
const uint8_t *seed
) {
uint8_t *lpr_data = &capsule[GF_BYTES * DIM];
shake256incctx ctx;
gf_t sk_expanded[DIM], b, c;
uint8_t tbi[ENC_SEED_BYTES + FEC_BYTES];
dlimb_t rlimb0, rlimb1;
limb_t h;
uint8_t *iv = &lpr_data[(ENC_BITS * LPR_BITS + 7) / 8];
memcpy(iv, &seed[ENC_SEED_BYTES], IV_BYTES);
threebears_hash_init(&ctx, HASH_PURPOSE_ENCAPS);
cshake256_inc_absorb(&ctx, pk, MATRIX_SEED_BYTES);
cshake256_inc_absorb(&ctx, seed, ENC_SEED_BYTES + IV_BYTES);
for (uint8_t i = 0; i < DIM; i++) {
noise(sk_expanded[i], &ctx, i);
}
for (uint8_t i = 0; i < DIM; i++) {
noise(c, &ctx, (uint8_t)(i + DIM));
for (uint8_t j = 0; j < DIM; j++) {
uniform(b, pk, (uint8_t)(j + DIM * i));
PQCLEAN_PAPABEAR_CLEAN_mac(c, b, sk_expanded[j]);
}
PQCLEAN_PAPABEAR_CLEAN_contract(&capsule[i * GF_BYTES], c);
}
noise(c, &ctx, (uint8_t)(2 * DIM));
/* Calculate approximate shared secret */
for (uint8_t i = 0; i < DIM; i++) {
PQCLEAN_PAPABEAR_CLEAN_expand(b, &pk[MATRIX_SEED_BYTES + i * GF_BYTES]);
PQCLEAN_PAPABEAR_CLEAN_mac(c, b, sk_expanded[i]);
}
PQCLEAN_PAPABEAR_CLEAN_canon(c);
memcpy(tbi, seed, ENC_SEED_BYTES);
PQCLEAN_PAPABEAR_CLEAN_melas_fec_set(&tbi[ENC_SEED_BYTES], tbi, ENC_SEED_BYTES);
/* Export with rounding */
for (size_t i = 0; i < ENC_BITS; i += 2) {
h = (limb_t)(tbi[i / 8] >> (i % 8));
rlimb0 = (dlimb_t)((c[i / 2] >> (LGX - LPR_BITS)) + (h << 3));
rlimb1 = (dlimb_t)((c[DIGITS - i / 2 - 1] >> (LGX - LPR_BITS)) + ((h >> 1) << 3));
lpr_data[i / 2] = (uint8_t)((rlimb0 & 0xF) | rlimb1 << 4);
}
cshake256_inc_finalize(&ctx);
cshake256_inc_squeeze(shared_secret, SHARED_SECRET_BYTES, &ctx);
}
/* Decapsulate a shared secret and return it */
void PQCLEAN_PAPABEAR_CLEAN_decapsulate(
uint8_t shared_secret[SHARED_SECRET_BYTES],
const uint8_t capsule[CAPSULE_BYTES],
const uint8_t sk[PRIVATE_KEY_BYTES]
) {
const uint8_t *lpr_data = &capsule[GF_BYTES * DIM];
shake256incctx ctx;
gf_t ska, b, c = {0};
uint8_t seed[ENC_SEED_BYTES + FEC_BYTES + IV_BYTES];
limb_t rounding, out;
size_t j;
limb_t our_rlimb, their_rlimb, delta;
uint8_t pk[PUBLIC_KEY_BYTES], capsule2[CAPSULE_BYTES];
uint8_t ret, ok, sep, prfk[PRF_KEY_BYTES];
uint8_t prfout[SHARED_SECRET_BYTES];
/* Calculate approximate shared secret */
threebears_hash_init(&ctx, HASH_PURPOSE_KEYGEN);
cshake256_inc_absorb(&ctx, sk, PRIVATE_KEY_BYTES);
for (uint8_t i = 0; i < DIM; i++) {
PQCLEAN_PAPABEAR_CLEAN_expand(b, &capsule[i * GF_BYTES]);
noise(ska, &ctx, i);
PQCLEAN_PAPABEAR_CLEAN_mac(c, ska, b);
}
/* Recover seed from LPR data */
PQCLEAN_PAPABEAR_CLEAN_canon(c);
rounding = 1 << (LPR_BITS - 1);
out = 0;
for (int32_t i = ENC_BITS - 1; i >= 0; i--) {
j = (size_t) ((i & 1) ? DIGITS - i / 2 - 1 : i / 2);
our_rlimb = (limb_t)(c[j] >> (LGX - LPR_BITS - 1));
their_rlimb = (limb_t)(lpr_data[i * LPR_BITS / 8] >> ((i * LPR_BITS) % 8));
delta = (limb_t)(their_rlimb * 2 - our_rlimb + rounding);
out |= (limb_t)(((delta >> LPR_BITS) & 1) << (i % 8));
if (i % 8 == 0) {
seed[i / 8] = (uint8_t)out;
out = 0;
}
}
PQCLEAN_PAPABEAR_CLEAN_melas_fec_correct(seed, ENC_SEED_BYTES, &seed[ENC_SEED_BYTES]);
/* Re-encapsulate and check; encapsulate will compute the shared secret */
//pk[PUBLIC_KEY_BYTES], capsule2[CAPSULE_BYTES];
PQCLEAN_PAPABEAR_CLEAN_get_pubkey(pk, sk);
memcpy(&seed[ENC_SEED_BYTES], &lpr_data[(ENC_BITS * LPR_BITS + 7) / 8], IV_BYTES);
PQCLEAN_PAPABEAR_CLEAN_encapsulate(shared_secret, capsule2, pk, seed);
/* Check capsule == capsule2 in constant time */
ret = 0;
for (size_t i = 0; i < CAPSULE_BYTES; i++) {
ret |= capsule[i] ^ capsule2[i];
}
ok = (uint8_t)(((int)ret - 1) >> 8);
/* Calculate PRF key */
sep = 0xFF;
cshake256_inc_absorb(&ctx, &sep, 1);
cshake256_inc_finalize(&ctx);
cshake256_inc_squeeze(prfk, PRF_KEY_BYTES, &ctx);
/* Calculate PRF */
threebears_hash_init(&ctx, HASH_PURPOSE_PRF);
cshake256_inc_absorb(&ctx, prfk, PRF_KEY_BYTES);
cshake256_inc_absorb(&ctx, capsule, CAPSULE_BYTES);
cshake256_inc_finalize(&ctx);
cshake256_inc_squeeze(prfout, SHARED_SECRET_BYTES, &ctx);
for (size_t i = 0; i < SHARED_SECRET_BYTES; i++) {
shared_secret[i] = (uint8_t)((shared_secret[i] & ok) | (prfout[i] & ~ok));
}
}

View File

@ -0,0 +1,60 @@
#ifndef __THREE_BEARS_PAPABEAR_H__
#define __THREE_BEARS_PAPABEAR_H__
#include <stddef.h> /* for size_t */
#include <stdint.h>
#define PAPABEAR_KEYGEN_SEED_BYTES 40
#define PAPABEAR_PRIVATE_KEY_BYTES PAPABEAR_KEYGEN_SEED_BYTES
#define PAPABEAR_SHARED_SECRET_BYTES 32
#define PAPABEAR_ENC_SEED_AND_IV_BYTES 32
#define PAPABEAR_PUBLIC_KEY_BYTES 1584
#define PAPABEAR_CAPSULE_BYTES 1697
/**
* Expand a secret seed to a public/private keypair.
*
* @param[out] pk The public key.
* @param[in] sk The private key, which must be uniformly random.
*/
void PapaBear_get_pubkey (
uint8_t pk[PAPABEAR_PUBLIC_KEY_BYTES],
const uint8_t sk[PAPABEAR_PRIVATE_KEY_BYTES]
);
/**
* Create a shared secret using a random seed and another party's public key.
*
* Input and output parameters may not alias.
*
* @param[out] shared_secret The shared secret key.
* @param[out] capsule A ciphertext to send to the other party.
* @param[in] pk The other party's public key.
* @param[in] seed A random seed.
*/
void PapaBear_encapsulate (
uint8_t shared_secret[PAPABEAR_SHARED_SECRET_BYTES],
uint8_t capsule[PAPABEAR_CAPSULE_BYTES],
const uint8_t pk[PAPABEAR_PUBLIC_KEY_BYTES],
const uint8_t seed[PAPABEAR_ENC_SEED_AND_IV_BYTES]
);
/**
* Extract the shared secret from a capsule using the private key.
* Has a negligible but nonzero probability of failure.
*
* Input and output parameters may not alias.
*
* @param[out] shared_secret The shared secret.
* @param[in] capsule The capsule produced by encapsulate_cca2.
* @param[in] sk The private key.
*/
void PapaBear_decapsulate (
uint8_t shared_secret[PAPABEAR_SHARED_SECRET_BYTES],
const uint8_t capsule[PAPABEAR_CAPSULE_BYTES],
const uint8_t sk[PAPABEAR_PRIVATE_KEY_BYTES]
);
void PQCLEAN_PAPABEAR_CLEAN_secure_bzero (void *s, size_t size);
#endif