Add FrodoKEM-976-AES

This commit is contained in:
Douglas Stebila 2019-04-10 12:11:27 -04:00
bovenliggende 395f14a2f6
commit 999ea3afed
12 gewijzigde bestanden met toevoegingen van 754 en 0 verwijderingen

Bestand weergeven

@ -0,0 +1,24 @@
name: FrodoKEM-976-AES
type: kem
claimed-nist-level: 1
length-public-key: 15632
length-ciphertext: 15744
length-shared-secret: 24
testvectors-sha256: 30a2a3f2d834b5d90cd10241f53c4a4379abeea0dbd4eb65b260749b2ba81391
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

Bestand weergeven

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

Bestand weergeven

@ -0,0 +1,19 @@
# This Makefile can be used with GNU Make or BSD Make
LIB=libfrodokem976aes_clean.a
HEADERS=api.h params.h common.h
OBJECTS=kem.o matrix_aes.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)

Bestand weergeven

@ -0,0 +1,19 @@
# This Makefile can be used with Microsoft Visual Studio's nmake using the command:
# nmake /f Makefile.Microsoft_nmake
LIBRARY=libfrodokem976aes_clean.lib
OBJECTS=kem.obj matrix_aes.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)

Bestand weergeven

@ -0,0 +1,20 @@
#ifndef PQCLEAN_FRODOKEM976AES_CLEAN_API_H
#define PQCLEAN_FRODOKEM976AES_CLEAN_API_H
#include <stddef.h>
#include <stdint.h>
#define PQCLEAN_FRODOKEM976AES_CLEAN_CRYPTO_SECRETKEYBYTES 31296 // sizeof(s) + CRYPTO_PUBLICKEYBYTES + 2*PARAMS_N*PARAMS_NBAR + BYTES_PKHASH
#define PQCLEAN_FRODOKEM976AES_CLEAN_CRYPTO_PUBLICKEYBYTES 15632 // sizeof(seed_A) + (PARAMS_LOGQ*PARAMS_N*PARAMS_NBAR)/8
#define PQCLEAN_FRODOKEM976AES_CLEAN_CRYPTO_BYTES 24
#define PQCLEAN_FRODOKEM976AES_CLEAN_CRYPTO_CIPHERTEXTBYTES 15744 // (PARAMS_LOGQ*PARAMS_N*PARAMS_NBAR)/8 + (PARAMS_LOGQ*PARAMS_NBAR*PARAMS_NBAR)/8
#define PQCLEAN_FRODOKEM976AES_CLEAN_CRYPTO_ALGNAME "FrodoKEM-976-AES"
int PQCLEAN_FRODOKEM976AES_CLEAN_crypto_kem_keypair(uint8_t *pk, uint8_t *sk);
int PQCLEAN_FRODOKEM976AES_CLEAN_crypto_kem_enc(uint8_t *ct, uint8_t *ss, const uint8_t *pk);
int PQCLEAN_FRODOKEM976AES_CLEAN_crypto_kem_dec(uint8_t *ss, const uint8_t *ct, const uint8_t *sk);
#endif

Bestand weergeven

@ -0,0 +1,19 @@
#ifndef COMMON_H
#define COMMON_H
int PQCLEAN_FRODOKEM976AES_CLEAN_mul_add_as_plus_e(uint16_t *out, const uint16_t *s, const uint16_t *e, const uint8_t *seed_A);
int PQCLEAN_FRODOKEM976AES_CLEAN_mul_add_sa_plus_e(uint16_t *out, const uint16_t *s, const uint16_t *e, const uint8_t *seed_A);
void PQCLEAN_FRODOKEM976AES_CLEAN_sample_n(uint16_t *s, size_t n);
void PQCLEAN_FRODOKEM976AES_CLEAN_mul_bs(uint16_t *out, const uint16_t *b, const uint16_t *s);
void PQCLEAN_FRODOKEM976AES_CLEAN_mul_add_sb_plus_e(uint16_t *out, const uint16_t *b, const uint16_t *s, const uint16_t *e);
void PQCLEAN_FRODOKEM976AES_CLEAN_add(uint16_t *out, const uint16_t *a, const uint16_t *b);
void PQCLEAN_FRODOKEM976AES_CLEAN_sub(uint16_t *out, const uint16_t *a, const uint16_t *b);
void PQCLEAN_FRODOKEM976AES_CLEAN_key_encode(uint16_t *out, const uint16_t *in);
void PQCLEAN_FRODOKEM976AES_CLEAN_key_decode(uint16_t *out, const uint16_t *in);
void PQCLEAN_FRODOKEM976AES_CLEAN_pack(uint8_t *out, size_t outlen, const uint16_t *in, size_t inlen, uint8_t lsb);
void PQCLEAN_FRODOKEM976AES_CLEAN_unpack(uint16_t *out, size_t outlen, const uint8_t *in, size_t inlen, uint8_t lsb);
void PQCLEAN_FRODOKEM976AES_CLEAN_clear_bytes(uint8_t *mem, size_t n);
uint16_t PQCLEAN_FRODOKEM976AES_CLEAN_LE_TO_UINT16(uint16_t n);
uint16_t PQCLEAN_FRODOKEM976AES_CLEAN_UINT16_TO_LE(uint16_t n);
#endif

Bestand weergeven

@ -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_FRODOKEM976AES_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_FRODOKEM976AES_CLEAN_LE_TO_UINT16(S[i]);
}
PQCLEAN_FRODOKEM976AES_CLEAN_sample_n(S, PARAMS_N * PARAMS_NBAR);
PQCLEAN_FRODOKEM976AES_CLEAN_sample_n(E, PARAMS_N * PARAMS_NBAR);
PQCLEAN_FRODOKEM976AES_CLEAN_mul_add_as_plus_e(B, S, E, pk);
// Encode the second part of the public key
PQCLEAN_FRODOKEM976AES_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_FRODOKEM976AES_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_FRODOKEM976AES_CLEAN_clear_bytes((uint8_t *)S, PARAMS_N * PARAMS_NBAR * sizeof(uint16_t));
PQCLEAN_FRODOKEM976AES_CLEAN_clear_bytes((uint8_t *)E, PARAMS_N * PARAMS_NBAR * sizeof(uint16_t));
PQCLEAN_FRODOKEM976AES_CLEAN_clear_bytes(randomness, 2 * CRYPTO_BYTES);
PQCLEAN_FRODOKEM976AES_CLEAN_clear_bytes(shake_input_seedSE, 1 + CRYPTO_BYTES);
return 0;
}
int PQCLEAN_FRODOKEM976AES_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_FRODOKEM976AES_CLEAN_LE_TO_UINT16(Sp[i]);
}
PQCLEAN_FRODOKEM976AES_CLEAN_sample_n(Sp, PARAMS_N * PARAMS_NBAR);
PQCLEAN_FRODOKEM976AES_CLEAN_sample_n(Ep, PARAMS_N * PARAMS_NBAR);
PQCLEAN_FRODOKEM976AES_CLEAN_mul_add_sa_plus_e(Bp, Sp, Ep, pk_seedA);
PQCLEAN_FRODOKEM976AES_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_FRODOKEM976AES_CLEAN_sample_n(Epp, PARAMS_NBAR * PARAMS_NBAR);
PQCLEAN_FRODOKEM976AES_CLEAN_unpack(B, PARAMS_N * PARAMS_NBAR, pk_b, CRYPTO_PUBLICKEYBYTES - BYTES_SEED_A, PARAMS_LOGQ);
PQCLEAN_FRODOKEM976AES_CLEAN_mul_add_sb_plus_e(V, B, Sp, Epp);
// Encode mu, and compute C = V + enc(mu) (mod q)
PQCLEAN_FRODOKEM976AES_CLEAN_key_encode(C, (uint16_t *)mu);
PQCLEAN_FRODOKEM976AES_CLEAN_add(C, V, C);
PQCLEAN_FRODOKEM976AES_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_FRODOKEM976AES_CLEAN_clear_bytes((uint8_t *)V, PARAMS_NBAR * PARAMS_NBAR * sizeof(uint16_t));
PQCLEAN_FRODOKEM976AES_CLEAN_clear_bytes((uint8_t *)Sp, PARAMS_N * PARAMS_NBAR * sizeof(uint16_t));
PQCLEAN_FRODOKEM976AES_CLEAN_clear_bytes((uint8_t *)Ep, PARAMS_N * PARAMS_NBAR * sizeof(uint16_t));
PQCLEAN_FRODOKEM976AES_CLEAN_clear_bytes((uint8_t *)Epp, PARAMS_NBAR * PARAMS_NBAR * sizeof(uint16_t));
PQCLEAN_FRODOKEM976AES_CLEAN_clear_bytes(mu, BYTES_MU);
PQCLEAN_FRODOKEM976AES_CLEAN_clear_bytes(G2out, 2 * CRYPTO_BYTES);
PQCLEAN_FRODOKEM976AES_CLEAN_clear_bytes(Fin_k, CRYPTO_BYTES);
PQCLEAN_FRODOKEM976AES_CLEAN_clear_bytes(shake_input_seedSE, 1 + CRYPTO_BYTES);
return 0;
}
int PQCLEAN_FRODOKEM976AES_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_FRODOKEM976AES_CLEAN_LE_TO_UINT16(sk_S[i]);
}
// Compute W = C - Bp*S (mod q), and decode the randomness mu
PQCLEAN_FRODOKEM976AES_CLEAN_unpack(Bp, PARAMS_N * PARAMS_NBAR, ct_c1, (PARAMS_LOGQ * PARAMS_N * PARAMS_NBAR) / 8, PARAMS_LOGQ);
PQCLEAN_FRODOKEM976AES_CLEAN_unpack(C, PARAMS_NBAR * PARAMS_NBAR, ct_c2, (PARAMS_LOGQ * PARAMS_NBAR * PARAMS_NBAR) / 8, PARAMS_LOGQ);
PQCLEAN_FRODOKEM976AES_CLEAN_mul_bs(W, Bp, S);
PQCLEAN_FRODOKEM976AES_CLEAN_sub(W, C, W);
PQCLEAN_FRODOKEM976AES_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_FRODOKEM976AES_CLEAN_LE_TO_UINT16(Sp[i]);
}
PQCLEAN_FRODOKEM976AES_CLEAN_sample_n(Sp, PARAMS_N * PARAMS_NBAR);
PQCLEAN_FRODOKEM976AES_CLEAN_sample_n(Ep, PARAMS_N * PARAMS_NBAR);
PQCLEAN_FRODOKEM976AES_CLEAN_mul_add_sa_plus_e(BBp, Sp, Ep, pk_seedA);
// Generate Epp, and compute W = Sp*B + Epp
PQCLEAN_FRODOKEM976AES_CLEAN_sample_n(Epp, PARAMS_NBAR * PARAMS_NBAR);
PQCLEAN_FRODOKEM976AES_CLEAN_unpack(B, PARAMS_N * PARAMS_NBAR, pk_b, CRYPTO_PUBLICKEYBYTES - BYTES_SEED_A, PARAMS_LOGQ);
PQCLEAN_FRODOKEM976AES_CLEAN_mul_add_sb_plus_e(W, B, Sp, Epp);
// Encode mu, and compute CC = W + enc(mu') (mod q)
PQCLEAN_FRODOKEM976AES_CLEAN_key_encode(CC, (uint16_t *)muprime);
PQCLEAN_FRODOKEM976AES_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_FRODOKEM976AES_CLEAN_clear_bytes((uint8_t *)W, PARAMS_NBAR * PARAMS_NBAR * sizeof(uint16_t));
PQCLEAN_FRODOKEM976AES_CLEAN_clear_bytes((uint8_t *)Sp, PARAMS_N * PARAMS_NBAR * sizeof(uint16_t));
PQCLEAN_FRODOKEM976AES_CLEAN_clear_bytes((uint8_t *)S, PARAMS_N * PARAMS_NBAR * sizeof(uint16_t));
PQCLEAN_FRODOKEM976AES_CLEAN_clear_bytes((uint8_t *)Ep, PARAMS_N * PARAMS_NBAR * sizeof(uint16_t));
PQCLEAN_FRODOKEM976AES_CLEAN_clear_bytes((uint8_t *)Epp, PARAMS_NBAR * PARAMS_NBAR * sizeof(uint16_t));
PQCLEAN_FRODOKEM976AES_CLEAN_clear_bytes(muprime, BYTES_MU);
PQCLEAN_FRODOKEM976AES_CLEAN_clear_bytes(G2out, 2 * CRYPTO_BYTES);
PQCLEAN_FRODOKEM976AES_CLEAN_clear_bytes(Fin_k, CRYPTO_BYTES);
PQCLEAN_FRODOKEM976AES_CLEAN_clear_bytes(shake_input_seedSEprime, 1 + CRYPTO_BYTES);
return 0;
}

Bestand weergeven

@ -0,0 +1,85 @@
/********************************************************************************************
* FrodoKEM: Learning with Errors Key Encapsulation
*
* Abstract: matrix arithmetic functions used by the KEM
*********************************************************************************************/
#include <stdint.h>
#include <string.h>
#include "aes.h"
#include "api.h"
#include "common.h"
#include "params.h"
int PQCLEAN_FRODOKEM976AES_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};
aes128ctx ctx128;
aes128_keyexp(&ctx128, seed_A);
for (i = 0; i < PARAMS_N; i++) {
for (j = 0; j < PARAMS_N; j += PARAMS_STRIPE_STEP) {
A[i*PARAMS_N + j] = i; // Loading values in the little-endian order
A[i*PARAMS_N + j + 1] = j;
}
}
aes128_ecb((uint8_t *) A, (uint8_t *) A, PARAMS_N * PARAMS_N * sizeof(int16_t) / AES_BLOCKBYTES, &ctx128);
for (i = 0; i < PARAMS_N * PARAMS_N; i++) {
A[i] = PQCLEAN_FRODOKEM976AES_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_FRODOKEM976AES_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};
aes128ctx ctx128;
aes128_keyexp(&ctx128, seed_A);
for (i = 0; i < PARAMS_N; i++) {
for (j = 0; j < PARAMS_N; j += PARAMS_STRIPE_STEP) {
A[i*PARAMS_N + j] = i; // Loading values in the little-endian order
A[i*PARAMS_N + j + 1] = j;
}
}
aes128_ecb((uint8_t *) A, (uint8_t *) A, PARAMS_N * PARAMS_N * sizeof(int16_t) / AES_BLOCKBYTES, &ctx128);
for (i = 0; i < PARAMS_N * PARAMS_N; i++) {
A[i] = PQCLEAN_FRODOKEM976AES_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;
}

Bestand weergeven

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

Bestand weergeven

@ -0,0 +1,27 @@
#ifndef PARAMS_H
#define PARAMS_H
#define CRYPTO_SECRETKEYBYTES PQCLEAN_FRODOKEM976AES_CLEAN_CRYPTO_SECRETKEYBYTES
#define CRYPTO_PUBLICKEYBYTES PQCLEAN_FRODOKEM976AES_CLEAN_CRYPTO_PUBLICKEYBYTES
#define CRYPTO_BYTES PQCLEAN_FRODOKEM976AES_CLEAN_CRYPTO_BYTES
#define CRYPTO_CIPHERTEXTBYTES PQCLEAN_FRODOKEM976AES_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

Bestand weergeven

@ -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_FRODOKEM976AES_CLEAN_LE_TO_UINT16(const uint16_t n) {
return (((uint8_t *) &(n))[0] | (((uint8_t *) &(n))[1] << 8));
}
uint16_t PQCLEAN_FRODOKEM976AES_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_FRODOKEM976AES_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_FRODOKEM976AES_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_FRODOKEM976AES_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_FRODOKEM976AES_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_FRODOKEM976AES_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_FRODOKEM976AES_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_FRODOKEM976AES_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_FRODOKEM976AES_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_FRODOKEM976AES_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;
}
}

Bestand weergeven

@ -0,0 +1,15 @@
consistency_checks:
- source:
scheme: frodokem640aes
implementation: clean
files:
- common.h
- kem.c
- matrix_aes.c
- noise.c
- util.c
- source:
scheme: frodokem976shake
implementation: clean
files:
- params.h