1
1
mirror of https://github.com/henrydcase/pqc.git synced 2024-11-22 23:48:58 +00:00

Add FrodoKEM-976-SHAKE and FrodoKEM-1344-SHAKE (#83)

Add FrodoKEM-976-SHAKE and FrodoKEM-1344-SHAKE
This commit is contained in:
Douglas Stebila 2019-04-09 09:37:51 -04:00 committed by GitHub
commit 27366d6559
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
31 changed files with 1561 additions and 22 deletions

View File

@ -0,0 +1,24 @@
name: FrodoKEM-1344-SHAKE
type: kem
claimed-nist-level: 5
length-public-key: 21520
length-ciphertext: 21632
length-shared-secret: 32
testvectors-sha256: 8b62fc01fc1e4b4e336776b09b37aaf55d161b7c815b3298f39d4444b011e10c
principal-submitter: Douglas Stebila, University of Waterloo
auxiliary-submitters:
- Erdem Alkim
- Joppe W. Bos, NXP Semiconductors
- Léo Ducas, CWI
- Patrick Longa, Microsoft Research
- Ilya Mironov, Google
- Michael Naehrig, Microsoft Research
- Valeria Nikolaenko
- Chris Peikert, University of Michigan
- Ananth Raghunathan, Google
- Karen Easterbrook, Microsoft Research
- Brian LaMacchia, Microsoft Research
implementations:
- name: clean
version: https://github.com/Microsoft/PQCrypto-LWEKE/commit/437e228fca580a82435cab09f30ae14b03183119
length-secret-key: 43088

View File

@ -0,0 +1,21 @@
MIT License
Copyright (c) Microsoft Corporation. All rights reserved.
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,19 @@
# This Makefile can be used with GNU Make or BSD Make
LIB=libfrodokem1344shake_clean.a
HEADERS=api.h params.h common.h
OBJECTS=kem.o matrix_shake.o noise.o util.o
CFLAGS=-Wall -Wextra -Wpedantic -Werror -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=libfrodokem1344shake_clean.lib
OBJECTS=kem.obj matrix_shake.obj noise.obj util.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,20 @@
#ifndef PQCLEAN_FRODOKEM1344SHAKE_CLEAN_API_H
#define PQCLEAN_FRODOKEM1344SHAKE_CLEAN_API_H
#include <stddef.h>
#include <stdint.h>
#define PQCLEAN_FRODOKEM1344SHAKE_CLEAN_CRYPTO_SECRETKEYBYTES 43088 // sizeof(s) + CRYPTO_PUBLICKEYBYTES + 2*PARAMS_N*PARAMS_NBAR + BYTES_PKHASH
#define PQCLEAN_FRODOKEM1344SHAKE_CLEAN_CRYPTO_PUBLICKEYBYTES 21520 // sizeof(seed_A) + (PARAMS_LOGQ*PARAMS_N*PARAMS_NBAR)/8
#define PQCLEAN_FRODOKEM1344SHAKE_CLEAN_CRYPTO_BYTES 32
#define PQCLEAN_FRODOKEM1344SHAKE_CLEAN_CRYPTO_CIPHERTEXTBYTES 21632 // (PARAMS_LOGQ*PARAMS_N*PARAMS_NBAR)/8 + (PARAMS_LOGQ*PARAMS_NBAR*PARAMS_NBAR)/8
#define PQCLEAN_FRODOKEM1344SHAKE_CLEAN_CRYPTO_ALGNAME "FrodoKEM-1344-SHAKE"
int PQCLEAN_FRODOKEM1344SHAKE_CLEAN_crypto_kem_keypair(uint8_t *pk, uint8_t *sk);
int PQCLEAN_FRODOKEM1344SHAKE_CLEAN_crypto_kem_enc(uint8_t *ct, uint8_t *ss, const uint8_t *pk);
int PQCLEAN_FRODOKEM1344SHAKE_CLEAN_crypto_kem_dec(uint8_t *ss, const uint8_t *ct, const uint8_t *sk);
#endif

View File

@ -0,0 +1,19 @@
#ifndef COMMON_H
#define COMMON_H
int PQCLEAN_FRODOKEM1344SHAKE_CLEAN_mul_add_as_plus_e(uint16_t *out, const uint16_t *s, const uint16_t *e, const uint8_t *seed_A);
int PQCLEAN_FRODOKEM1344SHAKE_CLEAN_mul_add_sa_plus_e(uint16_t *out, const uint16_t *s, const uint16_t *e, const uint8_t *seed_A);
void PQCLEAN_FRODOKEM1344SHAKE_CLEAN_sample_n(uint16_t *s, size_t n);
void PQCLEAN_FRODOKEM1344SHAKE_CLEAN_mul_bs(uint16_t *out, const uint16_t *b, const uint16_t *s);
void PQCLEAN_FRODOKEM1344SHAKE_CLEAN_mul_add_sb_plus_e(uint16_t *out, const uint16_t *b, const uint16_t *s, const uint16_t *e);
void PQCLEAN_FRODOKEM1344SHAKE_CLEAN_add(uint16_t *out, const uint16_t *a, const uint16_t *b);
void PQCLEAN_FRODOKEM1344SHAKE_CLEAN_sub(uint16_t *out, const uint16_t *a, const uint16_t *b);
void PQCLEAN_FRODOKEM1344SHAKE_CLEAN_key_encode(uint16_t *out, const uint16_t *in);
void PQCLEAN_FRODOKEM1344SHAKE_CLEAN_key_decode(uint16_t *out, const uint16_t *in);
void PQCLEAN_FRODOKEM1344SHAKE_CLEAN_pack(uint8_t *out, size_t outlen, const uint16_t *in, size_t inlen, uint8_t lsb);
void PQCLEAN_FRODOKEM1344SHAKE_CLEAN_unpack(uint16_t *out, size_t outlen, const uint8_t *in, size_t inlen, uint8_t lsb);
void PQCLEAN_FRODOKEM1344SHAKE_CLEAN_clear_bytes(uint8_t *mem, size_t n);
uint16_t PQCLEAN_FRODOKEM1344SHAKE_CLEAN_LE_TO_UINT16(uint16_t n);
uint16_t PQCLEAN_FRODOKEM1344SHAKE_CLEAN_UINT16_TO_LE(uint16_t n);
#endif

View File

@ -0,0 +1,238 @@
/********************************************************************************************
* FrodoKEM: Learning with Errors Key Encapsulation
*
* Abstract: Key Encapsulation Mechanism (KEM) based on Frodo
*********************************************************************************************/
#include <stdint.h>
#include <string.h>
#include "fips202.h"
#include "randombytes.h"
#include "api.h"
#include "common.h"
#include "params.h"
int PQCLEAN_FRODOKEM1344SHAKE_CLEAN_crypto_kem_keypair(uint8_t *pk, uint8_t *sk) {
// FrodoKEM's key generation
// Outputs: public key pk ( BYTES_SEED_A + (PARAMS_LOGQ*PARAMS_N*PARAMS_NBAR)/8 bytes)
// secret key sk (CRYPTO_BYTES + BYTES_SEED_A + (PARAMS_LOGQ*PARAMS_N*PARAMS_NBAR)/8 + 2*PARAMS_N*PARAMS_NBAR + BYTES_PKHASH bytes)
uint8_t *pk_seedA = &pk[0];
uint8_t *pk_b = &pk[BYTES_SEED_A];
uint8_t *sk_s = &sk[0];
uint8_t *sk_pk = &sk[CRYPTO_BYTES];
uint8_t *sk_S = &sk[CRYPTO_BYTES + CRYPTO_PUBLICKEYBYTES];
uint8_t *sk_pkh = &sk[CRYPTO_BYTES + CRYPTO_PUBLICKEYBYTES + 2 * PARAMS_N * PARAMS_NBAR];
uint16_t B[PARAMS_N * PARAMS_NBAR] = {0};
uint16_t S[2 * PARAMS_N * PARAMS_NBAR] = {0}; // contains secret data
uint16_t *E = &S[PARAMS_N * PARAMS_NBAR]; // contains secret data
uint8_t randomness[2 * CRYPTO_BYTES + BYTES_SEED_A]; // contains secret data via randomness_s and randomness_seedSE
uint8_t *randomness_s = &randomness[0]; // contains secret data
uint8_t *randomness_seedSE = &randomness[CRYPTO_BYTES]; // contains secret data
uint8_t *randomness_z = &randomness[2 * CRYPTO_BYTES];
uint8_t shake_input_seedSE[1 + CRYPTO_BYTES]; // contains secret data
// Generate the secret value s, the seed for S and E, and the seed for the seed for A. Add seed_A to the public key
randombytes(randomness, CRYPTO_BYTES + CRYPTO_BYTES + BYTES_SEED_A);
shake(pk_seedA, BYTES_SEED_A, randomness_z, BYTES_SEED_A);
// Generate S and E, and compute B = A*S + E. Generate A on-the-fly
shake_input_seedSE[0] = 0x5F;
memcpy(&shake_input_seedSE[1], randomness_seedSE, CRYPTO_BYTES);
shake((uint8_t *)S, 2 * PARAMS_N * PARAMS_NBAR * sizeof(uint16_t), shake_input_seedSE, 1 + CRYPTO_BYTES);
for (size_t i = 0; i < 2 * PARAMS_N * PARAMS_NBAR; i++) {
S[i] = PQCLEAN_FRODOKEM1344SHAKE_CLEAN_LE_TO_UINT16(S[i]);
}
PQCLEAN_FRODOKEM1344SHAKE_CLEAN_sample_n(S, PARAMS_N * PARAMS_NBAR);
PQCLEAN_FRODOKEM1344SHAKE_CLEAN_sample_n(E, PARAMS_N * PARAMS_NBAR);
PQCLEAN_FRODOKEM1344SHAKE_CLEAN_mul_add_as_plus_e(B, S, E, pk);
// Encode the second part of the public key
PQCLEAN_FRODOKEM1344SHAKE_CLEAN_pack(pk_b, CRYPTO_PUBLICKEYBYTES - BYTES_SEED_A, B, PARAMS_N * PARAMS_NBAR, PARAMS_LOGQ);
// Add s, pk and S to the secret key
memcpy(sk_s, randomness_s, CRYPTO_BYTES);
memcpy(sk_pk, pk, CRYPTO_PUBLICKEYBYTES);
for (size_t i = 0; i < PARAMS_N * PARAMS_NBAR; i++) {
S[i] = PQCLEAN_FRODOKEM1344SHAKE_CLEAN_UINT16_TO_LE(S[i]);
}
memcpy(sk_S, S, 2 * PARAMS_N * PARAMS_NBAR);
// Add H(pk) to the secret key
shake(sk_pkh, BYTES_PKHASH, pk, CRYPTO_PUBLICKEYBYTES);
// Cleanup:
PQCLEAN_FRODOKEM1344SHAKE_CLEAN_clear_bytes((uint8_t *)S, PARAMS_N * PARAMS_NBAR * sizeof(uint16_t));
PQCLEAN_FRODOKEM1344SHAKE_CLEAN_clear_bytes((uint8_t *)E, PARAMS_N * PARAMS_NBAR * sizeof(uint16_t));
PQCLEAN_FRODOKEM1344SHAKE_CLEAN_clear_bytes(randomness, 2 * CRYPTO_BYTES);
PQCLEAN_FRODOKEM1344SHAKE_CLEAN_clear_bytes(shake_input_seedSE, 1 + CRYPTO_BYTES);
return 0;
}
int PQCLEAN_FRODOKEM1344SHAKE_CLEAN_crypto_kem_enc(uint8_t *ct, uint8_t *ss, const uint8_t *pk) {
// FrodoKEM's key encapsulation
const uint8_t *pk_seedA = &pk[0];
const uint8_t *pk_b = &pk[BYTES_SEED_A];
uint8_t *ct_c1 = &ct[0];
uint8_t *ct_c2 = &ct[(PARAMS_LOGQ * PARAMS_N * PARAMS_NBAR) / 8];
uint16_t B[PARAMS_N * PARAMS_NBAR] = {0};
uint16_t V[PARAMS_NBAR * PARAMS_NBAR] = {0}; // contains secret data
uint16_t C[PARAMS_NBAR * PARAMS_NBAR] = {0};
uint16_t Bp[PARAMS_N * PARAMS_NBAR] = {0};
uint16_t Sp[(2 * PARAMS_N + PARAMS_NBAR)*PARAMS_NBAR] = {0}; // contains secret data
uint16_t *Ep = &Sp[PARAMS_N * PARAMS_NBAR]; // contains secret data
uint16_t *Epp = &Sp[2 * PARAMS_N * PARAMS_NBAR]; // contains secret data
uint8_t G2in[BYTES_PKHASH + BYTES_MU]; // contains secret data via mu
uint8_t *pkh = &G2in[0];
uint8_t *mu = &G2in[BYTES_PKHASH]; // contains secret data
uint8_t G2out[2 * CRYPTO_BYTES]; // contains secret data
uint8_t *seedSE = &G2out[0]; // contains secret data
uint8_t *k = &G2out[CRYPTO_BYTES]; // contains secret data
uint8_t Fin[CRYPTO_CIPHERTEXTBYTES + CRYPTO_BYTES]; // contains secret data via Fin_k
uint8_t *Fin_ct = &Fin[0];
uint8_t *Fin_k = &Fin[CRYPTO_CIPHERTEXTBYTES]; // contains secret data
uint8_t shake_input_seedSE[1 + CRYPTO_BYTES]; // contains secret data
// pkh <- G_1(pk), generate random mu, compute (seedSE || k) = G_2(pkh || mu)
shake(pkh, BYTES_PKHASH, pk, CRYPTO_PUBLICKEYBYTES);
randombytes(mu, BYTES_MU);
shake(G2out, CRYPTO_BYTES + CRYPTO_BYTES, G2in, BYTES_PKHASH + BYTES_MU);
// Generate Sp and Ep, and compute Bp = Sp*A + Ep. Generate A on-the-fly
shake_input_seedSE[0] = 0x96;
memcpy(&shake_input_seedSE[1], seedSE, CRYPTO_BYTES);
shake((uint8_t *)Sp, (2 * PARAMS_N + PARAMS_NBAR) * PARAMS_NBAR * sizeof(uint16_t), shake_input_seedSE, 1 + CRYPTO_BYTES);
for (size_t i = 0; i < (2 * PARAMS_N + PARAMS_NBAR) * PARAMS_NBAR; i++) {
Sp[i] = PQCLEAN_FRODOKEM1344SHAKE_CLEAN_LE_TO_UINT16(Sp[i]);
}
PQCLEAN_FRODOKEM1344SHAKE_CLEAN_sample_n(Sp, PARAMS_N * PARAMS_NBAR);
PQCLEAN_FRODOKEM1344SHAKE_CLEAN_sample_n(Ep, PARAMS_N * PARAMS_NBAR);
PQCLEAN_FRODOKEM1344SHAKE_CLEAN_mul_add_sa_plus_e(Bp, Sp, Ep, pk_seedA);
PQCLEAN_FRODOKEM1344SHAKE_CLEAN_pack(ct_c1, (PARAMS_LOGQ * PARAMS_N * PARAMS_NBAR) / 8, Bp, PARAMS_N * PARAMS_NBAR, PARAMS_LOGQ);
// Generate Epp, and compute V = Sp*B + Epp
PQCLEAN_FRODOKEM1344SHAKE_CLEAN_sample_n(Epp, PARAMS_NBAR * PARAMS_NBAR);
PQCLEAN_FRODOKEM1344SHAKE_CLEAN_unpack(B, PARAMS_N * PARAMS_NBAR, pk_b, CRYPTO_PUBLICKEYBYTES - BYTES_SEED_A, PARAMS_LOGQ);
PQCLEAN_FRODOKEM1344SHAKE_CLEAN_mul_add_sb_plus_e(V, B, Sp, Epp);
// Encode mu, and compute C = V + enc(mu) (mod q)
PQCLEAN_FRODOKEM1344SHAKE_CLEAN_key_encode(C, (uint16_t *)mu);
PQCLEAN_FRODOKEM1344SHAKE_CLEAN_add(C, V, C);
PQCLEAN_FRODOKEM1344SHAKE_CLEAN_pack(ct_c2, (PARAMS_LOGQ * PARAMS_NBAR * PARAMS_NBAR) / 8, C, PARAMS_NBAR * PARAMS_NBAR, PARAMS_LOGQ);
// Compute ss = F(ct||KK)
memcpy(Fin_ct, ct, CRYPTO_CIPHERTEXTBYTES);
memcpy(Fin_k, k, CRYPTO_BYTES);
shake(ss, CRYPTO_BYTES, Fin, CRYPTO_CIPHERTEXTBYTES + CRYPTO_BYTES);
// Cleanup:
PQCLEAN_FRODOKEM1344SHAKE_CLEAN_clear_bytes((uint8_t *)V, PARAMS_NBAR * PARAMS_NBAR * sizeof(uint16_t));
PQCLEAN_FRODOKEM1344SHAKE_CLEAN_clear_bytes((uint8_t *)Sp, PARAMS_N * PARAMS_NBAR * sizeof(uint16_t));
PQCLEAN_FRODOKEM1344SHAKE_CLEAN_clear_bytes((uint8_t *)Ep, PARAMS_N * PARAMS_NBAR * sizeof(uint16_t));
PQCLEAN_FRODOKEM1344SHAKE_CLEAN_clear_bytes((uint8_t *)Epp, PARAMS_NBAR * PARAMS_NBAR * sizeof(uint16_t));
PQCLEAN_FRODOKEM1344SHAKE_CLEAN_clear_bytes(mu, BYTES_MU);
PQCLEAN_FRODOKEM1344SHAKE_CLEAN_clear_bytes(G2out, 2 * CRYPTO_BYTES);
PQCLEAN_FRODOKEM1344SHAKE_CLEAN_clear_bytes(Fin_k, CRYPTO_BYTES);
PQCLEAN_FRODOKEM1344SHAKE_CLEAN_clear_bytes(shake_input_seedSE, 1 + CRYPTO_BYTES);
return 0;
}
int PQCLEAN_FRODOKEM1344SHAKE_CLEAN_crypto_kem_dec(uint8_t *ss, const uint8_t *ct, const uint8_t *sk) {
// FrodoKEM's key decapsulation
uint16_t B[PARAMS_N * PARAMS_NBAR] = {0};
uint16_t Bp[PARAMS_N * PARAMS_NBAR] = {0};
uint16_t W[PARAMS_NBAR * PARAMS_NBAR] = {0}; // contains secret data
uint16_t C[PARAMS_NBAR * PARAMS_NBAR] = {0};
uint16_t CC[PARAMS_NBAR * PARAMS_NBAR] = {0};
uint16_t BBp[PARAMS_N * PARAMS_NBAR] = {0};
uint16_t Sp[(2 * PARAMS_N + PARAMS_NBAR)*PARAMS_NBAR] = {0}; // contains secret data
uint16_t *Ep = &Sp[PARAMS_N * PARAMS_NBAR]; // contains secret data
uint16_t *Epp = &Sp[2 * PARAMS_N * PARAMS_NBAR]; // contains secret data
const uint8_t *ct_c1 = &ct[0];
const uint8_t *ct_c2 = &ct[(PARAMS_LOGQ * PARAMS_N * PARAMS_NBAR) / 8];
const uint8_t *sk_s = &sk[0];
const uint8_t *sk_pk = &sk[CRYPTO_BYTES];
const uint16_t *sk_S = (uint16_t *) &sk[CRYPTO_BYTES + CRYPTO_PUBLICKEYBYTES];
uint16_t S[PARAMS_N * PARAMS_NBAR]; // contains secret data
const uint8_t *sk_pkh = &sk[CRYPTO_BYTES + CRYPTO_PUBLICKEYBYTES + 2 * PARAMS_N * PARAMS_NBAR];
const uint8_t *pk_seedA = &sk_pk[0];
const uint8_t *pk_b = &sk_pk[BYTES_SEED_A];
uint8_t G2in[BYTES_PKHASH + BYTES_MU]; // contains secret data via muprime
uint8_t *pkh = &G2in[0];
uint8_t *muprime = &G2in[BYTES_PKHASH]; // contains secret data
uint8_t G2out[2 * CRYPTO_BYTES]; // contains secret data
uint8_t *seedSEprime = &G2out[0]; // contains secret data
uint8_t *kprime = &G2out[CRYPTO_BYTES]; // contains secret data
uint8_t Fin[CRYPTO_CIPHERTEXTBYTES + CRYPTO_BYTES]; // contains secret data via Fin_k
uint8_t *Fin_ct = &Fin[0];
uint8_t *Fin_k = &Fin[CRYPTO_CIPHERTEXTBYTES]; // contains secret data
uint8_t shake_input_seedSEprime[1 + CRYPTO_BYTES]; // contains secret data
for (size_t i = 0; i < PARAMS_N * PARAMS_NBAR; i++) {
S[i] = PQCLEAN_FRODOKEM1344SHAKE_CLEAN_LE_TO_UINT16(sk_S[i]);
}
// Compute W = C - Bp*S (mod q), and decode the randomness mu
PQCLEAN_FRODOKEM1344SHAKE_CLEAN_unpack(Bp, PARAMS_N * PARAMS_NBAR, ct_c1, (PARAMS_LOGQ * PARAMS_N * PARAMS_NBAR) / 8, PARAMS_LOGQ);
PQCLEAN_FRODOKEM1344SHAKE_CLEAN_unpack(C, PARAMS_NBAR * PARAMS_NBAR, ct_c2, (PARAMS_LOGQ * PARAMS_NBAR * PARAMS_NBAR) / 8, PARAMS_LOGQ);
PQCLEAN_FRODOKEM1344SHAKE_CLEAN_mul_bs(W, Bp, S);
PQCLEAN_FRODOKEM1344SHAKE_CLEAN_sub(W, C, W);
PQCLEAN_FRODOKEM1344SHAKE_CLEAN_key_decode((uint16_t *)muprime, W);
// Generate (seedSE' || k') = G_2(pkh || mu')
memcpy(pkh, sk_pkh, BYTES_PKHASH);
shake(G2out, CRYPTO_BYTES + CRYPTO_BYTES, G2in, BYTES_PKHASH + BYTES_MU);
// Generate Sp and Ep, and compute BBp = Sp*A + Ep. Generate A on-the-fly
shake_input_seedSEprime[0] = 0x96;
memcpy(&shake_input_seedSEprime[1], seedSEprime, CRYPTO_BYTES);
shake((uint8_t *)Sp, (2 * PARAMS_N + PARAMS_NBAR) * PARAMS_NBAR * sizeof(uint16_t), shake_input_seedSEprime, 1 + CRYPTO_BYTES);
for (size_t i = 0; i < (2 * PARAMS_N + PARAMS_NBAR) * PARAMS_NBAR; i++) {
Sp[i] = PQCLEAN_FRODOKEM1344SHAKE_CLEAN_LE_TO_UINT16(Sp[i]);
}
PQCLEAN_FRODOKEM1344SHAKE_CLEAN_sample_n(Sp, PARAMS_N * PARAMS_NBAR);
PQCLEAN_FRODOKEM1344SHAKE_CLEAN_sample_n(Ep, PARAMS_N * PARAMS_NBAR);
PQCLEAN_FRODOKEM1344SHAKE_CLEAN_mul_add_sa_plus_e(BBp, Sp, Ep, pk_seedA);
// Generate Epp, and compute W = Sp*B + Epp
PQCLEAN_FRODOKEM1344SHAKE_CLEAN_sample_n(Epp, PARAMS_NBAR * PARAMS_NBAR);
PQCLEAN_FRODOKEM1344SHAKE_CLEAN_unpack(B, PARAMS_N * PARAMS_NBAR, pk_b, CRYPTO_PUBLICKEYBYTES - BYTES_SEED_A, PARAMS_LOGQ);
PQCLEAN_FRODOKEM1344SHAKE_CLEAN_mul_add_sb_plus_e(W, B, Sp, Epp);
// Encode mu, and compute CC = W + enc(mu') (mod q)
PQCLEAN_FRODOKEM1344SHAKE_CLEAN_key_encode(CC, (uint16_t *)muprime);
PQCLEAN_FRODOKEM1344SHAKE_CLEAN_add(CC, W, CC);
// Prepare input to F
memcpy(Fin_ct, ct, CRYPTO_CIPHERTEXTBYTES);
// Reducing BBp modulo q
for (size_t i = 0; i < PARAMS_N * PARAMS_NBAR; i++) {
BBp[i] = BBp[i] & ((1 << PARAMS_LOGQ) - 1);
}
// Is (Bp == BBp & C == CC) = true
if (memcmp(Bp, BBp, 2 * PARAMS_N * PARAMS_NBAR) == 0 && memcmp(C, CC, 2 * PARAMS_NBAR * PARAMS_NBAR) == 0) {
// Load k' to do ss = F(ct || k')
memcpy(Fin_k, kprime, CRYPTO_BYTES);
} else {
// Load s to do ss = F(ct || s)
memcpy(Fin_k, sk_s, CRYPTO_BYTES);
}
shake(ss, CRYPTO_BYTES, Fin, CRYPTO_CIPHERTEXTBYTES + CRYPTO_BYTES);
// Cleanup:
PQCLEAN_FRODOKEM1344SHAKE_CLEAN_clear_bytes((uint8_t *)W, PARAMS_NBAR * PARAMS_NBAR * sizeof(uint16_t));
PQCLEAN_FRODOKEM1344SHAKE_CLEAN_clear_bytes((uint8_t *)Sp, PARAMS_N * PARAMS_NBAR * sizeof(uint16_t));
PQCLEAN_FRODOKEM1344SHAKE_CLEAN_clear_bytes((uint8_t *)S, PARAMS_N * PARAMS_NBAR * sizeof(uint16_t));
PQCLEAN_FRODOKEM1344SHAKE_CLEAN_clear_bytes((uint8_t *)Ep, PARAMS_N * PARAMS_NBAR * sizeof(uint16_t));
PQCLEAN_FRODOKEM1344SHAKE_CLEAN_clear_bytes((uint8_t *)Epp, PARAMS_NBAR * PARAMS_NBAR * sizeof(uint16_t));
PQCLEAN_FRODOKEM1344SHAKE_CLEAN_clear_bytes(muprime, BYTES_MU);
PQCLEAN_FRODOKEM1344SHAKE_CLEAN_clear_bytes(G2out, 2 * CRYPTO_BYTES);
PQCLEAN_FRODOKEM1344SHAKE_CLEAN_clear_bytes(Fin_k, CRYPTO_BYTES);
PQCLEAN_FRODOKEM1344SHAKE_CLEAN_clear_bytes(shake_input_seedSEprime, 1 + CRYPTO_BYTES);
return 0;
}

View File

@ -0,0 +1,79 @@
/********************************************************************************************
* FrodoKEM: Learning with Errors Key Encapsulation
*
* Abstract: matrix arithmetic functions used by the KEM
*********************************************************************************************/
#include <stdint.h>
#include <string.h>
#include "fips202.h"
#include "api.h"
#include "common.h"
#include "params.h"
int PQCLEAN_FRODOKEM1344SHAKE_CLEAN_mul_add_as_plus_e(uint16_t *out, const uint16_t *s, const uint16_t *e, const uint8_t *seed_A) {
// Generate-and-multiply: generate matrix A (N x N) row-wise, multiply by s on the right.
// Inputs: s, e (N x N_BAR)
// Output: out = A*s + e (N x N_BAR)
int i, j, k;
int16_t A[PARAMS_N * PARAMS_N] = {0};
uint8_t seed_A_separated[2 + BYTES_SEED_A];
uint16_t *seed_A_origin = (uint16_t *)&seed_A_separated;
memcpy(&seed_A_separated[2], seed_A, BYTES_SEED_A);
for (i = 0; i < PARAMS_N; i++) {
seed_A_origin[0] = PQCLEAN_FRODOKEM1344SHAKE_CLEAN_UINT16_TO_LE((uint16_t) i);
shake128((uint8_t *)(A + i * PARAMS_N), (unsigned long long)(2 * PARAMS_N), seed_A_separated, 2 + BYTES_SEED_A);
}
for (i = 0; i < PARAMS_N * PARAMS_N; i++) {
A[i] = PQCLEAN_FRODOKEM1344SHAKE_CLEAN_LE_TO_UINT16(A[i]);
}
memcpy(out, e, PARAMS_NBAR * PARAMS_N * sizeof(uint16_t));
for (i = 0; i < PARAMS_N; i++) { // Matrix multiplication-addition A*s + e
for (k = 0; k < PARAMS_NBAR; k++) {
uint16_t sum = 0;
for (j = 0; j < PARAMS_N; j++) {
sum += A[i * PARAMS_N + j] * s[k * PARAMS_N + j];
}
out[i * PARAMS_NBAR + k] += sum; // Adding e. No need to reduce modulo 2^15, extra bits are taken care of during packing later on.
}
}
return 1;
}
int PQCLEAN_FRODOKEM1344SHAKE_CLEAN_mul_add_sa_plus_e(uint16_t *out, const uint16_t *s, const uint16_t *e, const uint8_t *seed_A) {
// Generate-and-multiply: generate matrix A (N x N) column-wise, multiply by s' on the left.
// Inputs: s', e' (N_BAR x N)
// Output: out = s'*A + e' (N_BAR x N)
int i, j, k;
int16_t A[PARAMS_N * PARAMS_N] = {0};
uint8_t seed_A_separated[2 + BYTES_SEED_A];
uint16_t *seed_A_origin = (uint16_t *)&seed_A_separated;
memcpy(&seed_A_separated[2], seed_A, BYTES_SEED_A);
for (i = 0; i < PARAMS_N; i++) {
seed_A_origin[0] = PQCLEAN_FRODOKEM1344SHAKE_CLEAN_UINT16_TO_LE((uint16_t) i);
shake128((uint8_t *)(A + i * PARAMS_N), (unsigned long long)(2 * PARAMS_N), seed_A_separated, 2 + BYTES_SEED_A);
}
for (i = 0; i < PARAMS_N * PARAMS_N; i++) {
A[i] = PQCLEAN_FRODOKEM1344SHAKE_CLEAN_LE_TO_UINT16(A[i]);
}
memcpy(out, e, PARAMS_NBAR * PARAMS_N * sizeof(uint16_t));
for (i = 0; i < PARAMS_N; i++) { // Matrix multiplication-addition A*s + e
for (k = 0; k < PARAMS_NBAR; k++) {
uint16_t sum = 0;
for (j = 0; j < PARAMS_N; j++) {
sum += A[j * PARAMS_N + i] * s[k * PARAMS_N + j];
}
out[k * PARAMS_N + i] += sum; // Adding e. No need to reduce modulo 2^15, extra bits are taken care of during packing later on.
}
}
return 1;
}

View File

@ -0,0 +1,33 @@
/********************************************************************************************
* FrodoKEM: Learning with Errors Key Encapsulation
*
* Abstract: noise sampling functions
*********************************************************************************************/
#include <stdint.h>
#include "api.h"
#include "params.h"
static uint16_t CDF_TABLE[CDF_TABLE_LEN] = CDF_TABLE_DATA;
void PQCLEAN_FRODOKEM1344SHAKE_CLEAN_sample_n(uint16_t *s, const size_t n) {
// Fills vector s with n samples from the noise distribution which requires 16 bits to sample.
// The distribution is specified by its CDF.
// Input: pseudo-random values (2*n bytes) passed in s. The input is overwritten by the output.
unsigned int i, j;
for (i = 0; i < n; ++i) {
uint8_t sample = 0;
uint16_t prnd = s[i] >> 1; // Drop the least significant bit
uint8_t sign = s[i] & 0x1; // Pick the least significant bit
// No need to compare with the last value.
for (j = 0; j < (unsigned int)(CDF_TABLE_LEN - 1); j++) {
// Constant time comparison: 1 if CDF_TABLE[j] < s, 0 otherwise. Uses the fact that CDF_TABLE[j] and s fit in 15 bits.
sample += (uint16_t)(CDF_TABLE[j] - prnd) >> 15;
}
// Assuming that sign is either 0 or 1, flips sample iff sign = 1
s[i] = ((-sign) ^ sample) + sign;
}
}

View File

@ -0,0 +1,27 @@
#ifndef PARAMS_H
#define PARAMS_H
#define CRYPTO_SECRETKEYBYTES PQCLEAN_FRODOKEM1344SHAKE_CLEAN_CRYPTO_SECRETKEYBYTES
#define CRYPTO_PUBLICKEYBYTES PQCLEAN_FRODOKEM1344SHAKE_CLEAN_CRYPTO_PUBLICKEYBYTES
#define CRYPTO_BYTES PQCLEAN_FRODOKEM1344SHAKE_CLEAN_CRYPTO_BYTES
#define CRYPTO_CIPHERTEXTBYTES PQCLEAN_FRODOKEM1344SHAKE_CLEAN_CRYPTO_CIPHERTEXTBYTES
#define PARAMS_N 1344
#define PARAMS_NBAR 8
#define PARAMS_LOGQ 16
#define PARAMS_Q (1 << PARAMS_LOGQ)
#define PARAMS_EXTRACTED_BITS 4
#define PARAMS_STRIPE_STEP 8
#define PARAMS_PARALLEL 4
#define BYTES_SEED_A 16
#define BYTES_MU ((PARAMS_EXTRACTED_BITS * PARAMS_NBAR * PARAMS_NBAR) / 8)
#define BYTES_PKHASH CRYPTO_BYTES
// Selecting SHAKE XOF function for the KEM and noise sampling
#define shake shake256
// CDF table
#define CDF_TABLE_DATA {9142, 23462, 30338, 32361, 32725, 32765, 32767}
#define CDF_TABLE_LEN 7
#endif

View File

@ -0,0 +1,234 @@
/********************************************************************************************
* FrodoKEM: Learning with Errors Key Encapsulation
*
* Abstract: additional functions for FrodoKEM
*********************************************************************************************/
#include <stdint.h>
#include <string.h>
#include "api.h"
#include "params.h"
#define min(x, y) (((x) < (y)) ? (x) : (y))
uint16_t PQCLEAN_FRODOKEM1344SHAKE_CLEAN_LE_TO_UINT16(const uint16_t n) {
return (((uint8_t *) &(n))[0] | (((uint8_t *) &(n))[1] << 8));
}
uint16_t PQCLEAN_FRODOKEM1344SHAKE_CLEAN_UINT16_TO_LE(const uint16_t n) {
uint16_t y;
uint8_t *z = (uint8_t *) &y;
z[0] = n & 0xFF;
z[1] = (n & 0xFF00) >> 8;
return y;
}
void PQCLEAN_FRODOKEM1344SHAKE_CLEAN_mul_bs(uint16_t *out, const uint16_t *b, const uint16_t *s) {
// Multiply by s on the right
// Inputs: b (N_BAR x N), s (N x N_BAR)
// Output: out = b*s (N_BAR x N_BAR)
int i, j, k;
for (i = 0; i < PARAMS_NBAR; i++) {
for (j = 0; j < PARAMS_NBAR; j++) {
out[i * PARAMS_NBAR + j] = 0;
for (k = 0; k < PARAMS_N; k++) {
out[i * PARAMS_NBAR + j] += b[i * PARAMS_N + k] * s[j * PARAMS_N + k];
}
out[i * PARAMS_NBAR + j] = (uint32_t)(out[i * PARAMS_NBAR + j]) & ((1 << PARAMS_LOGQ) - 1);
}
}
}
void PQCLEAN_FRODOKEM1344SHAKE_CLEAN_mul_add_sb_plus_e(uint16_t *out, const uint16_t *b, const uint16_t *s, const uint16_t *e) {
// Multiply by s on the left
// Inputs: b (N x N_BAR), s (N_BAR x N), e (N_BAR x N_BAR)
// Output: out = s*b + e (N_BAR x N_BAR)
int i, j, k;
for (k = 0; k < PARAMS_NBAR; k++) {
for (i = 0; i < PARAMS_NBAR; i++) {
out[k * PARAMS_NBAR + i] = e[k * PARAMS_NBAR + i];
for (j = 0; j < PARAMS_N; j++) {
out[k * PARAMS_NBAR + i] += s[k * PARAMS_N + j] * b[j * PARAMS_NBAR + i];
}
out[k * PARAMS_NBAR + i] = (uint32_t)(out[k * PARAMS_NBAR + i]) & ((1 << PARAMS_LOGQ) - 1);
}
}
}
void PQCLEAN_FRODOKEM1344SHAKE_CLEAN_add(uint16_t *out, const uint16_t *a, const uint16_t *b) {
// Add a and b
// Inputs: a, b (N_BAR x N_BAR)
// Output: c = a + b
for (size_t i = 0; i < (PARAMS_NBAR * PARAMS_NBAR); i++) {
out[i] = (a[i] + b[i]) & ((1 << PARAMS_LOGQ) - 1);
}
}
void PQCLEAN_FRODOKEM1344SHAKE_CLEAN_sub(uint16_t *out, const uint16_t *a, const uint16_t *b) {
// Subtract a and b
// Inputs: a, b (N_BAR x N_BAR)
// Output: c = a - b
for (size_t i = 0; i < (PARAMS_NBAR * PARAMS_NBAR); i++) {
out[i] = (a[i] - b[i]) & ((1 << PARAMS_LOGQ) - 1);
}
}
void PQCLEAN_FRODOKEM1344SHAKE_CLEAN_key_encode(uint16_t *out, const uint16_t *in) {
// Encoding
unsigned int i, j, npieces_word = 8;
unsigned int nwords = (PARAMS_NBAR * PARAMS_NBAR) / 8;
uint64_t temp, mask = ((uint64_t)1 << PARAMS_EXTRACTED_BITS) - 1;
uint16_t *pos = out;
for (i = 0; i < nwords; i++) {
temp = 0;
for (j = 0; j < PARAMS_EXTRACTED_BITS; j++) {
temp |= ((uint64_t)((uint8_t *)in)[i * PARAMS_EXTRACTED_BITS + j]) << (8 * j);
}
for (j = 0; j < npieces_word; j++) {
*pos = (uint16_t)((temp & mask) << (PARAMS_LOGQ - PARAMS_EXTRACTED_BITS));
temp >>= PARAMS_EXTRACTED_BITS;
pos++;
}
}
}
void PQCLEAN_FRODOKEM1344SHAKE_CLEAN_key_decode(uint16_t *out, const uint16_t *in) {
// Decoding
unsigned int i, j, index = 0, npieces_word = 8;
unsigned int nwords = (PARAMS_NBAR * PARAMS_NBAR) / 8;
uint16_t temp, maskex = ((uint16_t)1 << PARAMS_EXTRACTED_BITS) - 1, maskq = ((uint16_t)1 << PARAMS_LOGQ) - 1;
uint8_t *pos = (uint8_t *)out;
uint64_t templong;
for (i = 0; i < nwords; i++) {
templong = 0;
for (j = 0; j < npieces_word; j++) { // temp = floor(in*2^{-11}+0.5)
temp = ((in[index] & maskq) + (1 << (PARAMS_LOGQ - PARAMS_EXTRACTED_BITS - 1))) >> (PARAMS_LOGQ - PARAMS_EXTRACTED_BITS);
templong |= ((uint64_t)(temp & maskex)) << (PARAMS_EXTRACTED_BITS * j);
index++;
}
for (j = 0; j < PARAMS_EXTRACTED_BITS; j++) {
pos[i * PARAMS_EXTRACTED_BITS + j] = (templong >> (8 * j)) & 0xFF;
}
}
}
void PQCLEAN_FRODOKEM1344SHAKE_CLEAN_pack(uint8_t *out, const size_t outlen, const uint16_t *in, const size_t inlen, const uint8_t lsb) {
// Pack the input uint16 vector into a char output vector, copying lsb bits from each input element.
// If inlen * lsb / 8 > outlen, only outlen * 8 bits are copied.
memset(out, 0, outlen);
size_t i = 0; // whole bytes already filled in
size_t j = 0; // whole uint16_t already copied
uint16_t w = 0; // the leftover, not yet copied
uint8_t bits = 0; // the number of lsb in w
while (i < outlen && (j < inlen || ((j == inlen) && (bits > 0)))) {
/*
in: | | |********|********|
^
j
w : | ****|
^
bits
out:|**|**|**|**|**|**|**|**|* |
^^
ib
*/
uint8_t b = 0; // bits in out[i] already filled in
while (b < 8) {
int nbits = min(8 - b, bits);
uint16_t mask = (1 << nbits) - 1;
uint8_t t = (uint8_t) ((w >> (bits - nbits)) & mask); // the bits to copy from w to out
out[i] = out[i] + (t << (8 - b - nbits));
b += (uint8_t) nbits;
bits -= (uint8_t) nbits;
w &= ~(mask << bits); // not strictly necessary; mostly for debugging
if (bits == 0) {
if (j < inlen) {
w = in[j];
bits = lsb;
j++;
} else {
break; // the input vector is exhausted
}
}
}
if (b == 8) { // out[i] is filled in
i++;
}
}
}
void PQCLEAN_FRODOKEM1344SHAKE_CLEAN_unpack(uint16_t *out, const size_t outlen, const uint8_t *in, const size_t inlen, const uint8_t lsb) {
// Unpack the input char vector into a uint16_t output vector, copying lsb bits
// for each output element from input. outlen must be at least ceil(inlen * 8 / lsb).
memset(out, 0, outlen * sizeof(uint16_t));
size_t i = 0; // whole uint16_t already filled in
size_t j = 0; // whole bytes already copied
uint8_t w = 0; // the leftover, not yet copied
uint8_t bits = 0; // the number of lsb bits of w
while (i < outlen && (j < inlen || ((j == inlen) && (bits > 0)))) {
/*
in: | | | | | | |**|**|...
^
j
w : | *|
^
bits
out:| *****| *****| *** | |...
^ ^
i b
*/
uint8_t b = 0; // bits in out[i] already filled in
while (b < lsb) {
int nbits = min(lsb - b, bits);
uint16_t mask = (1 << nbits) - 1;
uint8_t t = (w >> (bits - nbits)) & mask; // the bits to copy from w to out
out[i] = out[i] + (t << (lsb - b - nbits));
b += (uint8_t) nbits;
bits -= (uint8_t) nbits;
w &= ~(mask << bits); // not strictly necessary; mostly for debugging
if (bits == 0) {
if (j < inlen) {
w = in[j];
bits = 8;
j++;
} else {
break; // the input vector is exhausted
}
}
}
if (b == lsb) { // out[i] is filled in
i++;
}
}
}
void PQCLEAN_FRODOKEM1344SHAKE_CLEAN_clear_bytes(uint8_t *mem, size_t n) {
// Clear 8-bit bytes from memory. "n" indicates the number of bytes to be zeroed.
// This function uses the volatile type qualifier to inform the compiler not to optimize out the memory clearing.
volatile uint8_t *v = mem;
for (size_t i = 0; i < n; i++) {
v[i] = 0;
}
}

View File

@ -0,0 +1,24 @@
name: FrodoKEM-976-SHAKE
type: kem
claimed-nist-level: 3
length-public-key: 15632
length-ciphertext: 15744
length-shared-secret: 24
testvectors-sha256: 00707dc8158c6e51e70e9a7b23a87054c5f2167b77a2e5940b8e82519834717b
principal-submitter: Douglas Stebila, University of Waterloo
auxiliary-submitters:
- Erdem Alkim
- Joppe W. Bos, NXP Semiconductors
- Léo Ducas, CWI
- Patrick Longa, Microsoft Research
- Ilya Mironov, Google
- Michael Naehrig, Microsoft Research
- Valeria Nikolaenko
- Chris Peikert, University of Michigan
- Ananth Raghunathan, Google
- Karen Easterbrook, Microsoft Research
- Brian LaMacchia, Microsoft Research
implementations:
- name: clean
version: https://github.com/Microsoft/PQCrypto-LWEKE/commit/437e228fca580a82435cab09f30ae14b03183119
length-secret-key: 31296

View File

@ -0,0 +1,21 @@
MIT License
Copyright (c) Microsoft Corporation. All rights reserved.
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,19 @@
# This Makefile can be used with GNU Make or BSD Make
LIB=libfrodokem976shake_clean.a
HEADERS=api.h params.h common.h
OBJECTS=kem.o matrix_shake.o noise.o util.o
CFLAGS=-Wall -Wextra -Wpedantic -Werror -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=libfrodokem976shake_clean.lib
OBJECTS=kem.obj matrix_shake.obj noise.obj util.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,20 @@
#ifndef PQCLEAN_FRODOKEM976SHAKE_CLEAN_API_H
#define PQCLEAN_FRODOKEM976SHAKE_CLEAN_API_H
#include <stddef.h>
#include <stdint.h>
#define PQCLEAN_FRODOKEM976SHAKE_CLEAN_CRYPTO_SECRETKEYBYTES 31296 // sizeof(s) + CRYPTO_PUBLICKEYBYTES + 2*PARAMS_N*PARAMS_NBAR + BYTES_PKHASH
#define PQCLEAN_FRODOKEM976SHAKE_CLEAN_CRYPTO_PUBLICKEYBYTES 15632 // sizeof(seed_A) + (PARAMS_LOGQ*PARAMS_N*PARAMS_NBAR)/8
#define PQCLEAN_FRODOKEM976SHAKE_CLEAN_CRYPTO_BYTES 24
#define PQCLEAN_FRODOKEM976SHAKE_CLEAN_CRYPTO_CIPHERTEXTBYTES 15744 // (PARAMS_LOGQ*PARAMS_N*PARAMS_NBAR)/8 + (PARAMS_LOGQ*PARAMS_NBAR*PARAMS_NBAR)/8
#define PQCLEAN_FRODOKEM976SHAKE_CLEAN_CRYPTO_ALGNAME "FrodoKEM-976-SHAKE"
int PQCLEAN_FRODOKEM976SHAKE_CLEAN_crypto_kem_keypair(uint8_t *pk, uint8_t *sk);
int PQCLEAN_FRODOKEM976SHAKE_CLEAN_crypto_kem_enc(uint8_t *ct, uint8_t *ss, const uint8_t *pk);
int PQCLEAN_FRODOKEM976SHAKE_CLEAN_crypto_kem_dec(uint8_t *ss, const uint8_t *ct, const uint8_t *sk);
#endif

View File

@ -0,0 +1,19 @@
#ifndef COMMON_H
#define COMMON_H
int PQCLEAN_FRODOKEM976SHAKE_CLEAN_mul_add_as_plus_e(uint16_t *out, const uint16_t *s, const uint16_t *e, const uint8_t *seed_A);
int PQCLEAN_FRODOKEM976SHAKE_CLEAN_mul_add_sa_plus_e(uint16_t *out, const uint16_t *s, const uint16_t *e, const uint8_t *seed_A);
void PQCLEAN_FRODOKEM976SHAKE_CLEAN_sample_n(uint16_t *s, size_t n);
void PQCLEAN_FRODOKEM976SHAKE_CLEAN_mul_bs(uint16_t *out, const uint16_t *b, const uint16_t *s);
void PQCLEAN_FRODOKEM976SHAKE_CLEAN_mul_add_sb_plus_e(uint16_t *out, const uint16_t *b, const uint16_t *s, const uint16_t *e);
void PQCLEAN_FRODOKEM976SHAKE_CLEAN_add(uint16_t *out, const uint16_t *a, const uint16_t *b);
void PQCLEAN_FRODOKEM976SHAKE_CLEAN_sub(uint16_t *out, const uint16_t *a, const uint16_t *b);
void PQCLEAN_FRODOKEM976SHAKE_CLEAN_key_encode(uint16_t *out, const uint16_t *in);
void PQCLEAN_FRODOKEM976SHAKE_CLEAN_key_decode(uint16_t *out, const uint16_t *in);
void PQCLEAN_FRODOKEM976SHAKE_CLEAN_pack(uint8_t *out, size_t outlen, const uint16_t *in, size_t inlen, uint8_t lsb);
void PQCLEAN_FRODOKEM976SHAKE_CLEAN_unpack(uint16_t *out, size_t outlen, const uint8_t *in, size_t inlen, uint8_t lsb);
void PQCLEAN_FRODOKEM976SHAKE_CLEAN_clear_bytes(uint8_t *mem, size_t n);
uint16_t PQCLEAN_FRODOKEM976SHAKE_CLEAN_LE_TO_UINT16(uint16_t n);
uint16_t PQCLEAN_FRODOKEM976SHAKE_CLEAN_UINT16_TO_LE(uint16_t n);
#endif

View File

@ -0,0 +1,238 @@
/********************************************************************************************
* FrodoKEM: Learning with Errors Key Encapsulation
*
* Abstract: Key Encapsulation Mechanism (KEM) based on Frodo
*********************************************************************************************/
#include <stdint.h>
#include <string.h>
#include "fips202.h"
#include "randombytes.h"
#include "api.h"
#include "common.h"
#include "params.h"
int PQCLEAN_FRODOKEM976SHAKE_CLEAN_crypto_kem_keypair(uint8_t *pk, uint8_t *sk) {
// FrodoKEM's key generation
// Outputs: public key pk ( BYTES_SEED_A + (PARAMS_LOGQ*PARAMS_N*PARAMS_NBAR)/8 bytes)
// secret key sk (CRYPTO_BYTES + BYTES_SEED_A + (PARAMS_LOGQ*PARAMS_N*PARAMS_NBAR)/8 + 2*PARAMS_N*PARAMS_NBAR + BYTES_PKHASH bytes)
uint8_t *pk_seedA = &pk[0];
uint8_t *pk_b = &pk[BYTES_SEED_A];
uint8_t *sk_s = &sk[0];
uint8_t *sk_pk = &sk[CRYPTO_BYTES];
uint8_t *sk_S = &sk[CRYPTO_BYTES + CRYPTO_PUBLICKEYBYTES];
uint8_t *sk_pkh = &sk[CRYPTO_BYTES + CRYPTO_PUBLICKEYBYTES + 2 * PARAMS_N * PARAMS_NBAR];
uint16_t B[PARAMS_N * PARAMS_NBAR] = {0};
uint16_t S[2 * PARAMS_N * PARAMS_NBAR] = {0}; // contains secret data
uint16_t *E = &S[PARAMS_N * PARAMS_NBAR]; // contains secret data
uint8_t randomness[2 * CRYPTO_BYTES + BYTES_SEED_A]; // contains secret data via randomness_s and randomness_seedSE
uint8_t *randomness_s = &randomness[0]; // contains secret data
uint8_t *randomness_seedSE = &randomness[CRYPTO_BYTES]; // contains secret data
uint8_t *randomness_z = &randomness[2 * CRYPTO_BYTES];
uint8_t shake_input_seedSE[1 + CRYPTO_BYTES]; // contains secret data
// Generate the secret value s, the seed for S and E, and the seed for the seed for A. Add seed_A to the public key
randombytes(randomness, CRYPTO_BYTES + CRYPTO_BYTES + BYTES_SEED_A);
shake(pk_seedA, BYTES_SEED_A, randomness_z, BYTES_SEED_A);
// Generate S and E, and compute B = A*S + E. Generate A on-the-fly
shake_input_seedSE[0] = 0x5F;
memcpy(&shake_input_seedSE[1], randomness_seedSE, CRYPTO_BYTES);
shake((uint8_t *)S, 2 * PARAMS_N * PARAMS_NBAR * sizeof(uint16_t), shake_input_seedSE, 1 + CRYPTO_BYTES);
for (size_t i = 0; i < 2 * PARAMS_N * PARAMS_NBAR; i++) {
S[i] = PQCLEAN_FRODOKEM976SHAKE_CLEAN_LE_TO_UINT16(S[i]);
}
PQCLEAN_FRODOKEM976SHAKE_CLEAN_sample_n(S, PARAMS_N * PARAMS_NBAR);
PQCLEAN_FRODOKEM976SHAKE_CLEAN_sample_n(E, PARAMS_N * PARAMS_NBAR);
PQCLEAN_FRODOKEM976SHAKE_CLEAN_mul_add_as_plus_e(B, S, E, pk);
// Encode the second part of the public key
PQCLEAN_FRODOKEM976SHAKE_CLEAN_pack(pk_b, CRYPTO_PUBLICKEYBYTES - BYTES_SEED_A, B, PARAMS_N * PARAMS_NBAR, PARAMS_LOGQ);
// Add s, pk and S to the secret key
memcpy(sk_s, randomness_s, CRYPTO_BYTES);
memcpy(sk_pk, pk, CRYPTO_PUBLICKEYBYTES);
for (size_t i = 0; i < PARAMS_N * PARAMS_NBAR; i++) {
S[i] = PQCLEAN_FRODOKEM976SHAKE_CLEAN_UINT16_TO_LE(S[i]);
}
memcpy(sk_S, S, 2 * PARAMS_N * PARAMS_NBAR);
// Add H(pk) to the secret key
shake(sk_pkh, BYTES_PKHASH, pk, CRYPTO_PUBLICKEYBYTES);
// Cleanup:
PQCLEAN_FRODOKEM976SHAKE_CLEAN_clear_bytes((uint8_t *)S, PARAMS_N * PARAMS_NBAR * sizeof(uint16_t));
PQCLEAN_FRODOKEM976SHAKE_CLEAN_clear_bytes((uint8_t *)E, PARAMS_N * PARAMS_NBAR * sizeof(uint16_t));
PQCLEAN_FRODOKEM976SHAKE_CLEAN_clear_bytes(randomness, 2 * CRYPTO_BYTES);
PQCLEAN_FRODOKEM976SHAKE_CLEAN_clear_bytes(shake_input_seedSE, 1 + CRYPTO_BYTES);
return 0;
}
int PQCLEAN_FRODOKEM976SHAKE_CLEAN_crypto_kem_enc(uint8_t *ct, uint8_t *ss, const uint8_t *pk) {
// FrodoKEM's key encapsulation
const uint8_t *pk_seedA = &pk[0];
const uint8_t *pk_b = &pk[BYTES_SEED_A];
uint8_t *ct_c1 = &ct[0];
uint8_t *ct_c2 = &ct[(PARAMS_LOGQ * PARAMS_N * PARAMS_NBAR) / 8];
uint16_t B[PARAMS_N * PARAMS_NBAR] = {0};
uint16_t V[PARAMS_NBAR * PARAMS_NBAR] = {0}; // contains secret data
uint16_t C[PARAMS_NBAR * PARAMS_NBAR] = {0};
uint16_t Bp[PARAMS_N * PARAMS_NBAR] = {0};
uint16_t Sp[(2 * PARAMS_N + PARAMS_NBAR)*PARAMS_NBAR] = {0}; // contains secret data
uint16_t *Ep = &Sp[PARAMS_N * PARAMS_NBAR]; // contains secret data
uint16_t *Epp = &Sp[2 * PARAMS_N * PARAMS_NBAR]; // contains secret data
uint8_t G2in[BYTES_PKHASH + BYTES_MU]; // contains secret data via mu
uint8_t *pkh = &G2in[0];
uint8_t *mu = &G2in[BYTES_PKHASH]; // contains secret data
uint8_t G2out[2 * CRYPTO_BYTES]; // contains secret data
uint8_t *seedSE = &G2out[0]; // contains secret data
uint8_t *k = &G2out[CRYPTO_BYTES]; // contains secret data
uint8_t Fin[CRYPTO_CIPHERTEXTBYTES + CRYPTO_BYTES]; // contains secret data via Fin_k
uint8_t *Fin_ct = &Fin[0];
uint8_t *Fin_k = &Fin[CRYPTO_CIPHERTEXTBYTES]; // contains secret data
uint8_t shake_input_seedSE[1 + CRYPTO_BYTES]; // contains secret data
// pkh <- G_1(pk), generate random mu, compute (seedSE || k) = G_2(pkh || mu)
shake(pkh, BYTES_PKHASH, pk, CRYPTO_PUBLICKEYBYTES);
randombytes(mu, BYTES_MU);
shake(G2out, CRYPTO_BYTES + CRYPTO_BYTES, G2in, BYTES_PKHASH + BYTES_MU);
// Generate Sp and Ep, and compute Bp = Sp*A + Ep. Generate A on-the-fly
shake_input_seedSE[0] = 0x96;
memcpy(&shake_input_seedSE[1], seedSE, CRYPTO_BYTES);
shake((uint8_t *)Sp, (2 * PARAMS_N + PARAMS_NBAR) * PARAMS_NBAR * sizeof(uint16_t), shake_input_seedSE, 1 + CRYPTO_BYTES);
for (size_t i = 0; i < (2 * PARAMS_N + PARAMS_NBAR) * PARAMS_NBAR; i++) {
Sp[i] = PQCLEAN_FRODOKEM976SHAKE_CLEAN_LE_TO_UINT16(Sp[i]);
}
PQCLEAN_FRODOKEM976SHAKE_CLEAN_sample_n(Sp, PARAMS_N * PARAMS_NBAR);
PQCLEAN_FRODOKEM976SHAKE_CLEAN_sample_n(Ep, PARAMS_N * PARAMS_NBAR);
PQCLEAN_FRODOKEM976SHAKE_CLEAN_mul_add_sa_plus_e(Bp, Sp, Ep, pk_seedA);
PQCLEAN_FRODOKEM976SHAKE_CLEAN_pack(ct_c1, (PARAMS_LOGQ * PARAMS_N * PARAMS_NBAR) / 8, Bp, PARAMS_N * PARAMS_NBAR, PARAMS_LOGQ);
// Generate Epp, and compute V = Sp*B + Epp
PQCLEAN_FRODOKEM976SHAKE_CLEAN_sample_n(Epp, PARAMS_NBAR * PARAMS_NBAR);
PQCLEAN_FRODOKEM976SHAKE_CLEAN_unpack(B, PARAMS_N * PARAMS_NBAR, pk_b, CRYPTO_PUBLICKEYBYTES - BYTES_SEED_A, PARAMS_LOGQ);
PQCLEAN_FRODOKEM976SHAKE_CLEAN_mul_add_sb_plus_e(V, B, Sp, Epp);
// Encode mu, and compute C = V + enc(mu) (mod q)
PQCLEAN_FRODOKEM976SHAKE_CLEAN_key_encode(C, (uint16_t *)mu);
PQCLEAN_FRODOKEM976SHAKE_CLEAN_add(C, V, C);
PQCLEAN_FRODOKEM976SHAKE_CLEAN_pack(ct_c2, (PARAMS_LOGQ * PARAMS_NBAR * PARAMS_NBAR) / 8, C, PARAMS_NBAR * PARAMS_NBAR, PARAMS_LOGQ);
// Compute ss = F(ct||KK)
memcpy(Fin_ct, ct, CRYPTO_CIPHERTEXTBYTES);
memcpy(Fin_k, k, CRYPTO_BYTES);
shake(ss, CRYPTO_BYTES, Fin, CRYPTO_CIPHERTEXTBYTES + CRYPTO_BYTES);
// Cleanup:
PQCLEAN_FRODOKEM976SHAKE_CLEAN_clear_bytes((uint8_t *)V, PARAMS_NBAR * PARAMS_NBAR * sizeof(uint16_t));
PQCLEAN_FRODOKEM976SHAKE_CLEAN_clear_bytes((uint8_t *)Sp, PARAMS_N * PARAMS_NBAR * sizeof(uint16_t));
PQCLEAN_FRODOKEM976SHAKE_CLEAN_clear_bytes((uint8_t *)Ep, PARAMS_N * PARAMS_NBAR * sizeof(uint16_t));
PQCLEAN_FRODOKEM976SHAKE_CLEAN_clear_bytes((uint8_t *)Epp, PARAMS_NBAR * PARAMS_NBAR * sizeof(uint16_t));
PQCLEAN_FRODOKEM976SHAKE_CLEAN_clear_bytes(mu, BYTES_MU);
PQCLEAN_FRODOKEM976SHAKE_CLEAN_clear_bytes(G2out, 2 * CRYPTO_BYTES);
PQCLEAN_FRODOKEM976SHAKE_CLEAN_clear_bytes(Fin_k, CRYPTO_BYTES);
PQCLEAN_FRODOKEM976SHAKE_CLEAN_clear_bytes(shake_input_seedSE, 1 + CRYPTO_BYTES);
return 0;
}
int PQCLEAN_FRODOKEM976SHAKE_CLEAN_crypto_kem_dec(uint8_t *ss, const uint8_t *ct, const uint8_t *sk) {
// FrodoKEM's key decapsulation
uint16_t B[PARAMS_N * PARAMS_NBAR] = {0};
uint16_t Bp[PARAMS_N * PARAMS_NBAR] = {0};
uint16_t W[PARAMS_NBAR * PARAMS_NBAR] = {0}; // contains secret data
uint16_t C[PARAMS_NBAR * PARAMS_NBAR] = {0};
uint16_t CC[PARAMS_NBAR * PARAMS_NBAR] = {0};
uint16_t BBp[PARAMS_N * PARAMS_NBAR] = {0};
uint16_t Sp[(2 * PARAMS_N + PARAMS_NBAR)*PARAMS_NBAR] = {0}; // contains secret data
uint16_t *Ep = &Sp[PARAMS_N * PARAMS_NBAR]; // contains secret data
uint16_t *Epp = &Sp[2 * PARAMS_N * PARAMS_NBAR]; // contains secret data
const uint8_t *ct_c1 = &ct[0];
const uint8_t *ct_c2 = &ct[(PARAMS_LOGQ * PARAMS_N * PARAMS_NBAR) / 8];
const uint8_t *sk_s = &sk[0];
const uint8_t *sk_pk = &sk[CRYPTO_BYTES];
const uint16_t *sk_S = (uint16_t *) &sk[CRYPTO_BYTES + CRYPTO_PUBLICKEYBYTES];
uint16_t S[PARAMS_N * PARAMS_NBAR]; // contains secret data
const uint8_t *sk_pkh = &sk[CRYPTO_BYTES + CRYPTO_PUBLICKEYBYTES + 2 * PARAMS_N * PARAMS_NBAR];
const uint8_t *pk_seedA = &sk_pk[0];
const uint8_t *pk_b = &sk_pk[BYTES_SEED_A];
uint8_t G2in[BYTES_PKHASH + BYTES_MU]; // contains secret data via muprime
uint8_t *pkh = &G2in[0];
uint8_t *muprime = &G2in[BYTES_PKHASH]; // contains secret data
uint8_t G2out[2 * CRYPTO_BYTES]; // contains secret data
uint8_t *seedSEprime = &G2out[0]; // contains secret data
uint8_t *kprime = &G2out[CRYPTO_BYTES]; // contains secret data
uint8_t Fin[CRYPTO_CIPHERTEXTBYTES + CRYPTO_BYTES]; // contains secret data via Fin_k
uint8_t *Fin_ct = &Fin[0];
uint8_t *Fin_k = &Fin[CRYPTO_CIPHERTEXTBYTES]; // contains secret data
uint8_t shake_input_seedSEprime[1 + CRYPTO_BYTES]; // contains secret data
for (size_t i = 0; i < PARAMS_N * PARAMS_NBAR; i++) {
S[i] = PQCLEAN_FRODOKEM976SHAKE_CLEAN_LE_TO_UINT16(sk_S[i]);
}
// Compute W = C - Bp*S (mod q), and decode the randomness mu
PQCLEAN_FRODOKEM976SHAKE_CLEAN_unpack(Bp, PARAMS_N * PARAMS_NBAR, ct_c1, (PARAMS_LOGQ * PARAMS_N * PARAMS_NBAR) / 8, PARAMS_LOGQ);
PQCLEAN_FRODOKEM976SHAKE_CLEAN_unpack(C, PARAMS_NBAR * PARAMS_NBAR, ct_c2, (PARAMS_LOGQ * PARAMS_NBAR * PARAMS_NBAR) / 8, PARAMS_LOGQ);
PQCLEAN_FRODOKEM976SHAKE_CLEAN_mul_bs(W, Bp, S);
PQCLEAN_FRODOKEM976SHAKE_CLEAN_sub(W, C, W);
PQCLEAN_FRODOKEM976SHAKE_CLEAN_key_decode((uint16_t *)muprime, W);
// Generate (seedSE' || k') = G_2(pkh || mu')
memcpy(pkh, sk_pkh, BYTES_PKHASH);
shake(G2out, CRYPTO_BYTES + CRYPTO_BYTES, G2in, BYTES_PKHASH + BYTES_MU);
// Generate Sp and Ep, and compute BBp = Sp*A + Ep. Generate A on-the-fly
shake_input_seedSEprime[0] = 0x96;
memcpy(&shake_input_seedSEprime[1], seedSEprime, CRYPTO_BYTES);
shake((uint8_t *)Sp, (2 * PARAMS_N + PARAMS_NBAR) * PARAMS_NBAR * sizeof(uint16_t), shake_input_seedSEprime, 1 + CRYPTO_BYTES);
for (size_t i = 0; i < (2 * PARAMS_N + PARAMS_NBAR) * PARAMS_NBAR; i++) {
Sp[i] = PQCLEAN_FRODOKEM976SHAKE_CLEAN_LE_TO_UINT16(Sp[i]);
}
PQCLEAN_FRODOKEM976SHAKE_CLEAN_sample_n(Sp, PARAMS_N * PARAMS_NBAR);
PQCLEAN_FRODOKEM976SHAKE_CLEAN_sample_n(Ep, PARAMS_N * PARAMS_NBAR);
PQCLEAN_FRODOKEM976SHAKE_CLEAN_mul_add_sa_plus_e(BBp, Sp, Ep, pk_seedA);
// Generate Epp, and compute W = Sp*B + Epp
PQCLEAN_FRODOKEM976SHAKE_CLEAN_sample_n(Epp, PARAMS_NBAR * PARAMS_NBAR);
PQCLEAN_FRODOKEM976SHAKE_CLEAN_unpack(B, PARAMS_N * PARAMS_NBAR, pk_b, CRYPTO_PUBLICKEYBYTES - BYTES_SEED_A, PARAMS_LOGQ);
PQCLEAN_FRODOKEM976SHAKE_CLEAN_mul_add_sb_plus_e(W, B, Sp, Epp);
// Encode mu, and compute CC = W + enc(mu') (mod q)
PQCLEAN_FRODOKEM976SHAKE_CLEAN_key_encode(CC, (uint16_t *)muprime);
PQCLEAN_FRODOKEM976SHAKE_CLEAN_add(CC, W, CC);
// Prepare input to F
memcpy(Fin_ct, ct, CRYPTO_CIPHERTEXTBYTES);
// Reducing BBp modulo q
for (size_t i = 0; i < PARAMS_N * PARAMS_NBAR; i++) {
BBp[i] = BBp[i] & ((1 << PARAMS_LOGQ) - 1);
}
// Is (Bp == BBp & C == CC) = true
if (memcmp(Bp, BBp, 2 * PARAMS_N * PARAMS_NBAR) == 0 && memcmp(C, CC, 2 * PARAMS_NBAR * PARAMS_NBAR) == 0) {
// Load k' to do ss = F(ct || k')
memcpy(Fin_k, kprime, CRYPTO_BYTES);
} else {
// Load s to do ss = F(ct || s)
memcpy(Fin_k, sk_s, CRYPTO_BYTES);
}
shake(ss, CRYPTO_BYTES, Fin, CRYPTO_CIPHERTEXTBYTES + CRYPTO_BYTES);
// Cleanup:
PQCLEAN_FRODOKEM976SHAKE_CLEAN_clear_bytes((uint8_t *)W, PARAMS_NBAR * PARAMS_NBAR * sizeof(uint16_t));
PQCLEAN_FRODOKEM976SHAKE_CLEAN_clear_bytes((uint8_t *)Sp, PARAMS_N * PARAMS_NBAR * sizeof(uint16_t));
PQCLEAN_FRODOKEM976SHAKE_CLEAN_clear_bytes((uint8_t *)S, PARAMS_N * PARAMS_NBAR * sizeof(uint16_t));
PQCLEAN_FRODOKEM976SHAKE_CLEAN_clear_bytes((uint8_t *)Ep, PARAMS_N * PARAMS_NBAR * sizeof(uint16_t));
PQCLEAN_FRODOKEM976SHAKE_CLEAN_clear_bytes((uint8_t *)Epp, PARAMS_NBAR * PARAMS_NBAR * sizeof(uint16_t));
PQCLEAN_FRODOKEM976SHAKE_CLEAN_clear_bytes(muprime, BYTES_MU);
PQCLEAN_FRODOKEM976SHAKE_CLEAN_clear_bytes(G2out, 2 * CRYPTO_BYTES);
PQCLEAN_FRODOKEM976SHAKE_CLEAN_clear_bytes(Fin_k, CRYPTO_BYTES);
PQCLEAN_FRODOKEM976SHAKE_CLEAN_clear_bytes(shake_input_seedSEprime, 1 + CRYPTO_BYTES);
return 0;
}

View File

@ -0,0 +1,79 @@
/********************************************************************************************
* FrodoKEM: Learning with Errors Key Encapsulation
*
* Abstract: matrix arithmetic functions used by the KEM
*********************************************************************************************/
#include <stdint.h>
#include <string.h>
#include "fips202.h"
#include "api.h"
#include "common.h"
#include "params.h"
int PQCLEAN_FRODOKEM976SHAKE_CLEAN_mul_add_as_plus_e(uint16_t *out, const uint16_t *s, const uint16_t *e, const uint8_t *seed_A) {
// Generate-and-multiply: generate matrix A (N x N) row-wise, multiply by s on the right.
// Inputs: s, e (N x N_BAR)
// Output: out = A*s + e (N x N_BAR)
int i, j, k;
int16_t A[PARAMS_N * PARAMS_N] = {0};
uint8_t seed_A_separated[2 + BYTES_SEED_A];
uint16_t *seed_A_origin = (uint16_t *)&seed_A_separated;
memcpy(&seed_A_separated[2], seed_A, BYTES_SEED_A);
for (i = 0; i < PARAMS_N; i++) {
seed_A_origin[0] = PQCLEAN_FRODOKEM976SHAKE_CLEAN_UINT16_TO_LE((uint16_t) i);
shake128((uint8_t *)(A + i * PARAMS_N), (unsigned long long)(2 * PARAMS_N), seed_A_separated, 2 + BYTES_SEED_A);
}
for (i = 0; i < PARAMS_N * PARAMS_N; i++) {
A[i] = PQCLEAN_FRODOKEM976SHAKE_CLEAN_LE_TO_UINT16(A[i]);
}
memcpy(out, e, PARAMS_NBAR * PARAMS_N * sizeof(uint16_t));
for (i = 0; i < PARAMS_N; i++) { // Matrix multiplication-addition A*s + e
for (k = 0; k < PARAMS_NBAR; k++) {
uint16_t sum = 0;
for (j = 0; j < PARAMS_N; j++) {
sum += A[i * PARAMS_N + j] * s[k * PARAMS_N + j];
}
out[i * PARAMS_NBAR + k] += sum; // Adding e. No need to reduce modulo 2^15, extra bits are taken care of during packing later on.
}
}
return 1;
}
int PQCLEAN_FRODOKEM976SHAKE_CLEAN_mul_add_sa_plus_e(uint16_t *out, const uint16_t *s, const uint16_t *e, const uint8_t *seed_A) {
// Generate-and-multiply: generate matrix A (N x N) column-wise, multiply by s' on the left.
// Inputs: s', e' (N_BAR x N)
// Output: out = s'*A + e' (N_BAR x N)
int i, j, k;
int16_t A[PARAMS_N * PARAMS_N] = {0};
uint8_t seed_A_separated[2 + BYTES_SEED_A];
uint16_t *seed_A_origin = (uint16_t *)&seed_A_separated;
memcpy(&seed_A_separated[2], seed_A, BYTES_SEED_A);
for (i = 0; i < PARAMS_N; i++) {
seed_A_origin[0] = PQCLEAN_FRODOKEM976SHAKE_CLEAN_UINT16_TO_LE((uint16_t) i);
shake128((uint8_t *)(A + i * PARAMS_N), (unsigned long long)(2 * PARAMS_N), seed_A_separated, 2 + BYTES_SEED_A);
}
for (i = 0; i < PARAMS_N * PARAMS_N; i++) {
A[i] = PQCLEAN_FRODOKEM976SHAKE_CLEAN_LE_TO_UINT16(A[i]);
}
memcpy(out, e, PARAMS_NBAR * PARAMS_N * sizeof(uint16_t));
for (i = 0; i < PARAMS_N; i++) { // Matrix multiplication-addition A*s + e
for (k = 0; k < PARAMS_NBAR; k++) {
uint16_t sum = 0;
for (j = 0; j < PARAMS_N; j++) {
sum += A[j * PARAMS_N + i] * s[k * PARAMS_N + j];
}
out[k * PARAMS_N + i] += sum; // Adding e. No need to reduce modulo 2^15, extra bits are taken care of during packing later on.
}
}
return 1;
}

View File

@ -0,0 +1,33 @@
/********************************************************************************************
* FrodoKEM: Learning with Errors Key Encapsulation
*
* Abstract: noise sampling functions
*********************************************************************************************/
#include <stdint.h>
#include "api.h"
#include "params.h"
static uint16_t CDF_TABLE[CDF_TABLE_LEN] = CDF_TABLE_DATA;
void PQCLEAN_FRODOKEM976SHAKE_CLEAN_sample_n(uint16_t *s, const size_t n) {
// Fills vector s with n samples from the noise distribution which requires 16 bits to sample.
// The distribution is specified by its CDF.
// Input: pseudo-random values (2*n bytes) passed in s. The input is overwritten by the output.
unsigned int i, j;
for (i = 0; i < n; ++i) {
uint8_t sample = 0;
uint16_t prnd = s[i] >> 1; // Drop the least significant bit
uint8_t sign = s[i] & 0x1; // Pick the least significant bit
// No need to compare with the last value.
for (j = 0; j < (unsigned int)(CDF_TABLE_LEN - 1); j++) {
// Constant time comparison: 1 if CDF_TABLE[j] < s, 0 otherwise. Uses the fact that CDF_TABLE[j] and s fit in 15 bits.
sample += (uint16_t)(CDF_TABLE[j] - prnd) >> 15;
}
// Assuming that sign is either 0 or 1, flips sample iff sign = 1
s[i] = ((-sign) ^ sample) + sign;
}
}

View File

@ -0,0 +1,27 @@
#ifndef PARAMS_H
#define PARAMS_H
#define CRYPTO_SECRETKEYBYTES PQCLEAN_FRODOKEM976SHAKE_CLEAN_CRYPTO_SECRETKEYBYTES
#define CRYPTO_PUBLICKEYBYTES PQCLEAN_FRODOKEM976SHAKE_CLEAN_CRYPTO_PUBLICKEYBYTES
#define CRYPTO_BYTES PQCLEAN_FRODOKEM976SHAKE_CLEAN_CRYPTO_BYTES
#define CRYPTO_CIPHERTEXTBYTES PQCLEAN_FRODOKEM976SHAKE_CLEAN_CRYPTO_CIPHERTEXTBYTES
#define PARAMS_N 976
#define PARAMS_NBAR 8
#define PARAMS_LOGQ 16
#define PARAMS_Q (1 << PARAMS_LOGQ)
#define PARAMS_EXTRACTED_BITS 3
#define PARAMS_STRIPE_STEP 8
#define PARAMS_PARALLEL 4
#define BYTES_SEED_A 16
#define BYTES_MU ((PARAMS_EXTRACTED_BITS * PARAMS_NBAR * PARAMS_NBAR) / 8)
#define BYTES_PKHASH CRYPTO_BYTES
// Selecting SHAKE XOF function for the KEM and noise sampling
#define shake shake256
// CDF table
#define CDF_TABLE_DATA {5638, 15915, 23689, 28571, 31116, 32217, 32613, 32731, 32760, 32766, 32767}
#define CDF_TABLE_LEN 11
#endif

View File

@ -0,0 +1,234 @@
/********************************************************************************************
* FrodoKEM: Learning with Errors Key Encapsulation
*
* Abstract: additional functions for FrodoKEM
*********************************************************************************************/
#include <stdint.h>
#include <string.h>
#include "api.h"
#include "params.h"
#define min(x, y) (((x) < (y)) ? (x) : (y))
uint16_t PQCLEAN_FRODOKEM976SHAKE_CLEAN_LE_TO_UINT16(const uint16_t n) {
return (((uint8_t *) &(n))[0] | (((uint8_t *) &(n))[1] << 8));
}
uint16_t PQCLEAN_FRODOKEM976SHAKE_CLEAN_UINT16_TO_LE(const uint16_t n) {
uint16_t y;
uint8_t *z = (uint8_t *) &y;
z[0] = n & 0xFF;
z[1] = (n & 0xFF00) >> 8;
return y;
}
void PQCLEAN_FRODOKEM976SHAKE_CLEAN_mul_bs(uint16_t *out, const uint16_t *b, const uint16_t *s) {
// Multiply by s on the right
// Inputs: b (N_BAR x N), s (N x N_BAR)
// Output: out = b*s (N_BAR x N_BAR)
int i, j, k;
for (i = 0; i < PARAMS_NBAR; i++) {
for (j = 0; j < PARAMS_NBAR; j++) {
out[i * PARAMS_NBAR + j] = 0;
for (k = 0; k < PARAMS_N; k++) {
out[i * PARAMS_NBAR + j] += b[i * PARAMS_N + k] * s[j * PARAMS_N + k];
}
out[i * PARAMS_NBAR + j] = (uint32_t)(out[i * PARAMS_NBAR + j]) & ((1 << PARAMS_LOGQ) - 1);
}
}
}
void PQCLEAN_FRODOKEM976SHAKE_CLEAN_mul_add_sb_plus_e(uint16_t *out, const uint16_t *b, const uint16_t *s, const uint16_t *e) {
// Multiply by s on the left
// Inputs: b (N x N_BAR), s (N_BAR x N), e (N_BAR x N_BAR)
// Output: out = s*b + e (N_BAR x N_BAR)
int i, j, k;
for (k = 0; k < PARAMS_NBAR; k++) {
for (i = 0; i < PARAMS_NBAR; i++) {
out[k * PARAMS_NBAR + i] = e[k * PARAMS_NBAR + i];
for (j = 0; j < PARAMS_N; j++) {
out[k * PARAMS_NBAR + i] += s[k * PARAMS_N + j] * b[j * PARAMS_NBAR + i];
}
out[k * PARAMS_NBAR + i] = (uint32_t)(out[k * PARAMS_NBAR + i]) & ((1 << PARAMS_LOGQ) - 1);
}
}
}
void PQCLEAN_FRODOKEM976SHAKE_CLEAN_add(uint16_t *out, const uint16_t *a, const uint16_t *b) {
// Add a and b
// Inputs: a, b (N_BAR x N_BAR)
// Output: c = a + b
for (size_t i = 0; i < (PARAMS_NBAR * PARAMS_NBAR); i++) {
out[i] = (a[i] + b[i]) & ((1 << PARAMS_LOGQ) - 1);
}
}
void PQCLEAN_FRODOKEM976SHAKE_CLEAN_sub(uint16_t *out, const uint16_t *a, const uint16_t *b) {
// Subtract a and b
// Inputs: a, b (N_BAR x N_BAR)
// Output: c = a - b
for (size_t i = 0; i < (PARAMS_NBAR * PARAMS_NBAR); i++) {
out[i] = (a[i] - b[i]) & ((1 << PARAMS_LOGQ) - 1);
}
}
void PQCLEAN_FRODOKEM976SHAKE_CLEAN_key_encode(uint16_t *out, const uint16_t *in) {
// Encoding
unsigned int i, j, npieces_word = 8;
unsigned int nwords = (PARAMS_NBAR * PARAMS_NBAR) / 8;
uint64_t temp, mask = ((uint64_t)1 << PARAMS_EXTRACTED_BITS) - 1;
uint16_t *pos = out;
for (i = 0; i < nwords; i++) {
temp = 0;
for (j = 0; j < PARAMS_EXTRACTED_BITS; j++) {
temp |= ((uint64_t)((uint8_t *)in)[i * PARAMS_EXTRACTED_BITS + j]) << (8 * j);
}
for (j = 0; j < npieces_word; j++) {
*pos = (uint16_t)((temp & mask) << (PARAMS_LOGQ - PARAMS_EXTRACTED_BITS));
temp >>= PARAMS_EXTRACTED_BITS;
pos++;
}
}
}
void PQCLEAN_FRODOKEM976SHAKE_CLEAN_key_decode(uint16_t *out, const uint16_t *in) {
// Decoding
unsigned int i, j, index = 0, npieces_word = 8;
unsigned int nwords = (PARAMS_NBAR * PARAMS_NBAR) / 8;
uint16_t temp, maskex = ((uint16_t)1 << PARAMS_EXTRACTED_BITS) - 1, maskq = ((uint16_t)1 << PARAMS_LOGQ) - 1;
uint8_t *pos = (uint8_t *)out;
uint64_t templong;
for (i = 0; i < nwords; i++) {
templong = 0;
for (j = 0; j < npieces_word; j++) { // temp = floor(in*2^{-11}+0.5)
temp = ((in[index] & maskq) + (1 << (PARAMS_LOGQ - PARAMS_EXTRACTED_BITS - 1))) >> (PARAMS_LOGQ - PARAMS_EXTRACTED_BITS);
templong |= ((uint64_t)(temp & maskex)) << (PARAMS_EXTRACTED_BITS * j);
index++;
}
for (j = 0; j < PARAMS_EXTRACTED_BITS; j++) {
pos[i * PARAMS_EXTRACTED_BITS + j] = (templong >> (8 * j)) & 0xFF;
}
}
}
void PQCLEAN_FRODOKEM976SHAKE_CLEAN_pack(uint8_t *out, const size_t outlen, const uint16_t *in, const size_t inlen, const uint8_t lsb) {
// Pack the input uint16 vector into a char output vector, copying lsb bits from each input element.
// If inlen * lsb / 8 > outlen, only outlen * 8 bits are copied.
memset(out, 0, outlen);
size_t i = 0; // whole bytes already filled in
size_t j = 0; // whole uint16_t already copied
uint16_t w = 0; // the leftover, not yet copied
uint8_t bits = 0; // the number of lsb in w
while (i < outlen && (j < inlen || ((j == inlen) && (bits > 0)))) {
/*
in: | | |********|********|
^
j
w : | ****|
^
bits
out:|**|**|**|**|**|**|**|**|* |
^^
ib
*/
uint8_t b = 0; // bits in out[i] already filled in
while (b < 8) {
int nbits = min(8 - b, bits);
uint16_t mask = (1 << nbits) - 1;
uint8_t t = (uint8_t) ((w >> (bits - nbits)) & mask); // the bits to copy from w to out
out[i] = out[i] + (t << (8 - b - nbits));
b += (uint8_t) nbits;
bits -= (uint8_t) nbits;
w &= ~(mask << bits); // not strictly necessary; mostly for debugging
if (bits == 0) {
if (j < inlen) {
w = in[j];
bits = lsb;
j++;
} else {
break; // the input vector is exhausted
}
}
}
if (b == 8) { // out[i] is filled in
i++;
}
}
}
void PQCLEAN_FRODOKEM976SHAKE_CLEAN_unpack(uint16_t *out, const size_t outlen, const uint8_t *in, const size_t inlen, const uint8_t lsb) {
// Unpack the input char vector into a uint16_t output vector, copying lsb bits
// for each output element from input. outlen must be at least ceil(inlen * 8 / lsb).
memset(out, 0, outlen * sizeof(uint16_t));
size_t i = 0; // whole uint16_t already filled in
size_t j = 0; // whole bytes already copied
uint8_t w = 0; // the leftover, not yet copied
uint8_t bits = 0; // the number of lsb bits of w
while (i < outlen && (j < inlen || ((j == inlen) && (bits > 0)))) {
/*
in: | | | | | | |**|**|...
^
j
w : | *|
^
bits
out:| *****| *****| *** | |...
^ ^
i b
*/
uint8_t b = 0; // bits in out[i] already filled in
while (b < lsb) {
int nbits = min(lsb - b, bits);
uint16_t mask = (1 << nbits) - 1;
uint8_t t = (w >> (bits - nbits)) & mask; // the bits to copy from w to out
out[i] = out[i] + (t << (lsb - b - nbits));
b += (uint8_t) nbits;
bits -= (uint8_t) nbits;
w &= ~(mask << bits); // not strictly necessary; mostly for debugging
if (bits == 0) {
if (j < inlen) {
w = in[j];
bits = 8;
j++;
} else {
break; // the input vector is exhausted
}
}
}
if (b == lsb) { // out[i] is filled in
i++;
}
}
}
void PQCLEAN_FRODOKEM976SHAKE_CLEAN_clear_bytes(uint8_t *mem, size_t n) {
// Clear 8-bit bytes from memory. "n" indicates the number of bytes to be zeroed.
// This function uses the volatile type qualifier to inform the compiler not to optimize out the memory clearing.
volatile uint8_t *v = mem;
for (size_t i = 0; i < n; i++) {
v[i] = 0;
}
}

View File

@ -41,13 +41,13 @@ $(DEST_DIR)\functest_$(SCHEME)_$(IMPLEMENTATION).exe: build-scheme $(COMMON_OBJE
-MKDIR $(DEST_DIR)
-DEL functest.obj
$(CC) /c crypto_$(TYPE)\functest.c $(CFLAGS) /I $(SCHEME_DIR) /DPQCLEAN_NAMESPACE=PQCLEAN_$(SCHEME_UPPERCASE)_$(IMPLEMENTATION_UPPERCASE)
LINK.EXE /OUT:$@ functest.obj $(COMMON_OBJECTS_NOPATH) randombytes.obj $(SCHEME_DIR)\lib$(SCHEME)_$(IMPLEMENTATION).lib Advapi32.lib
LINK.EXE /STACK:8192000 /OUT:$@ functest.obj $(COMMON_OBJECTS_NOPATH) randombytes.obj $(SCHEME_DIR)\lib$(SCHEME)_$(IMPLEMENTATION).lib Advapi32.lib
$(DEST_DIR)\testvectors_$(SCHEME)_$(IMPLEMENTATION).exe: build-scheme $(COMMON_OBJECTS) $(COMMON_DIR)\notrandombytes.obj
-MKDIR $(DEST_DIR)
-DEL testvectors.obj
$(CC) /c crypto_$(TYPE)\testvectors.c $(CFLAGS) /I $(SCHEME_DIR) /DPQCLEAN_NAMESPACE=PQCLEAN_$(SCHEME_UPPERCASE)_$(IMPLEMENTATION_UPPERCASE)
LINK.EXE /OUT:$@ testvectors.obj $(COMMON_OBJECTS_NOPATH) notrandombytes.obj $(SCHEME_DIR)\lib$(SCHEME)_$(IMPLEMENTATION).lib
LINK.EXE /STACK:8192000 /OUT:$@ testvectors.obj $(COMMON_OBJECTS_NOPATH) notrandombytes.obj $(SCHEME_DIR)\lib$(SCHEME)_$(IMPLEMENTATION).lib
$(DEST_DIR)\printparams_$(SCHEME)_$(IMPLEMENTATION).exe: crypto_$(TYPE)\printparams.c $(SCHEME_DIR)\api.h
-MKDIR $(DEST_DIR)

View File

@ -161,7 +161,7 @@ static int test_invalid_sk_a(void) {
if (!memcmp(key_a, key_b, CRYPTO_BYTES)) {
printf("ERROR invalid sk_a\n");
return 1;
return -1;
}
}
@ -199,7 +199,7 @@ static int test_invalid_ciphertext(void) {
if (!memcmp(key_a, key_b, CRYPTO_BYTES)) {
printf("ERROR invalid ciphertext\n");
return 1;
return -1;
}
}

View File

@ -0,0 +1,9 @@
source:
scheme: frodokem640shake
implementation: clean
files:
- common.h
- kem.c
- matrix_shake.c
- noise.c
- util.c

View File

@ -0,0 +1,9 @@
source:
scheme: frodokem640shake
implementation: clean
files:
- common.h
- kem.c
- matrix_shake.c
- noise.c
- util.c

View File

@ -71,9 +71,17 @@ class Implementation:
self.scheme = scheme
self.name = name
def metadata(self):
for i in self.scheme.metadata()['implementations']:
if i['name'] == self.name:
return i
def path(self, base='..') -> str:
return os.path.join(self.scheme.path(), self.name)
def namespace_prefix(self):
return 'PQCLEAN_{}_{}_'.format(self.scheme.name.upper(), self.name.upper()).replace('-', '')
def libname(self) -> str:
if os.name == 'nt':
return "lib{}_{}.lib".format(self.scheme.name, self.name)

View File

@ -11,6 +11,10 @@ import helpers
def test_char():
if not(os.path.exists(os.path.join('pycparser', '.git'))):
helpers.run_subprocess(
['git', 'submodule', 'update', '--init']
)
for scheme in pqclean.Scheme.all_schemes():
for implementation in scheme.implementations:
yield check_char, implementation

View File

@ -0,0 +1,42 @@
"""
Checks that files duplicated across schemes/implementations are consistent.
"""
import os
import pqclean
import helpers
import unittest
import yaml
def test_duplicate_consistency():
for scheme in pqclean.Scheme.all_schemes():
for implementation in scheme.implementations:
if os.path.isfile(os.path.join('duplicate_consistency', '{}_{}.yml'.format(scheme.name, implementation.name))):
yield check_duplicate_consistency, implementation
def file_get_contents(filename):
with open(filename) as f:
return f.read()
def check_duplicate_consistency(implementation):
helpers.skip_windows()
metafile = os.path.join('duplicate_consistency', '{}_{}.yml'.format(implementation.scheme.name, implementation.name))
with open(metafile, encoding='utf-8') as f:
metadata = yaml.load(f.read())
source = pqclean.Implementation.by_name(metadata['source']['scheme'], metadata['source']['implementation'])
for file in metadata['files']:
transformed_src = helpers.run_subprocess(
['sed', '-e', 's/{}/{}/g'.format(source.namespace_prefix(), implementation.namespace_prefix()), os.path.join(source.path(), file)]
)
this_src = file_get_contents(os.path.join(implementation.path(), file))
print(os.path.join(implementation.path(), file))
print(this_src)
assert(transformed_src == this_src)
if __name__ == '__main__':
try:
import nose2
nose2.main()
except ImportError:
import nose
nose.runmodule()

View File

@ -72,10 +72,11 @@ SIGNATURE_FIELDS = {
def check_spec(metadata, spec):
for field, props in spec:
if field not in metadata:
if field not in metadata and 'optional' not in props:
raise AssertionError("Field '{}' not present.".format(field))
# validate element
if field in metadata:
check_element(field, metadata[field], props)
# delete it to detect extras