mirror of
https://github.com/henrydcase/pqc.git
synced 2024-11-22 07:35:38 +00:00
add ThreeBears
This commit is contained in:
parent
d1f2d16f46
commit
3b740033ef
15
crypto_kem/babybear/META.yml
Normal file
15
crypto_kem/babybear/META.yml
Normal 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
|
||||
|
24
crypto_kem/babybear/clean/LICENSE
Normal file
24
crypto_kem/babybear/clean/LICENSE
Normal 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.
|
21
crypto_kem/babybear/clean/Makefile
Normal file
21
crypto_kem/babybear/clean/Makefile
Normal 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)
|
19
crypto_kem/babybear/clean/Makefile.Microsoft_nmake
Normal file
19
crypto_kem/babybear/clean/Makefile.Microsoft_nmake
Normal 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)
|
18
crypto_kem/babybear/clean/api.h
Normal file
18
crypto_kem/babybear/clean/api.h
Normal 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
|
22
crypto_kem/babybear/clean/kem.c
Normal file
22
crypto_kem/babybear/clean/kem.c
Normal 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;
|
||||
}
|
87
crypto_kem/babybear/clean/melas_fec.c
Normal file
87
crypto_kem/babybear/clean/melas_fec.c
Normal 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);
|
||||
}
|
||||
}
|
26
crypto_kem/babybear/clean/melas_fec.h
Normal file
26
crypto_kem/babybear/clean/melas_fec.h
Normal 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
|
29
crypto_kem/babybear/clean/params.h
Normal file
29
crypto_kem/babybear/clean/params.h
Normal 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
|
102
crypto_kem/babybear/clean/ring.c
Normal file
102
crypto_kem/babybear/clean/ring.c
Normal 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;
|
||||
}
|
||||
}
|
||||
}
|
31
crypto_kem/babybear/clean/ring.h
Normal file
31
crypto_kem/babybear/clean/ring.h
Normal 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
|
225
crypto_kem/babybear/clean/threebears.c
Normal file
225
crypto_kem/babybear/clean/threebears.c
Normal 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));
|
||||
}
|
||||
}
|
60
crypto_kem/babybear/clean/threebears.h
Normal file
60
crypto_kem/babybear/clean/threebears.h
Normal 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
|
15
crypto_kem/mamabear/META.yml
Normal file
15
crypto_kem/mamabear/META.yml
Normal 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
|
||||
|
24
crypto_kem/mamabear/clean/LICENSE
Normal file
24
crypto_kem/mamabear/clean/LICENSE
Normal 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.
|
21
crypto_kem/mamabear/clean/Makefile
Normal file
21
crypto_kem/mamabear/clean/Makefile
Normal 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)
|
19
crypto_kem/mamabear/clean/Makefile.Microsoft_nmake
Normal file
19
crypto_kem/mamabear/clean/Makefile.Microsoft_nmake
Normal 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)
|
18
crypto_kem/mamabear/clean/api.h
Normal file
18
crypto_kem/mamabear/clean/api.h
Normal 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
|
22
crypto_kem/mamabear/clean/kem.c
Normal file
22
crypto_kem/mamabear/clean/kem.c
Normal 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;
|
||||
}
|
87
crypto_kem/mamabear/clean/melas_fec.c
Normal file
87
crypto_kem/mamabear/clean/melas_fec.c
Normal 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);
|
||||
}
|
||||
}
|
26
crypto_kem/mamabear/clean/melas_fec.h
Normal file
26
crypto_kem/mamabear/clean/melas_fec.h
Normal 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
|
29
crypto_kem/mamabear/clean/params.h
Normal file
29
crypto_kem/mamabear/clean/params.h
Normal 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
|
102
crypto_kem/mamabear/clean/ring.c
Normal file
102
crypto_kem/mamabear/clean/ring.c
Normal 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;
|
||||
}
|
||||
}
|
||||
}
|
31
crypto_kem/mamabear/clean/ring.h
Normal file
31
crypto_kem/mamabear/clean/ring.h
Normal 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
|
225
crypto_kem/mamabear/clean/threebears.c
Normal file
225
crypto_kem/mamabear/clean/threebears.c
Normal 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));
|
||||
}
|
||||
}
|
60
crypto_kem/mamabear/clean/threebears.h
Normal file
60
crypto_kem/mamabear/clean/threebears.h
Normal 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
|
15
crypto_kem/papabear/META.yml
Normal file
15
crypto_kem/papabear/META.yml
Normal 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
|
||||
|
24
crypto_kem/papabear/clean/LICENSE
Normal file
24
crypto_kem/papabear/clean/LICENSE
Normal 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.
|
21
crypto_kem/papabear/clean/Makefile
Normal file
21
crypto_kem/papabear/clean/Makefile
Normal 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)
|
19
crypto_kem/papabear/clean/Makefile.Microsoft_nmake
Normal file
19
crypto_kem/papabear/clean/Makefile.Microsoft_nmake
Normal 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)
|
18
crypto_kem/papabear/clean/api.h
Normal file
18
crypto_kem/papabear/clean/api.h
Normal 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
|
22
crypto_kem/papabear/clean/kem.c
Normal file
22
crypto_kem/papabear/clean/kem.c
Normal 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;
|
||||
}
|
87
crypto_kem/papabear/clean/melas_fec.c
Normal file
87
crypto_kem/papabear/clean/melas_fec.c
Normal 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);
|
||||
}
|
||||
}
|
26
crypto_kem/papabear/clean/melas_fec.h
Normal file
26
crypto_kem/papabear/clean/melas_fec.h
Normal 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
|
29
crypto_kem/papabear/clean/params.h
Normal file
29
crypto_kem/papabear/clean/params.h
Normal 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
|
102
crypto_kem/papabear/clean/ring.c
Normal file
102
crypto_kem/papabear/clean/ring.c
Normal 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;
|
||||
}
|
||||
}
|
||||
}
|
31
crypto_kem/papabear/clean/ring.h
Normal file
31
crypto_kem/papabear/clean/ring.h
Normal 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
|
225
crypto_kem/papabear/clean/threebears.c
Normal file
225
crypto_kem/papabear/clean/threebears.c
Normal 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));
|
||||
}
|
||||
}
|
60
crypto_kem/papabear/clean/threebears.h
Normal file
60
crypto_kem/papabear/clean/threebears.h
Normal 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
|
Loading…
Reference in New Issue
Block a user