@@ -3,11 +3,9 @@ | |||
<!-- Type some lines about your submission --> | |||
## Manually checked properties | |||
#### Manually checked properties | |||
<!-- These checkboxes serve for the maintainers of PQClean to verify your submission. Please do not check them yourself. --> | |||
* [ ] `#ifdef`s only for header encapsulation | |||
* [ ] `api.h` does not include other files | |||
* [ ] No stringification macros | |||
* [ ] Output-parameter pointers in functions are on the left | |||
* [ ] Negative return values on failure of API functions (within restrictions of FO transform). | |||
@@ -34,7 +34,9 @@ _The checking of items on this list is still being developed. Checked items shou | |||
* [x] Code is valid C99 | |||
* [x] Passes functional tests | |||
* [x] API functions do not write outside provided buffers | |||
* [x] `api.h` cannot include external files | |||
* [x] Compiles with `-Wall -Wextra -Wpedantic -Werror` with `gcc` and `clang` | |||
* [x] `#if`/`#ifdef`s only for header encapsulation | |||
* [x] Consistent test vectors across runs | |||
* [x] Consistent test vectors on big-endian and little-endian machines | |||
* [x] Consistent test vectors on 32-bit and 64-bit machines | |||
@@ -65,7 +67,6 @@ _The checking of items on this list is still being developed. Checked items shou | |||
## Requirements on C implementations that are manually checked | |||
* Minimalist Makefiles | |||
* `#ifdef`s only for header encapsulation | |||
* No stringification macros | |||
* Output-parameter pointers in functions are on the left | |||
* `const` arguments are labeled as `const` | |||
@@ -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 |
@@ -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 |
@@ -1,8 +1,8 @@ | |||
# This Makefile can be used with GNU Make or BSD Make | |||
LIB=libdilithium-iii_clean.a | |||
HEADERS=api.h ntt.h packing.h params.h poly.h polyvec.h reduce.h rounding.h sign.h | |||
OBJECTS=ntt.o packing.o poly.o polyvec.o reduce.o rounding.o sign.o | |||
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) | |||
@@ -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) |
@@ -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 |
@@ -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 |
@@ -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; | |||
} |
@@ -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; | |||
} |
@@ -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; | |||
} | |||
} |
@@ -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 |
@@ -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; | |||
} | |||
} |
@@ -2,23 +2,23 @@ name: FrodoKEM-640-SHAKE | |||
type: kem | |||
claimed-nist-level: 1 | |||
length-public-key: 9616 | |||
length-secret-key: 19888 | |||
length-ciphertext: 9720 | |||
length-shared-secret: 16 | |||
testvectors-sha256: 521ff891de20efe74e6584d09612dae989427ac76261a41630c4e4d6a4fc78a4 | |||
testvectors-sha256: 8f922de02d41005fcc3c4164b2ab74c4c7b588ed69e34e22607d1ae4ab13d2c5 | |||
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 | |||
- 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 | |||
- name: clean | |||
version: https://github.com/Microsoft/PQCrypto-LWEKE/commit/437e228fca580a82435cab09f30ae14b03183119 | |||
length-secret-key: 19888 |
@@ -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 |
@@ -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 |
@@ -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) |
@@ -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) |
@@ -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 |
@@ -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 |
@@ -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; | |||
} |
@@ -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; | |||
} |
@@ -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; | |||
} | |||
} |
@@ -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 |
@@ -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; | |||
} | |||
} |
@@ -2,10 +2,9 @@ name: Kyber768 | |||
type: kem | |||
claimed-nist-level: 3 | |||
length-public-key: 1088 | |||
length-secret-key: 2400 | |||
length-ciphertext: 1152 | |||
length-shared-secret: 32 | |||
testvectors-sha256: 0e002ee528febdab1709f100df79ceb00b31a809e03a4fb84e3a72c39235d372 | |||
testvectors-sha256: 2f5cf9937959eb4a3bc910f71e830e9e0de029b28093c6192d2c3e915913016f | |||
principal-submitter: Peter Schwabe | |||
auxiliary-submitters: | |||
- Roberto Avanzi | |||
@@ -20,3 +19,4 @@ auxiliary-submitters: | |||
implementations: | |||
- name: clean | |||
version: https://github.com/pq-crystals/kyber/commit/ab996e7460e5356b0e23aa034e7c2fe6922e60e6 | |||
length-secret-key: 2400 |
@@ -3,12 +3,10 @@ | |||
#include <stdint.h> | |||
#include "params.h" | |||
#define PQCLEAN_KYBER768_CLEAN_CRYPTO_SECRETKEYBYTES KYBER_SECRETKEYBYTES | |||
#define PQCLEAN_KYBER768_CLEAN_CRYPTO_PUBLICKEYBYTES KYBER_PUBLICKEYBYTES | |||
#define PQCLEAN_KYBER768_CLEAN_CRYPTO_CIPHERTEXTBYTES KYBER_CIPHERTEXTBYTES | |||
#define PQCLEAN_KYBER768_CLEAN_CRYPTO_BYTES KYBER_SYMBYTES | |||
#define PQCLEAN_KYBER768_CLEAN_CRYPTO_SECRETKEYBYTES 2400 | |||
#define PQCLEAN_KYBER768_CLEAN_CRYPTO_PUBLICKEYBYTES 1088 | |||
#define PQCLEAN_KYBER768_CLEAN_CRYPTO_CIPHERTEXTBYTES 1152 | |||
#define PQCLEAN_KYBER768_CLEAN_CRYPTO_BYTES 32 | |||
#define PQCLEAN_KYBER768_CLEAN_CRYPTO_ALGNAME "Kyber768" | |||
@@ -32,32 +32,6 @@ static uint64_t load_littleendian(const unsigned char *x, int bytes) { | |||
* - const unsigned char *buf: pointer to input byte array | |||
**************************************************/ | |||
void PQCLEAN_KYBER768_CLEAN_cbd(poly *r, const unsigned char *buf) { | |||
#if KYBER_ETA == 3 | |||
uint32_t t, d, a[4], b[4]; | |||
int i, j; | |||
for (i = 0; i < KYBER_N / 4; i++) { | |||
t = (uint32_t)load_littleendian(buf + 3 * i, 3); | |||
d = 0; | |||
for (j = 0; j < 3; j++) { | |||
d += (t >> j) & 0x249249; | |||
} | |||
a[0] = d & 0x7; | |||
b[0] = (d >> 3) & 0x7; | |||
a[1] = (d >> 6) & 0x7; | |||
b[1] = (d >> 9) & 0x7; | |||
a[2] = (d >> 12) & 0x7; | |||
b[2] = (d >> 15) & 0x7; | |||
a[3] = (d >> 18) & 0x7; | |||
b[3] = (d >> 21); | |||
r->coeffs[4 * i + 0] = (uint16_t)(a[0] + KYBER_Q - b[0]); | |||
r->coeffs[4 * i + 1] = (uint16_t)(a[1] + KYBER_Q - b[1]); | |||
r->coeffs[4 * i + 2] = (uint16_t)(a[2] + KYBER_Q - b[2]); | |||
r->coeffs[4 * i + 3] = (uint16_t)(a[3] + KYBER_Q - b[3]); | |||
} | |||
#elif KYBER_ETA == 4 | |||
uint32_t t, d, a[4], b[4]; | |||
int i, j; | |||
@@ -82,32 +56,4 @@ void PQCLEAN_KYBER768_CLEAN_cbd(poly *r, const unsigned char *buf) { | |||
r->coeffs[4 * i + 2] = (uint16_t)(a[2] + KYBER_Q - b[2]); | |||
r->coeffs[4 * i + 3] = (uint16_t)(a[3] + KYBER_Q - b[3]); | |||
} | |||
#elif KYBER_ETA == 5 | |||
uint64_t t, d, a[4], b[4]; | |||
int i, j; | |||
for (i = 0; i < KYBER_N / 4; i++) { | |||
t = load_littleendian(buf + 5 * i, 5); | |||
d = 0; | |||
for (j = 0; j < 5; j++) { | |||
d += (t >> j) & 0x0842108421UL; | |||
} | |||
a[0] = d & 0x1f; | |||
b[0] = (d >> 5) & 0x1f; | |||
a[1] = (d >> 10) & 0x1f; | |||
b[1] = (d >> 15) & 0x1f; | |||
a[2] = (d >> 20) & 0x1f; | |||
b[2] = (d >> 25) & 0x1f; | |||
a[3] = (d >> 30) & 0x1f; | |||
b[3] = (d >> 35); | |||
r->coeffs[4 * i + 0] = (uint16_t)(a[0] + KYBER_Q - b[0]); | |||
r->coeffs[4 * i + 1] = (uint16_t)(a[1] + KYBER_Q - b[1]); | |||
r->coeffs[4 * i + 2] = (uint16_t)(a[2] + KYBER_Q - b[2]); | |||
r->coeffs[4 * i + 3] = (uint16_t)(a[3] + KYBER_Q - b[3]); | |||
} | |||
#else | |||
#error "poly_getnoise in poly.c only supports eta in {3,4,5}" | |||
#endif | |||
} |
@@ -1,18 +0,0 @@ | |||
name: Dilithium-III | |||
type: signature | |||
claimed-nist-level: 3 | |||
length-public-key: 1472 | |||
length-secret-key: 3504 | |||
length-signature: 2701 | |||
testvectors-sha256: e1852a975842c44a683c914ed131d95bee9b786c36c41e47bb77d7dd3c0c07be | |||
principal-submitter: Vadim Lyubashevsky | |||
auxiliary-submitters: | |||
- Léo Ducas | |||
- Eike Kiltz | |||
- Tancrède Lepoint | |||
- Peter Schwabe | |||
- Gregor Seiler | |||
- Damien Stehlé | |||
implementations: | |||
- name: clean | |||
version: https://github.com/pq-crystals/dilithium/commit/ffa89bdbc12a8ee178ccec4890aeea5449ef937a |
@@ -1 +0,0 @@ | |||
Public Domain |
@@ -1,30 +0,0 @@ | |||
#ifndef PQCLEAN_DILITHIUMIII_CLEAN_API_H | |||
#define PQCLEAN_DILITHIUMIII_CLEAN_API_H | |||
#include <stddef.h> | |||
#include <stdint.h> | |||
#define MODE 2 | |||
#define PQCLEAN_DILITHIUMIII_CLEAN_CRYPTO_PUBLICKEYBYTES 1472U | |||
#define PQCLEAN_DILITHIUMIII_CLEAN_CRYPTO_SECRETKEYBYTES 3504U | |||
#define PQCLEAN_DILITHIUMIII_CLEAN_CRYPTO_BYTES 2701U | |||
#define PQCLEAN_DILITHIUMIII_CLEAN_CRYPTO_ALGNAME "Dilithium-III" | |||
int PQCLEAN_DILITHIUMIII_CLEAN_crypto_sign_keypair(uint8_t *pk, | |||
uint8_t *sk); | |||
int PQCLEAN_DILITHIUMIII_CLEAN_crypto_sign(uint8_t *sm, | |||
size_t *smlen, | |||
const uint8_t *msg, | |||
size_t len, | |||
const uint8_t *sk); | |||
int PQCLEAN_DILITHIUMIII_CLEAN_crypto_sign_open(uint8_t *m, | |||
size_t *mlen, | |||
const uint8_t *sm, | |||
size_t smlen, | |||
const uint8_t *pk); | |||
#endif |
@@ -1,139 +0,0 @@ | |||
#include "ntt.h" | |||
#include "params.h" | |||
#include "poly.h" | |||
#include "reduce.h" | |||
/* Roots of unity in order needed by forward ntt */ | |||
static const uint32_t PQCLEAN_DILITHIUMIII_CLEAN_zetas[N] = { | |||
0, 25847, 5771523, 7861508, 237124, 7602457, 7504169, 466468, | |||
1826347, 2353451, 8021166, 6288512, 3119733, 5495562, 3111497, 2680103, | |||
2725464, 1024112, 7300517, 3585928, 7830929, 7260833, 2619752, 6271868, | |||
6262231, 4520680, 6980856, 5102745, 1757237, 8360995, 4010497, 280005, | |||
2706023, 95776, 3077325, 3530437, 6718724, 4788269, 5842901, 3915439, | |||
4519302, 5336701, 3574422, 5512770, 3539968, 8079950, 2348700, 7841118, | |||
6681150, 6736599, 3505694, 4558682, 3507263, 6239768, 6779997, 3699596, | |||
811944, 531354, 954230, 3881043, 3900724, 5823537, 2071892, 5582638, | |||
4450022, 6851714, 4702672, 5339162, 6927966, 3475950, 2176455, 6795196, | |||
7122806, 1939314, 4296819, 7380215, 5190273, 5223087, 4747489, 126922, | |||
3412210, 7396998, 2147896, 2715295, 5412772, 4686924, 7969390, 5903370, | |||
7709315, 7151892, 8357436, 7072248, 7998430, 1349076, 1852771, 6949987, | |||
5037034, 264944, 508951, 3097992, 44288, 7280319, 904516, 3958618, | |||
4656075, 8371839, 1653064, 5130689, 2389356, 8169440, 759969, 7063561, | |||
189548, 4827145, 3159746, 6529015, 5971092, 8202977, 1315589, 1341330, | |||
1285669, 6795489, 7567685, 6940675, 5361315, 4499357, 4751448, 3839961, | |||
2091667, 3407706, 2316500, 3817976, 5037939, 2244091, 5933984, 4817955, | |||
266997, 2434439, 7144689, 3513181, 4860065, 4621053, 7183191, 5187039, | |||
900702, 1859098, 909542, 819034, 495491, 6767243, 8337157, 7857917, | |||
7725090, 5257975, 2031748, 3207046, 4823422, 7855319, 7611795, 4784579, | |||
342297, 286988, 5942594, 4108315, 3437287, 5038140, 1735879, 203044, | |||
2842341, 2691481, 5790267, 1265009, 4055324, 1247620, 2486353, 1595974, | |||
4613401, 1250494, 2635921, 4832145, 5386378, 1869119, 1903435, 7329447, | |||
7047359, 1237275, 5062207, 6950192, 7929317, 1312455, 3306115, 6417775, | |||
7100756, 1917081, 5834105, 7005614, 1500165, 777191, 2235880, 3406031, | |||
7838005, 5548557, 6709241, 6533464, 5796124, 4656147, 594136, 4603424, | |||
6366809, 2432395, 2454455, 8215696, 1957272, 3369112, 185531, 7173032, | |||
5196991, 162844, 1616392, 3014001, 810149, 1652634, 4686184, 6581310, | |||
5341501, 3523897, 3866901, 269760, 2213111, 7404533, 1717735, 472078, | |||
7953734, 1723600, 6577327, 1910376, 6712985, 7276084, 8119771, 4546524, | |||
5441381, 6144432, 7959518, 6094090, 183443, 7403526, 1612842, 4834730, | |||
7826001, 3919660, 8332111, 7018208, 3937738, 1400424, 7534263, 1976782 | |||
}; | |||
/* Roots of unity in order needed by inverse ntt */ | |||
static const uint32_t PQCLEAN_DILITHIUMIII_CLEAN_zetas_inv[N] = { | |||
6403635, 846154, 6979993, 4442679, 1362209, 48306, 4460757, 554416, | |||
3545687, 6767575, 976891, 8196974, 2286327, 420899, 2235985, 2939036, | |||
3833893, 260646, 1104333, 1667432, 6470041, 1803090, 6656817, 426683, | |||
7908339, 6662682, 975884, 6167306, 8110657, 4513516, 4856520, 3038916, | |||
1799107, 3694233, 6727783, 7570268, 5366416, 6764025, 8217573, 3183426, | |||
1207385, 8194886, 5011305, 6423145, 164721, 5925962, 5948022, 2013608, | |||
3776993, 7786281, 3724270, 2584293, 1846953, 1671176, 2831860, 542412, | |||
4974386, 6144537, 7603226, 6880252, 1374803, 2546312, 6463336, 1279661, | |||
1962642, 5074302, 7067962, 451100, 1430225, 3318210, 7143142, 1333058, | |||
1050970, 6476982, 6511298, 2994039, 3548272, 5744496, 7129923, 3767016, | |||
6784443, 5894064, 7132797, 4325093, 7115408, 2590150, 5688936, 5538076, | |||
8177373, 6644538, 3342277, 4943130, 4272102, 2437823, 8093429, 8038120, | |||
3595838, 768622, 525098, 3556995, 5173371, 6348669, 3122442, 655327, | |||
522500, 43260, 1613174, 7884926, 7561383, 7470875, 6521319, 7479715, | |||
3193378, 1197226, 3759364, 3520352, 4867236, 1235728, 5945978, 8113420, | |||
3562462, 2446433, 6136326, 3342478, 4562441, 6063917, 4972711, 6288750, | |||
4540456, 3628969, 3881060, 3019102, 1439742, 812732, 1584928, 7094748, | |||
7039087, 7064828, 177440, 2409325, 1851402, 5220671, 3553272, 8190869, | |||
1316856, 7620448, 210977, 5991061, 3249728, 6727353, 8578, 3724342, | |||
4421799, 7475901, 1100098, 8336129, 5282425, 7871466, 8115473, 3343383, | |||
1430430, 6527646, 7031341, 381987, 1308169, 22981, 1228525, 671102, | |||
2477047, 411027, 3693493, 2967645, 5665122, 6232521, 983419, 4968207, | |||
8253495, 3632928, 3157330, 3190144, 1000202, 4083598, 6441103, 1257611, | |||
1585221, 6203962, 4904467, 1452451, 3041255, 3677745, 1528703, 3930395, | |||
2797779, 6308525, 2556880, 4479693, 4499374, 7426187, 7849063, 7568473, | |||
4680821, 1600420, 2140649, 4873154, 3821735, 4874723, 1643818, 1699267, | |||
539299, 6031717, 300467, 4840449, 2867647, 4805995, 3043716, 3861115, | |||
4464978, 2537516, 3592148, 1661693, 4849980, 5303092, 8284641, 5674394, | |||
8100412, 4369920, 19422, 6623180, 3277672, 1399561, 3859737, 2118186, | |||
2108549, 5760665, 1119584, 549488, 4794489, 1079900, 7356305, 5654953, | |||
5700314, 5268920, 2884855, 5260684, 2091905, 359251, 6026966, 6554070, | |||
7913949, 876248, 777960, 8143293, 518909, 2608894, 8354570 | |||
}; | |||
/************************************************* | |||
* Name: ntt | |||
* | |||
* Description: Forward NTT, in-place. No modular reduction is performed after | |||
* additions or subtractions. Hence output coefficients can be up | |||
* to 16*Q larger than the coefficients of the input polynomial. | |||
* Output vector is in bitreversed order. | |||
* | |||
* Arguments: - uint32_t p[N]: input/output coefficient array | |||
**************************************************/ | |||
void PQCLEAN_DILITHIUMIII_CLEAN_ntt(uint32_t p[N]) { | |||
unsigned int len, start, j, k; | |||
uint32_t zeta, t; | |||
k = 1; | |||
for (len = 128; len > 0; len >>= 1) { | |||
for (start = 0; start < N; start = j + len) { | |||
zeta = PQCLEAN_DILITHIUMIII_CLEAN_zetas[k++]; | |||
for (j = start; j < start + len; ++j) { | |||
t = PQCLEAN_DILITHIUMIII_CLEAN_montgomery_reduce( | |||
(uint64_t)zeta * p[j + len]); | |||
p[j + len] = p[j] + 2 * Q - t; | |||
p[j] = p[j] + t; | |||
} | |||
} | |||
} | |||
} | |||
/************************************************* | |||
* Name: invntt_frominvmont | |||
* | |||
* Description: Inverse NTT and multiplication by Montgomery factor 2^32. | |||
* In-place. No modular reductions after additions or | |||
* subtractions. Input coefficient need to be smaller than 2*Q. | |||
* Output coefficient are smaller than 2*Q. | |||
* | |||
* Arguments: - uint32_t p[N]: input/output coefficient array | |||
**************************************************/ | |||
void PQCLEAN_DILITHIUMIII_CLEAN_invntt_frominvmont(uint32_t p[N]) { | |||
unsigned int start, len, j, k; | |||
uint32_t t, zeta; | |||
const uint32_t f = | |||
(((uint64_t)MONT * MONT % Q) * (Q - 1) % Q) * ((Q - 1) >> 8) % Q; | |||
k = 0; | |||
for (len = 1; len < N; len <<= 1) { | |||
for (start = 0; start < N; start = j + len) { | |||
zeta = PQCLEAN_DILITHIUMIII_CLEAN_zetas_inv[k++]; | |||
for (j = start; j < start + len; ++j) { | |||
t = p[j]; | |||
p[j] = t + p[j + len]; | |||
p[j + len] = t + 256 * Q - p[j + len]; | |||
p[j + len] = PQCLEAN_DILITHIUMIII_CLEAN_montgomery_reduce( | |||
(uint64_t)zeta * p[j + len]); | |||
} | |||
} | |||
} | |||
for (j = 0; j < N; ++j) { | |||
p[j] = PQCLEAN_DILITHIUMIII_CLEAN_montgomery_reduce((uint64_t)f * p[j]); | |||
} | |||
} |
@@ -1,10 +0,0 @@ | |||
#ifndef NTT_H | |||
#define NTT_H | |||
#include "params.h" | |||
#include <stdint.h> | |||
void PQCLEAN_DILITHIUMIII_CLEAN_ntt(uint32_t p[N]); | |||
void PQCLEAN_DILITHIUMIII_CLEAN_invntt_frominvmont(uint32_t p[N]); | |||
#endif |
@@ -1,301 +0,0 @@ | |||
#include "packing.h" | |||
#include "params.h" | |||
#include "poly.h" | |||
#include "polyvec.h" | |||
/************************************************* | |||
* Name: pack_pk | |||
* | |||
* Description: Bit-pack public key pk = (rho, t1). | |||
* | |||
* Arguments: - unsigned char pk[]: output byte array | |||
* - const unsigned char rho[]: byte array containing rho | |||
* - const polyveck *t1: pointer to vector t1 | |||
**************************************************/ | |||
void PQCLEAN_DILITHIUMIII_CLEAN_pack_pk(unsigned char pk[CRYPTO_PUBLICKEYBYTES], | |||
const unsigned char rho[SEEDBYTES], | |||
const polyveck *t1) { | |||
unsigned int i; | |||
for (i = 0; i < SEEDBYTES; ++i) { | |||
pk[i] = rho[i]; | |||
} | |||
pk += SEEDBYTES; | |||
for (i = 0; i < K; ++i) { | |||
PQCLEAN_DILITHIUMIII_CLEAN_polyt1_pack(pk + i * POLT1_SIZE_PACKED, | |||
t1->vec + i); | |||
} | |||
} | |||
/************************************************* | |||
* Name: unpack_pk | |||
* | |||
* Description: Unpack public key pk = (rho, t1). | |||
* | |||
* Arguments: - const unsigned char rho[]: output byte array for rho | |||
* - const polyveck *t1: pointer to output vector t1 | |||
* - unsigned char pk[]: byte array containing bit-packed pk | |||
**************************************************/ | |||
void PQCLEAN_DILITHIUMIII_CLEAN_unpack_pk( | |||
unsigned char rho[SEEDBYTES], polyveck *t1, | |||
const unsigned char pk[CRYPTO_PUBLICKEYBYTES]) { | |||
unsigned int i; | |||
for (i = 0; i < SEEDBYTES; ++i) { | |||
rho[i] = pk[i]; | |||
} | |||
pk += SEEDBYTES; | |||
for (i = 0; i < K; ++i) { | |||
PQCLEAN_DILITHIUMIII_CLEAN_polyt1_unpack(t1->vec + i, | |||
pk + i * POLT1_SIZE_PACKED); | |||
} | |||
} | |||
/************************************************* | |||
* Name: pack_sk | |||
* | |||
* Description: Bit-pack secret key sk = (rho, key, tr, s1, s2, t0). | |||
* | |||
* Arguments: - unsigned char sk[]: output byte array | |||
* - const unsigned char rho[]: byte array containing rho | |||
* - const unsigned char key[]: byte array containing key | |||
* - const unsigned char tr[]: byte array containing tr | |||
* - const polyvecl *s1: pointer to vector s1 | |||
* - const polyveck *s2: pointer to vector s2 | |||
* - const polyveck *t0: pointer to vector t0 | |||
**************************************************/ | |||
void PQCLEAN_DILITHIUMIII_CLEAN_pack_sk(unsigned char sk[CRYPTO_SECRETKEYBYTES], | |||
const unsigned char rho[SEEDBYTES], | |||
const unsigned char key[SEEDBYTES], | |||
const unsigned char tr[CRHBYTES], | |||
const polyvecl *s1, const polyveck *s2, | |||
const polyveck *t0) { | |||
unsigned int i; | |||
for (i = 0; i < SEEDBYTES; ++i) { | |||
sk[i] = rho[i]; | |||
} | |||
sk += SEEDBYTES; | |||
for (i = 0; i < SEEDBYTES; ++i) { | |||
sk[i] = key[i]; | |||
} | |||
sk += SEEDBYTES; | |||
for (i = 0; i < CRHBYTES; ++i) { | |||
sk[i] = tr[i]; | |||
} | |||
sk += CRHBYTES; | |||
for (i = 0; i < L; ++i) { | |||
PQCLEAN_DILITHIUMIII_CLEAN_polyeta_pack(sk + i * POLETA_SIZE_PACKED, | |||
s1->vec + i); | |||
} | |||
sk += L * POLETA_SIZE_PACKED; | |||
for (i = 0; i < K; ++i) { | |||
PQCLEAN_DILITHIUMIII_CLEAN_polyeta_pack(sk + i * POLETA_SIZE_PACKED, | |||
s2->vec + i); | |||
} | |||
sk += K * POLETA_SIZE_PACKED; | |||
for (i = 0; i < K; ++i) { | |||
PQCLEAN_DILITHIUMIII_CLEAN_polyt0_pack(sk + i * POLT0_SIZE_PACKED, | |||
t0->vec + i); | |||
} | |||
} | |||
/************************************************* | |||
* Name: unpack_sk | |||
* | |||
* Description: Unpack secret key sk = (rho, key, tr, s1, s2, t0). | |||
* | |||
* Arguments: - const unsigned char rho[]: output byte array for rho | |||
* - const unsigned char key[]: output byte array for key | |||
* - const unsigned char tr[]: output byte array for tr | |||
* - const polyvecl *s1: pointer to output vector s1 | |||
* - const polyveck *s2: pointer to output vector s2 | |||
* - const polyveck *r0: pointer to output vector t0 | |||
* - unsigned char sk[]: byte array containing bit-packed sk | |||
**************************************************/ | |||
void PQCLEAN_DILITHIUMIII_CLEAN_unpack_sk( | |||
unsigned char rho[SEEDBYTES], unsigned char key[SEEDBYTES], | |||
unsigned char tr[CRHBYTES], polyvecl *s1, polyveck *s2, polyveck *t0, | |||
const unsigned char sk[CRYPTO_SECRETKEYBYTES]) { | |||
unsigned int i; | |||
for (i = 0; i < SEEDBYTES; ++i) { | |||
rho[i] = sk[i]; | |||
} | |||
sk += SEEDBYTES; | |||
for (i = 0; i < SEEDBYTES; ++i) { | |||
key[i] = sk[i]; | |||
} | |||
sk += SEEDBYTES; | |||
for (i = 0; i < CRHBYTES; ++i) { | |||
tr[i] = sk[i]; | |||
} | |||
sk += CRHBYTES; | |||
for (i = 0; i < L; ++i) { | |||
PQCLEAN_DILITHIUMIII_CLEAN_polyeta_unpack(s1->vec + i, | |||
sk + i * POLETA_SIZE_PACKED); | |||
} | |||
sk += L * POLETA_SIZE_PACKED; | |||
for (i = 0; i < K; ++i) { | |||
PQCLEAN_DILITHIUMIII_CLEAN_polyeta_unpack(s2->vec + i, | |||
sk + i * POLETA_SIZE_PACKED); | |||
} | |||
sk += K * POLETA_SIZE_PACKED; | |||
for (i = 0; i < K; ++i) { | |||
PQCLEAN_DILITHIUMIII_CLEAN_polyt0_unpack(t0->vec + i, | |||
sk + i * POLT0_SIZE_PACKED); | |||
} | |||
} | |||
/************************************************* | |||
* Name: pack_sig | |||
* | |||
* Description: Bit-pack signature sig = (z, h, c). | |||
* | |||
* Arguments: - unsigned char sig[]: output byte array | |||
* - const polyvecl *z: pointer to vector z | |||
* - const polyveck *h: pointer to hint vector h | |||
* - const poly *c: pointer to challenge polynomial | |||
**************************************************/ | |||
void PQCLEAN_DILITHIUMIII_CLEAN_pack_sig(unsigned char sig[CRYPTO_BYTES], | |||
const polyvecl *z, const polyveck *h, | |||
const poly *c) { | |||
unsigned int i, j, k; | |||
uint64_t signs, mask; | |||
for (i = 0; i < L; ++i) { | |||
PQCLEAN_DILITHIUMIII_CLEAN_polyz_pack(sig + i * POLZ_SIZE_PACKED, z->vec + i); | |||
} | |||
sig += L * POLZ_SIZE_PACKED; | |||
/* Encode h */ | |||
k = 0; | |||
for (i = 0; i < K; ++i) { | |||
for (j = 0; j < N; ++j) { | |||
if (h->vec[i].coeffs[j] != 0) { | |||
sig[k++] = j; | |||
} | |||
} | |||
sig[OMEGA + i] = k; | |||
} | |||
while (k < OMEGA) { | |||
sig[k++] = 0; | |||
} | |||
sig += OMEGA + K; | |||
/* Encode c */ | |||
signs = 0; | |||
mask = 1; | |||
for (i = 0; i < N / 8; ++i) { | |||
sig[i] = 0; | |||
for (j = 0; j < 8; ++j) { | |||
if (c->coeffs[8 * i + j] != 0) { | |||
sig[i] |= (1U << j); | |||
if (c->coeffs[8 * i + j] == (Q - 1)) { | |||
signs |= mask; | |||
} | |||
mask <<= 1; | |||
} | |||
} | |||
} | |||
sig += N / 8; | |||
for (i = 0; i < 8; ++i) { | |||
sig[i] = signs >> 8 * i; | |||
} | |||
} | |||
/************************************************* | |||
* Name: unpack_sig | |||
* | |||
* Description: Unpack signature sig = (z, h, c). | |||
* | |||
* Arguments: - polyvecl *z: pointer to output vector z | |||
* - polyveck *h: pointer to output hint vector h | |||
* - poly *c: pointer to output challenge polynomial | |||
* - const unsigned char sig[]: byte array containing | |||
* bit-packed signature | |||
* | |||
* Returns 1 in case of malformed signature; otherwise 0. | |||
**************************************************/ | |||
int PQCLEAN_DILITHIUMIII_CLEAN_unpack_sig(polyvecl *z, polyveck *h, poly *c, | |||
const unsigned char sig[CRYPTO_BYTES]) { | |||
unsigned int i, j, k; | |||
uint64_t signs, mask; | |||
for (i = 0; i < L; ++i) { | |||
PQCLEAN_DILITHIUMIII_CLEAN_polyz_unpack(z->vec + i, | |||
sig + i * POLZ_SIZE_PACKED); | |||
} | |||
sig += L * POLZ_SIZE_PACKED; | |||
/* Decode h */ | |||
k = 0; | |||
for (i = 0; i < K; ++i) { | |||
for (j = 0; j < N; ++j) { | |||
h->vec[i].coeffs[j] = 0; | |||
} | |||
if (sig[OMEGA + i] < k || sig[OMEGA + i] > OMEGA) { | |||
return 1; | |||
} | |||
for (j = k; j < sig[OMEGA + i]; ++j) { | |||
/* Coefficients are ordered for strong unforgeability */ | |||
if (j > k && sig[j] <= sig[j - 1]) { | |||
return 1; | |||
} | |||
h->vec[i].coeffs[sig[j]] = 1; | |||
} | |||
k = sig[OMEGA + i]; | |||
} | |||
/* Extra indices are zero for strong unforgeability */ | |||
for (j = k; j < OMEGA; ++j) { | |||
if (sig[j]) { | |||
return 1; | |||
} | |||
} | |||
sig += OMEGA + K; | |||
/* Decode c */ | |||
for (i = 0; i < N; ++i) { | |||
c->coeffs[i] = 0; | |||
} | |||
signs = 0; | |||
for (i = 0; i < 8; ++i) { | |||
signs |= (uint64_t)sig[N / 8 + i] << 8 * i; | |||
} | |||
/* Extra sign bits are zero for strong unforgeability */ | |||
if (signs >> 60) { | |||
return 1; | |||
} | |||
mask = 1; | |||
for (i = 0; i < N / 8; ++i) { | |||
for (j = 0; j < 8; ++j) { | |||
if ((sig[i] >> j) & 0x01) { | |||
c->coeffs[8 * i + j] = (signs & mask) ? Q - 1 : 1; | |||
mask <<= 1; | |||
} | |||
} | |||
} | |||
return 0; | |||
} |
@@ -1,30 +0,0 @@ | |||
#ifndef PACKING_H | |||
#define PACKING_H | |||
#include "params.h" | |||
#include "polyvec.h" | |||
void PQCLEAN_DILITHIUMIII_CLEAN_pack_pk(unsigned char pk[CRYPTO_PUBLICKEYBYTES], | |||
const unsigned char rho[SEEDBYTES], | |||
const polyveck *t1); | |||
void PQCLEAN_DILITHIUMIII_CLEAN_pack_sk(unsigned char sk[CRYPTO_SECRETKEYBYTES], | |||
const unsigned char rho[SEEDBYTES], | |||
const unsigned char key[SEEDBYTES], | |||
const unsigned char tr[CRHBYTES], | |||
const polyvecl *s1, const polyveck *s2, | |||
const polyveck *t0); | |||
void PQCLEAN_DILITHIUMIII_CLEAN_pack_sig(unsigned char sig[CRYPTO_BYTES], | |||
const polyvecl *z, const polyveck *h, | |||
const poly *c); | |||
void PQCLEAN_DILITHIUMIII_CLEAN_unpack_pk( | |||
unsigned char rho[SEEDBYTES], polyveck *t1, | |||
const unsigned char pk[CRYPTO_PUBLICKEYBYTES]); | |||
void PQCLEAN_DILITHIUMIII_CLEAN_unpack_sk( | |||
unsigned char rho[SEEDBYTES], unsigned char key[SEEDBYTES], | |||
unsigned char tr[CRHBYTES], polyvecl *s1, polyveck *s2, polyveck *t0, | |||
const unsigned char sk[CRYPTO_SECRETKEYBYTES]); | |||
int PQCLEAN_DILITHIUMIII_CLEAN_unpack_sig(polyvecl *z, polyveck *h, poly *c, | |||
const unsigned char sig[CRYPTO_BYTES]); | |||
#endif |
@@ -1,68 +0,0 @@ | |||
#ifndef PARAMS_H | |||
#define PARAMS_H | |||
#ifndef MODE | |||
#define MODE 2 | |||
#endif | |||
#define SEEDBYTES 32U | |||
#define CRHBYTES 48U | |||
#define N 256U | |||
#define Q 8380417U | |||
#define QBITS 23U | |||
#define ROOT_OF_UNITY 1753U | |||
#define D 14U | |||
#define GAMMA1 ((Q - 1U) / 16U) | |||
#define GAMMA2 (GAMMA1 / 2U) | |||
#define ALPHA (2U * GAMMA2) | |||
#if MODE == 0 | |||
#define K 3U | |||
#define L 2U | |||
#define ETA 7U | |||
#define SETABITS 4U | |||
#define BETA 375U | |||
#define OMEGA 64U | |||
#elif MODE == 1 | |||
#define K 4U | |||
#define L 3U | |||
#define ETA 6U | |||
#define SETABITS 4U | |||
#define BETA 325U | |||
#define OMEGA 80U | |||
#elif MODE == 2 | |||
#define K 5U | |||
#define L 4U | |||
#define ETA 5U | |||
#define SETABITS 4U | |||
#define BETA 275U | |||
#define OMEGA 96U | |||
#elif MODE == 3 | |||
#define K 6U | |||
#define L 5U | |||
#define ETA 3U | |||
#define SETABITS 3U | |||
#define BETA 175U | |||
#define OMEGA 120U | |||
#endif | |||
#define POL_SIZE_PACKED ((N * QBITS) / 8) | |||
#define POLT1_SIZE_PACKED ((N * (QBITS - D)) / 8) | |||
#define POLT0_SIZE_PACKED ((N * D) / 8) | |||
#define POLETA_SIZE_PACKED ((N * SETABITS) / 8) | |||
#define POLZ_SIZE_PACKED ((N * (QBITS - 3)) / 8) | |||
#define POLW1_SIZE_PACKED ((N * 4) / 8) | |||
#define POLVECK_SIZE_PACKED (K * POL_SIZE_PACKED) | |||
#define POLVECL_SIZE_PACKED (L * POL_SIZE_PACKED) | |||
#define CRYPTO_PUBLICKEYBYTES (SEEDBYTES + K * POLT1_SIZE_PACKED) | |||
#define CRYPTO_SECRETKEYBYTES \ | |||
(2 * SEEDBYTES + (L + K) * POLETA_SIZE_PACKED + CRHBYTES + \ | |||
K * POLT0_SIZE_PACKED) | |||
#define CRYPTO_BYTES (L * POLZ_SIZE_PACKED + (OMEGA + K) + (N / 8 + 8)) | |||
#endif |
@@ -1,777 +0,0 @@ | |||
#include "poly.h" | |||
#include "fips202.h" | |||
#include "ntt.h" | |||
#include "params.h" | |||
#include "reduce.h" | |||
#include "rounding.h" | |||
#include <stdint.h> | |||
/************************************************* | |||
* Name: poly_reduce | |||
* | |||
* Description: Reduce all coefficients of input polynomial to representative | |||
* in [0,2*Q[. | |||
* | |||
* Arguments: - poly *a: pointer to input/output polynomial | |||
**************************************************/ | |||
void PQCLEAN_DILITHIUMIII_CLEAN_poly_reduce(poly *a) { | |||
unsigned int i; | |||
for (i = 0; i < N; ++i) { | |||
a->coeffs[i] = PQCLEAN_DILITHIUMIII_CLEAN_reduce32(a->coeffs[i]); | |||
} | |||
} | |||
/************************************************* | |||
* Name: poly_csubq | |||
* | |||
* Description: For all coefficients of input polynomial subtract Q if | |||
* coefficient is bigger than Q. | |||
* | |||
* Arguments: - poly *a: pointer to input/output polynomial | |||
**************************************************/ | |||
void PQCLEAN_DILITHIUMIII_CLEAN_poly_csubq(poly *a) { | |||
unsigned int i; | |||
for (i = 0; i < N; ++i) { | |||
a->coeffs[i] = PQCLEAN_DILITHIUMIII_CLEAN_csubq(a->coeffs[i]); | |||
} | |||
} | |||
/************************************************* | |||
* Name: poly_freeze | |||
* | |||
* Description: Reduce all coefficients of the polynomial to standard | |||
* representatives. | |||
* | |||
* Arguments: - poly *a: pointer to input/output polynomial | |||
**************************************************/ | |||
void PQCLEAN_DILITHIUMIII_CLEAN_poly_freeze(poly *a) { | |||
unsigned int i; | |||
for (i = 0; i < N; ++i) { | |||
a->coeffs[i] = PQCLEAN_DILITHIUMIII_CLEAN_freeze(a->coeffs[i]); | |||
} | |||
} | |||
/************************************************* | |||
* Name: poly_add | |||
* | |||
* Description: Add polynomials. No modular reduction is performed. | |||
* | |||
* Arguments: - poly *c: pointer to output polynomial | |||
* - const poly *a: pointer to first summand | |||
* - const poly *b: pointer to second summand | |||
**************************************************/ | |||
void PQCLEAN_DILITHIUMIII_CLEAN_poly_add(poly *c, const poly *a, const poly *b) { | |||
unsigned int i; | |||
for (i = 0; i < N; ++i) { | |||
c->coeffs[i] = a->coeffs[i] + b->coeffs[i]; | |||
} | |||
} | |||
/************************************************* | |||
* Name: poly_sub | |||
* | |||
* Description: Subtract polynomials. Assumes coefficients of second input | |||
* polynomial to be less than 2*Q. No modular reduction is | |||
* performed. | |||
* | |||
* Arguments: - poly *c: pointer to output polynomial | |||
* - const poly *a: pointer to first input polynomial | |||
* - const poly *b: pointer to second input polynomial to be | |||
* subtraced from first input polynomial | |||
**************************************************/ | |||
void PQCLEAN_DILITHIUMIII_CLEAN_poly_sub(poly *c, const poly *a, const poly *b) { | |||
unsigned int i; | |||
for (i = 0; i < N; ++i) { | |||
c->coeffs[i] = a->coeffs[i] + 2 * Q - b->coeffs[i]; | |||
} | |||
} | |||
/************************************************* | |||
* Name: poly_neg | |||
* | |||
* Description: Negate polynomial. Assumes input coefficients to be standard | |||
* representatives. | |||
* | |||
* Arguments: - poly *a: pointer to input/output polynomial | |||
**************************************************/ | |||
void PQCLEAN_DILITHIUMIII_CLEAN_poly_neg(poly *a) { | |||
unsigned int i; | |||
for (i = 0; i < N; ++i) { | |||
a->coeffs[i] = Q - a->coeffs[i]; | |||
} | |||
} | |||
/************************************************* | |||
* Name: poly_shiftl | |||
* | |||
* Description: Multiply polynomial by 2^k without modular reduction. Assumes | |||
* input coefficients to be less than 2^{32-k}. | |||
* | |||
* Arguments: - poly *a: pointer to input/output polynomial | |||
* - unsigned int k: exponent | |||
**************************************************/ | |||
void PQCLEAN_DILITHIUMIII_CLEAN_poly_shiftl(poly *a, unsigned int k) { | |||
unsigned int i; | |||
for (i = 0; i < N; ++i) { | |||
a->coeffs[i] <<= k; | |||
} | |||
} | |||
/************************************************* | |||
* Name: poly_ntt | |||
* | |||
* Description: Forward NTT. Output coefficients can be up to 16*Q larger than | |||
* input coefficients. | |||
* | |||
* Arguments: - poly *a: pointer to input/output polynomial | |||
**************************************************/ | |||
void PQCLEAN_DILITHIUMIII_CLEAN_poly_ntt(poly *a) { | |||
PQCLEAN_DILITHIUMIII_CLEAN_ntt(a->coeffs); | |||
} | |||
/************************************************* | |||
* Name: poly_invntt_montgomery | |||
* | |||
* Description: Inverse NTT and multiplication with 2^{32}. Input coefficients | |||
* need to be less than 2*Q. Output coefficients are less than 2*Q. | |||
* | |||
* Arguments: - poly *a: pointer to input/output polynomial | |||
**************************************************/ | |||
void PQCLEAN_DILITHIUMIII_CLEAN_poly_invntt_montgomery(poly *a) { | |||
PQCLEAN_DILITHIUMIII_CLEAN_invntt_frominvmont(a->coeffs); | |||
} | |||
/************************************************* | |||
* Name: poly_pointwise_invmontgomery | |||
* | |||
* Description: Pointwise multiplication of polynomials in NTT domain | |||
* representation and multiplication of resulting polynomial | |||
* with 2^{-32}. Output coefficients are less than 2*Q if input | |||
* coefficient are less than 22*Q. | |||
* | |||
* Arguments: - poly *c: pointer to output polynomial | |||
* - const poly *a: pointer to first input polynomial | |||
* - const poly *b: pointer to second input polynomial | |||
**************************************************/ | |||
void PQCLEAN_DILITHIUMIII_CLEAN_poly_pointwise_invmontgomery(poly *c, const poly *a, | |||
const poly *b) { | |||
unsigned int i; | |||
for (i = 0; i < N; ++i) { | |||
c->coeffs[i] = PQCLEAN_DILITHIUMIII_CLEAN_montgomery_reduce( | |||
(uint64_t)a->coeffs[i] * b->coeffs[i]); | |||
} | |||
} | |||
/************************************************* | |||
* Name: poly_power2round | |||
* | |||
* Description: For all coefficients c of the input polynomial, | |||
* compute c0, c1 such that c mod Q = c1*2^D + c0 | |||
* with -2^{D-1} < c0 <= 2^{D-1}. Assumes coefficients to be | |||
* standard representatives. | |||
* | |||
* Arguments: - poly *a1: pointer to output polynomial with coefficients c1 | |||
* - poly *a0: pointer to output polynomial with coefficients Q + | |||
*a0 | |||
* - const poly *v: pointer to input polynomial | |||
**************************************************/ | |||
void PQCLEAN_DILITHIUMIII_CLEAN_poly_power2round(poly *a1, poly *a0, const poly *a) { | |||
unsigned int i; | |||
for (i = 0; i < N; ++i) { | |||
a1->coeffs[i] = | |||
PQCLEAN_DILITHIUMIII_CLEAN_power2round(a->coeffs[i], a0->coeffs + i); | |||
} | |||
} | |||
/************************************************* | |||
* Name: poly_decompose | |||
* | |||
* Description: For all coefficients c of the input polynomial, | |||
* compute high and low bits c0, c1 such c mod Q = c1*ALPHA + c0 | |||
* with -ALPHA/2 < c0 <= ALPHA/2 except c1 = (Q-1)/ALPHA where we | |||
* set c1 = 0 and -ALPHA/2 <= c0 = c mod Q - Q < 0. | |||
* Assumes coefficients to be standard representatives. | |||
* | |||
* Arguments: - poly *a1: pointer to output polynomial with coefficients c1 | |||
* - poly *a0: pointer to output polynomial with coefficients Q + | |||
*a0 | |||
* - const poly *c: pointer to input polynomial | |||
**************************************************/ | |||
void PQCLEAN_DILITHIUMIII_CLEAN_poly_decompose(poly *a1, poly *a0, const poly *a) { | |||
unsigned int i; | |||
for (i = 0; i < N; ++i) { | |||
a1->coeffs[i] = | |||
PQCLEAN_DILITHIUMIII_CLEAN_decompose(a->coeffs[i], a0->coeffs + i); | |||
} | |||
} | |||
/************************************************* | |||
* Name: poly_make_hint | |||
* | |||
* Description: Compute hint polynomial. The coefficients of which indicate | |||
* whether the high bits of the corresponding coefficients | |||
* of the first input polynomial and of the sum of the input | |||
* polynomials differ. | |||
* | |||
* Arguments: - poly *h: pointer to output hint polynomial | |||
* - const poly *a: pointer to first input polynomial | |||
* - const poly *b: pointer to second input polynomial | |||
* | |||
* Returns number of 1 bits. | |||
**************************************************/ | |||
unsigned int PQCLEAN_DILITHIUMIII_CLEAN_poly_make_hint(poly *h, const poly *a, | |||
const poly *b) { | |||
unsigned int i, s = 0; | |||
for (i = 0; i < N; ++i) { | |||
h->coeffs[i] = | |||
PQCLEAN_DILITHIUMIII_CLEAN_make_hint(a->coeffs[i], b->coeffs[i]); | |||
s += h->coeffs[i]; | |||
} | |||
return s; | |||
} | |||
/************************************************* | |||
* Name: poly_use_hint | |||
* | |||
* Description: Use hint polynomial to correct the high bits of a polynomial. | |||
* | |||
* Arguments: - poly *a: pointer to output polynomial with corrected high bits | |||
* - const poly *b: pointer to input polynomial | |||
* - const poly *h: pointer to input hint polynomial | |||
**************************************************/ | |||
void PQCLEAN_DILITHIUMIII_CLEAN_poly_use_hint(poly *a, const poly *b, const poly *h) { | |||
unsigned int i; | |||
for (i = 0; i < N; ++i) { | |||
a->coeffs[i] = | |||
PQCLEAN_DILITHIUMIII_CLEAN_use_hint(b->coeffs[i], h->coeffs[i]); | |||
} | |||
} | |||
/************************************************* | |||
* Name: poly_chknorm | |||
* | |||
* Description: Check infinity norm of polynomial against given bound. | |||
* Assumes input coefficients to be standard representatives. | |||
* | |||
* Arguments: - const poly *a: pointer to polynomial | |||
* - uint32_t B: norm bound | |||
* | |||
* Returns 0 if norm is strictly smaller than B and 1 otherwise. | |||
**************************************************/ | |||
int PQCLEAN_DILITHIUMIII_CLEAN_poly_chknorm(const poly *a, uint32_t B) { | |||
unsigned int i; | |||
int32_t t; | |||
/* It is ok to leak which coefficient violates the bound since | |||
the probability for each coefficient is independent of secret | |||
data but we must not leak the sign of the centralized representative. */ | |||
for (i = 0; i < N; ++i) { | |||
/* Absolute value of centralized representative */ | |||
t = (Q - 1) / 2 - a->coeffs[i]; | |||
t ^= (t >> 31); | |||
t = (Q - 1) / 2 - t; | |||
if ((uint32_t)t >= B) { | |||
return 1; | |||
} | |||
} | |||
return 0; | |||
} | |||
/************************************************* | |||
* Name: poly_uniform | |||
* | |||
* Description: Sample uniformly random polynomial using stream of random bytes. | |||
* Assumes that enough random bytes are given (e.g. | |||
* 5*SHAKE128_RATE bytes). | |||
* | |||
* Arguments: - poly *a: pointer to output polynomial | |||
* - const unsigned char *buf: array of random bytes | |||
**************************************************/ | |||
void PQCLEAN_DILITHIUMIII_CLEAN_poly_uniform(poly *a, const unsigned char *buf) { | |||
unsigned int ctr, pos; | |||
uint32_t t; | |||
ctr = pos = 0; | |||
while (ctr < N) { | |||
t = buf[pos++]; | |||
t |= (uint32_t)buf[pos++] << 8; | |||
t |= (uint32_t)buf[pos++] << 16; | |||
t &= 0x7FFFFF; | |||
if (t < Q) { | |||
a->coeffs[ctr++] = t; | |||
} | |||
} | |||
} | |||
/************************************************* | |||
* Name: rej_eta | |||
* | |||
* Description: Sample uniformly random coefficients in [-ETA, ETA] by | |||
* performing rejection sampling using array of random bytes. | |||
* | |||
* Arguments: - uint32_t *a: pointer to output array (allocated) | |||
* - unsigned int len: number of coefficients to be sampled | |||
* - const unsigned char *buf: array of random bytes | |||
* - unsigned int buflen: length of array of random bytes | |||
* | |||
* Returns number of sampled coefficients. Can be smaller than len if not enough | |||
* random bytes were given. | |||
**************************************************/ | |||
static unsigned int rej_eta(uint32_t *a, unsigned int len, | |||
const unsigned char *buf, unsigned int buflen) { | |||
#if ETA > 7 | |||
#error "rej_eta() assumes ETA <= 7" | |||
#endif | |||
unsigned int ctr, pos; | |||
unsigned char t0, t1; | |||
ctr = pos = 0; | |||
while (ctr < len && pos < buflen) { | |||
#if ETA <= 3 | |||
t0 = buf[pos] & 0x07; | |||
t1 = buf[pos++] >> 5; | |||
#else | |||
t0 = buf[pos] & 0x0F; | |||
t1 = buf[pos++] >> 4; | |||
#endif | |||
if (t0 <= 2 * ETA) { | |||
a[ctr++] = Q + ETA - t0; | |||
} | |||
if (t1 <= 2 * ETA && ctr < len) { | |||
a[ctr++] = Q + ETA - t1; | |||
} | |||
} | |||
return ctr; | |||
} | |||
/************************************************* | |||
* Name: poly_uniform_eta | |||
* | |||
* Description: Sample polynomial with uniformly random coefficients | |||
* in [-ETA,ETA] by performing rejection sampling using the | |||
* output stream from SHAKE256(seed|nonce). | |||
* | |||
* Arguments: - poly *a: pointer to output polynomial | |||
* - const unsigned char seed[]: byte array with seed of length | |||
* SEEDBYTES | |||
* - unsigned char nonce: nonce byte | |||
**************************************************/ | |||
void PQCLEAN_DILITHIUMIII_CLEAN_poly_uniform_eta(poly *a, | |||
const unsigned char seed[SEEDBYTES], | |||
unsigned char nonce) { | |||
unsigned int i, ctr; | |||
unsigned char inbuf[SEEDBYTES + 1]; | |||
/* Probability that we need more than 2 blocks: < 2^{-84} | |||
Probability that we need more than 3 blocks: < 2^{-352} */ | |||
unsigned char outbuf[2 * SHAKE256_RATE]; | |||
uint64_t state[25]; | |||
for (i = 0; i < SEEDBYTES; ++i) { | |||
inbuf[i] = seed[i]; | |||
} | |||
inbuf[SEEDBYTES] = nonce; | |||
shake256_absorb(state, inbuf, SEEDBYTES + 1); | |||
shake256_squeezeblocks(outbuf, 2, state); | |||
ctr = rej_eta(a->coeffs, N, outbuf, 2 * SHAKE256_RATE); | |||
if (ctr < N) { | |||
shake256_squeezeblocks(outbuf, 1, state); | |||
rej_eta(a->coeffs + ctr, N - ctr, outbuf, SHAKE256_RATE); | |||
} | |||
} | |||
/************************************************* | |||
* Name: rej_gamma1m1 | |||
* | |||
* Description: Sample uniformly random coefficients | |||
* in [-(GAMMA1 - 1), GAMMA1 - 1] by performing rejection sampling | |||
* using array of random bytes. | |||
* | |||
* Arguments: - uint32_t *a: pointer to output array (allocated) | |||
* - unsigned int len: number of coefficients to be sampled | |||
* - const unsigned char *buf: array of random bytes | |||
* - unsigned int buflen: length of array of random bytes | |||
* | |||
* Returns number of sampled coefficients. Can be smaller than len if not enough | |||
* random bytes were given. | |||
**************************************************/ | |||
static unsigned int rej_gamma1m1(uint32_t *a, unsigned int len, | |||
const unsigned char *buf, | |||
unsigned int buflen) { | |||
#if GAMMA1 > (1 << 19) | |||
#error "rej_gamma1m1() assumes GAMMA1 - 1 fits in 19 bits" | |||
#endif | |||
unsigned int ctr, pos; | |||
uint32_t t0, t1; | |||
ctr = pos = 0; | |||
while (ctr < len && pos + 5 <= buflen) { | |||
t0 = buf[pos]; | |||
t0 |= (uint32_t)buf[pos + 1] << 8; | |||
t0 |= (uint32_t)buf[pos + 2] << 16; | |||
t0 &= 0xFFFFF; | |||
t1 = buf[pos + 2] >> 4; | |||
t1 |= (uint32_t)buf[pos + 3] << 4; | |||
t1 |= (uint32_t)buf[pos + 4] << 12; | |||
pos += 5; | |||
if (t0 <= 2 * GAMMA1 - 2) { | |||
a[ctr++] = Q + GAMMA1 - 1 - t0; | |||
} | |||
if (t1 <= 2 * GAMMA1 - 2 && ctr < len) { | |||
a[ctr++] = Q + GAMMA1 - 1 - t1; | |||
} | |||
} | |||
return ctr; | |||
} | |||
/************************************************* | |||
* Name: poly_uniform_gamma1m1 | |||
* | |||
* Description: Sample polynomial with uniformly random coefficients | |||
* in [-(GAMMA1 - 1), GAMMA1 - 1] by performing rejection | |||
* sampling on output stream of SHAKE256(seed|nonce). | |||
* | |||
* Arguments: - poly *a: pointer to output polynomial | |||
* - const unsigned char seed[]: byte array with seed of length | |||
* SEEDBYTES + CRHBYTES | |||
* - uint16_t nonce: 16-bit nonce | |||
**************************************************/ | |||
void PQCLEAN_DILITHIUMIII_CLEAN_poly_uniform_gamma1m1( | |||
poly *a, const unsigned char seed[SEEDBYTES + CRHBYTES], uint16_t nonce) { | |||
unsigned int i, ctr; | |||
unsigned char inbuf[SEEDBYTES + CRHBYTES + 2]; | |||
/* Probability that we need more than 5 blocks: < 2^{-81} | |||
Probability that we need more than 6 blocks: < 2^{-467} */ | |||
unsigned char outbuf[5 * SHAKE256_RATE]; | |||
uint64_t state[25]; | |||
for (i = 0; i < SEEDBYTES + CRHBYTES; ++i) { | |||
inbuf[i] = seed[i]; | |||
} | |||
inbuf[SEEDBYTES + CRHBYTES] = nonce & 0xFF; | |||
inbuf[SEEDBYTES + CRHBYTES + 1] = nonce >> 8; | |||
shake256_absorb(state, inbuf, SEEDBYTES + CRHBYTES + 2); | |||
shake256_squeezeblocks(outbuf, 5, state); | |||
ctr = rej_gamma1m1(a->coeffs, N, outbuf, 5 * SHAKE256_RATE); | |||
if (ctr < N) { | |||
/* There are no bytes left in outbuf | |||
since 5*SHAKE256_RATE is divisible by 5 */ | |||
shake256_squeezeblocks(outbuf, 1, state); | |||
rej_gamma1m1(a->coeffs + ctr, N - ctr, outbuf, SHAKE256_RATE); | |||
} | |||
} | |||
/************************************************* | |||
* Name: polyeta_pack | |||
* | |||
* Description: Bit-pack polynomial with coefficients in [-ETA,ETA]. | |||
* Input coefficients are assumed to lie in [Q-ETA,Q+ETA]. | |||
* | |||
* Arguments: - unsigned char *r: pointer to output byte array with at least | |||
* POLETA_SIZE_PACKED bytes | |||
* - const poly *a: pointer to input polynomial | |||
**************************************************/ | |||
void PQCLEAN_DILITHIUMIII_CLEAN_polyeta_pack(unsigned char *r, const poly *a) { | |||
#if ETA > 7 | |||
#error "polyeta_pack() assumes ETA <= 7" | |||
#endif | |||
unsigned int i; | |||
unsigned char t[8]; | |||
#if ETA <= 3 | |||
for (i = 0; i < N / 8; ++i) { | |||
t[0] = Q + ETA - a->coeffs[8 * i + 0]; | |||
t[1] = Q + ETA - a->coeffs[8 * i + 1]; | |||
t[2] = Q + ETA - a->coeffs[8 * i + 2]; | |||
t[3] = Q + ETA - a->coeffs[8 * i + 3]; | |||
t[4] = Q + ETA - a->coeffs[8 * i + 4]; | |||
t[5] = Q + ETA - a->coeffs[8 * i + 5]; | |||
t[6] = Q + ETA - a->coeffs[8 * i + 6]; | |||
t[7] = Q + ETA - a->coeffs[8 * i + 7]; | |||
r[3 * i + 0] = t[0]; | |||
r[3 * i + 0] |= t[1] << 3; | |||
r[3 * i + 0] |= t[2] << 6; | |||
r[3 * i + 1] = t[2] >> 2; | |||
r[3 * i + 1] |= t[3] << 1; | |||
r[3 * i + 1] |= t[4] << 4; | |||
r[3 * i + 1] |= t[5] << 7; | |||
r[3 * i + 2] = t[5] >> 1; | |||
r[3 * i + 2] |= t[6] << 2; | |||
r[3 * i + 2] |= t[7] << 5; | |||
} | |||
#else | |||
for (i = 0; i < N / 2; ++i) { | |||
t[0] = Q + ETA - a->coeffs[2 * i + 0]; | |||
t[1] = Q + ETA - a->coeffs[2 * i + 1]; | |||
r[i] = t[0] | (t[1] << 4); | |||
} | |||
#endif | |||
} | |||
/************************************************* | |||
* Name: polyeta_unpack | |||
* | |||
* Description: Unpack polynomial with coefficients in [-ETA,ETA]. | |||
* Output coefficients lie in [Q-ETA,Q+ETA]. | |||
* | |||
* Arguments: - poly *r: pointer to output polynomial | |||
* - const unsigned char *a: byte array with bit-packed polynomial | |||
**************************************************/ | |||
void PQCLEAN_DILITHIUMIII_CLEAN_polyeta_unpack(poly *r, const unsigned char *a) { | |||
unsigned int i; | |||
#if ETA <= 3 | |||
for (i = 0; i < N / 8; ++i) { | |||
r->coeffs[8 * i + 0] = a[3 * i + 0] & 0x07; | |||
r->coeffs[8 * i + 1] = (a[3 * i + 0] >> 3) & 0x07; | |||
r->coeffs[8 * i + 2] = | |||
(a[3 * i + 0] >> 6) | ((a[3 * i + 1] & 0x01) << 2); | |||
r->coeffs[8 * i + 3] = (a[3 * i + 1] >> 1) & 0x07; | |||
r->coeffs[8 * i + 4] = (a[3 * i + 1] >> 4) & 0x07; | |||
r->coeffs[8 * i + 5] = | |||
(a[3 * i + 1] >> 7) | ((a[3 * i + 2] & 0x03) << 1); | |||
r->coeffs[8 * i + 6] = (a[3 * i + 2] >> 2) & 0x07; | |||
r->coeffs[8 * i + 7] = (a[3 * i + 2] >> 5); | |||
r->coeffs[8 * i + 0] = Q + ETA - r->coeffs[8 * i + 0]; | |||
r->coeffs[8 * i + 1] = Q + ETA - r->coeffs[8 * i + 1]; | |||
r->coeffs[8 * i + 2] = Q + ETA - r->coeffs[8 * i + 2]; | |||
r->coeffs[8 * i + 3] = Q + ETA - r->coeffs[8 * i + 3]; | |||
r->coeffs[8 * i + 4] = Q + ETA - r->coeffs[8 * i + 4]; | |||
r->coeffs[8 * i + 5] = Q + ETA - r->coeffs[8 * i + 5]; | |||
r->coeffs[8 * i + 6] = Q + ETA - r->coeffs[8 * i + 6]; | |||
r->coeffs[8 * i + 7] = Q + ETA - r->coeffs[8 * i + 7]; | |||
} | |||
#else | |||
for (i = 0; i < N / 2; ++i) { | |||
r->coeffs[2 * i + 0] = a[i] & 0x0F; | |||
r->coeffs[2 * i + 1] = a[i] >> 4; | |||
r->coeffs[2 * i + 0] = Q + ETA - r->coeffs[2 * i + 0]; | |||
r->coeffs[2 * i + 1] = Q + ETA - r->coeffs[2 * i + 1]; | |||
} | |||
#endif | |||
} | |||
/************************************************* | |||
* Name: polyt1_pack | |||
* | |||
* Description: Bit-pack polynomial t1 with coefficients fitting in 9 bits. | |||
* Input coefficients are assumed to be standard representatives. | |||
* | |||
* Arguments: - unsigned char *r: pointer to output byte array with at least | |||
* POLT1_SIZE_PACKED bytes | |||
* - const poly *a: pointer to input polynomial | |||
**************************************************/ | |||
void PQCLEAN_DILITHIUMIII_CLEAN_polyt1_pack(unsigned char *r, const poly *a) { | |||
#if D != 14 | |||
#error "polyt1_pack() assumes D == 14" | |||
#endif | |||
unsigned int i; | |||
for (i = 0; i < N / 8; ++i) { | |||
r[9 * i + 0] = a->coeffs[8 * i + 0] & 0xFF; | |||
r[9 * i + 1] = (a->coeffs[8 * i + 0] >> 8) | ((a->coeffs[8 * i + 1] & 0x7F) << 1); | |||
r[9 * i + 2] = (a->coeffs[8 * i + 1] >> 7) | ((a->coeffs[8 * i + 2] & 0x3F) << 2); | |||
r[9 * i + 3] = (a->coeffs[8 * i + 2] >> 6) | ((a->coeffs[8 * i + 3] & 0x1F) << 3); | |||
r[9 * i + 4] = (a->coeffs[8 * i + 3] >> 5) | ((a->coeffs[8 * i + 4] & 0x0F) << 4); | |||
r[9 * i + 5] = (a->coeffs[8 * i + 4] >> 4) | ((a->coeffs[8 * i + 5] & 0x07) << 5); | |||
r[9 * i + 6] = (a->coeffs[8 * i + 5] >> 3) | ((a->coeffs[8 * i + 6] & 0x03) << 6); | |||
r[9 * i + 7] = (a->coeffs[8 * i + 6] >> 2) | ((a->coeffs[8 * i + 7] & 0x01) << 7); | |||
r[9 * i + 8] = a->coeffs[8 * i + 7] >> 1; | |||
} | |||
} | |||
/************************************************* | |||
* Name: polyt1_unpack | |||
* | |||
* Description: Unpack polynomial t1 with 9-bit coefficients. | |||
* Output coefficients are standard representatives. | |||
* | |||
* Arguments: - poly *r: pointer to output polynomial | |||
* - const unsigned char *a: byte array with bit-packed polynomial | |||
**************************************************/ | |||
void PQCLEAN_DILITHIUMIII_CLEAN_polyt1_unpack(poly *r, const unsigned char *a) { | |||
unsigned int i; | |||
for (i = 0; i < N / 8; ++i) { | |||
r->coeffs[8 * i + 0] = a[9 * i + 0] | ((uint32_t)(a[9 * i + 1] & 0x01) << 8); | |||
r->coeffs[8 * i + 1] = (a[9 * i + 1] >> 1) | ((uint32_t)(a[9 * i + 2] & 0x03) << 7); | |||
r->coeffs[8 * i + 2] = (a[9 * i + 2] >> 2) | ((uint32_t)(a[9 * i + 3] & 0x07) << 6); | |||
r->coeffs[8 * i + 3] = (a[9 * i + 3] >> 3) | ((uint32_t)(a[9 * i + 4] & 0x0F) << 5); | |||
r->coeffs[8 * i + 4] = (a[9 * i + 4] >> 4) | ((uint32_t)(a[9 * i + 5] & 0x1F) << 4); | |||
r->coeffs[8 * i + 5] = (a[9 * i + 5] >> 5) | ((uint32_t)(a[9 * i + 6] & 0x3F) << 3); | |||
r->coeffs[8 * i + 6] = (a[9 * i + 6] >> 6) | ((uint32_t)(a[9 * i + 7] & 0x7F) << 2); | |||
r->coeffs[8 * i + 7] = (a[9 * i + 7] >> 7) | ((uint32_t)(a[9 * i + 8] & 0xFF) << 1); | |||
} | |||
} | |||
/************************************************* | |||
* Name: polyt0_pack | |||
* | |||
* Description: Bit-pack polynomial t0 with coefficients in ]-2^{D-1}, 2^{D-1}]. | |||
* Input coefficients are assumed to lie in ]Q-2^{D-1}, Q+2^{D-1}]. | |||
* | |||
* Arguments: - unsigned char *r: pointer to output byte array with at least | |||
* POLT0_SIZE_PACKED bytes | |||
* - const poly *a: pointer to input polynomial | |||
**************************************************/ | |||
void PQCLEAN_DILITHIUMIII_CLEAN_polyt0_pack(unsigned char *r, const poly *a) { | |||
unsigned int i; | |||
uint32_t t[4]; | |||
for (i = 0; i < N / 4; ++i) { | |||
t[0] = Q + (1 << (D - 1)) - a->coeffs[4 * i + 0]; | |||
t[1] = Q + (1 << (D - 1)) - a->coeffs[4 * i + 1]; | |||
t[2] = Q + (1 << (D - 1)) - a->coeffs[4 * i + 2]; | |||
t[3] = Q + (1 << (D - 1)) - a->coeffs[4 * i + 3]; | |||
r[7 * i + 0] = t[0]; | |||
r[7 * i + 1] = t[0] >> 8; | |||
r[7 * i + 1] |= t[1] << 6; | |||
r[7 * i + 2] = t[1] >> 2; | |||
r[7 * i + 3] = t[1] >> 10; | |||
r[7 * i + 3] |= t[2] << 4; | |||
r[7 * i + 4] = t[2] >> 4; | |||
r[7 * i + 5] = t[2] >> 12; | |||
r[7 * i + 5] |= t[3] << 2; | |||
r[7 * i + 6] = t[3] >> 6; | |||
} | |||
} | |||
/************************************************* | |||
* Name: polyt0_unpack | |||
* | |||
* Description: Unpack polynomial t0 with coefficients in ]-2^{D-1}, 2^{D-1}]. | |||
* Output coefficients lie in ]Q-2^{D-1},Q+2^{D-1}]. | |||
* | |||
* Arguments: - poly *r: pointer to output polynomial | |||
* - const unsigned char *a: byte array with bit-packed polynomial | |||
**************************************************/ | |||
void PQCLEAN_DILITHIUMIII_CLEAN_polyt0_unpack(poly *r, const unsigned char *a) { | |||
unsigned int i; | |||
for (i = 0; i < N / 4; ++i) { | |||
r->coeffs[4 * i + 0] = a[7 * i + 0]; | |||
r->coeffs[4 * i + 0] |= (uint32_t)(a[7 * i + 1] & 0x3F) << 8; | |||
r->coeffs[4 * i + 1] = a[7 * i + 1] >> 6; | |||
r->coeffs[4 * i + 1] |= (uint32_t)a[7 * i + 2] << 2; | |||
r->coeffs[4 * i + 1] |= (uint32_t)(a[7 * i + 3] & 0x0F) << 10; | |||
r->coeffs[4 * i + 2] = a[7 * i + 3] >> 4; | |||
r->coeffs[4 * i + 2] |= (uint32_t)a[7 * i + 4] << 4; | |||
r->coeffs[4 * i + 2] |= (uint32_t)(a[7 * i + 5] & 0x03) << 12; | |||
r->coeffs[4 * i + 3] = a[7 * i + 5] >> 2; | |||
r->coeffs[4 * i + 3] |= (uint32_t)a[7 * i + 6] << 6; | |||
r->coeffs[4 * i + 0] = Q + (1 << (D - 1)) - r->coeffs[4 * i + 0]; | |||
r->coeffs[4 * i + 1] = Q + (1 << (D - 1)) - r->coeffs[4 * i + 1]; | |||
r->coeffs[4 * i + 2] = Q + (1 << (D - 1)) - r->coeffs[4 * i + 2]; | |||
r->coeffs[4 * i + 3] = Q + (1 << (D - 1)) - r->coeffs[4 * i + 3]; | |||
} | |||
} | |||
/************************************************* | |||
* Name: polyz_pack | |||
* | |||
* Description: Bit-pack polynomial z with coefficients | |||
* in [-(GAMMA1 - 1), GAMMA1 - 1]. | |||
* Input coefficients are assumed to be standard representatives. | |||
* | |||
* Arguments: - unsigned char *r: pointer to output byte array with at least | |||
* POLZ_SIZE_PACKED bytes | |||
* - const poly *a: pointer to input polynomial | |||
**************************************************/ | |||
void PQCLEAN_DILITHIUMIII_CLEAN_polyz_pack(unsigned char *r, const poly *a) { | |||
#if GAMMA1 > (1 << 19) | |||
#error "polyz_pack() assumes GAMMA1 <= 2^{19}" | |||
#endif | |||
unsigned int i; | |||
uint32_t t[2]; | |||
for (i = 0; i < N / 2; ++i) { | |||
/* Map to {0,...,2*GAMMA1 - 2} */ | |||
t[0] = GAMMA1 - 1 - a->coeffs[2 * i + 0]; | |||
t[0] += ((int32_t)t[0] >> 31) & Q; | |||
t[1] = GAMMA1 - 1 - a->coeffs[2 * i + 1]; | |||
t[1] += ((int32_t)t[1] >> 31) & Q; | |||
r[5 * i + 0] = t[0]; | |||
r[5 * i + 1] = t[0] >> 8; | |||
r[5 * i + 2] = t[0] >> 16; | |||
r[5 * i + 2] |= t[1] << 4; | |||
r[5 * i + 3] = t[1] >> 4; | |||
r[5 * i + 4] = t[1] >> 12; | |||
} | |||
} | |||
/************************************************* | |||
* Name: polyz_unpack | |||
* | |||
* Description: Unpack polynomial z with coefficients | |||
* in [-(GAMMA1 - 1), GAMMA1 - 1]. | |||
* Output coefficients are standard representatives. | |||
* | |||
* Arguments: - poly *r: pointer to output polynomial | |||
* - const unsigned char *a: byte array with bit-packed polynomial | |||
**************************************************/ | |||
void PQCLEAN_DILITHIUMIII_CLEAN_polyz_unpack(poly *r, const unsigned char *a) { | |||
unsigned int i; | |||
for (i = 0; i < N / 2; ++i) { | |||
r->coeffs[2 * i + 0] = a[5 * i + 0]; | |||
r->coeffs[2 * i + 0] |= (uint32_t)a[5 * i + 1] << 8; | |||
r->coeffs[2 * i + 0] |= (uint32_t)(a[5 * i + 2] & 0x0F) << 16; | |||
r->coeffs[2 * i + 1] = a[5 * i + 2] >> 4; | |||
r->coeffs[2 * i + 1] |= (uint32_t)a[5 * i + 3] << 4; | |||
r->coeffs[2 * i + 1] |= (uint32_t)a[5 * i + 4] << 12; | |||
r->coeffs[2 * i + 0] = GAMMA1 - 1 - r->coeffs[2 * i + 0]; | |||
r->coeffs[2 * i + 0] += ((int32_t)r->coeffs[2 * i + 0] >> 31) & Q; | |||
r->coeffs[2 * i + 1] = GAMMA1 - 1 - r->coeffs[2 * i + 1]; | |||
r->coeffs[2 * i + 1] += ((int32_t)r->coeffs[2 * i + 1] >> 31) & Q; | |||
} | |||
} | |||
/************************************************* | |||
* Name: polyw1_pack | |||
* | |||
* Description: Bit-pack polynomial w1 with coefficients in [0, 15]. | |||
* Input coefficients are assumed to be standard representatives. | |||
* | |||
* Arguments: - unsigned char *r: pointer to output byte array with at least | |||
* POLW1_SIZE_PACKED bytes | |||
* - const poly *a: pointer to input polynomial | |||
**************************************************/ | |||
void PQCLEAN_DILITHIUMIII_CLEAN_polyw1_pack(unsigned char *r, const poly *a) { | |||
unsigned int i; | |||
for (i = 0; i < N / 2; ++i) { | |||
r[i] = a->coeffs[2 * i + 0] | (a->coeffs[2 * i + 1] << 4); | |||
} | |||
} |
@@ -1,53 +0,0 @@ | |||
#ifndef POLY_H | |||
#define POLY_H | |||
#include "fips202.h" | |||
#include "params.h" | |||
#include <stdint.h> | |||
typedef struct { | |||
uint32_t coeffs[N]; | |||
} poly; | |||
void PQCLEAN_DILITHIUMIII_CLEAN_poly_reduce(poly *a); | |||
void PQCLEAN_DILITHIUMIII_CLEAN_poly_csubq(poly *a); | |||
void PQCLEAN_DILITHIUMIII_CLEAN_poly_freeze(poly *a); | |||
void PQCLEAN_DILITHIUMIII_CLEAN_poly_add(poly *c, const poly *a, const poly *b); | |||
void PQCLEAN_DILITHIUMIII_CLEAN_poly_sub(poly *c, const poly *a, const poly *b); | |||
void PQCLEAN_DILITHIUMIII_CLEAN_poly_neg(poly *a); | |||
void PQCLEAN_DILITHIUMIII_CLEAN_poly_shiftl(poly *a, unsigned int k); | |||
void PQCLEAN_DILITHIUMIII_CLEAN_poly_ntt(poly *a); | |||
void PQCLEAN_DILITHIUMIII_CLEAN_poly_invntt_montgomery(poly *a); | |||
void PQCLEAN_DILITHIUMIII_CLEAN_poly_pointwise_invmontgomery(poly *c, const poly *a, | |||
const poly *b); | |||
void PQCLEAN_DILITHIUMIII_CLEAN_poly_power2round(poly *a1, poly *a0, const poly *a); | |||
void PQCLEAN_DILITHIUMIII_CLEAN_poly_decompose(poly *a1, poly *a0, const poly *a); | |||
unsigned int PQCLEAN_DILITHIUMIII_CLEAN_poly_make_hint(poly *h, const poly *a, | |||
const poly *b); | |||
void PQCLEAN_DILITHIUMIII_CLEAN_poly_use_hint(poly *a, const poly *b, const poly *h); | |||
int PQCLEAN_DILITHIUMIII_CLEAN_poly_chknorm(const poly *a, uint32_t B); | |||
void PQCLEAN_DILITHIUMIII_CLEAN_poly_uniform(poly *a, const unsigned char *buf); | |||
void PQCLEAN_DILITHIUMIII_CLEAN_poly_uniform_eta(poly *a, | |||
const unsigned char seed[SEEDBYTES], | |||
unsigned char nonce); | |||
void PQCLEAN_DILITHIUMIII_CLEAN_poly_uniform_gamma1m1( | |||
poly *a, const unsigned char seed[SEEDBYTES + CRHBYTES], uint16_t nonce); | |||
void PQCLEAN_DILITHIUMIII_CLEAN_polyeta_pack(unsigned char *r, const poly *a); | |||
void PQCLEAN_DILITHIUMIII_CLEAN_polyeta_unpack(poly *r, const unsigned char *a); | |||
void PQCLEAN_DILITHIUMIII_CLEAN_polyt1_pack(unsigned char *r, const poly *a); | |||
void PQCLEAN_DILITHIUMIII_CLEAN_polyt1_unpack(poly *r, const unsigned char *a); | |||
void PQCLEAN_DILITHIUMIII_CLEAN_polyt0_pack(unsigned char *r, const poly *a); | |||
void PQCLEAN_DILITHIUMIII_CLEAN_polyt0_unpack(poly *r, const unsigned char *a); | |||
void PQCLEAN_DILITHIUMIII_CLEAN_polyz_pack(unsigned char *r, const poly *a); | |||
void PQCLEAN_DILITHIUMIII_CLEAN_polyz_unpack(poly *r, const unsigned char *a); | |||
void PQCLEAN_DILITHIUMIII_CLEAN_polyw1_pack(unsigned char *r, const poly *a); | |||
#endif |
@@ -1,367 +0,0 @@ | |||
#include "polyvec.h" | |||
#include "params.h" | |||
#include "poly.h" | |||
#include <stdint.h> | |||
/**************************************************************/ | |||
/************ Vectors of polynomials of length L **************/ | |||
/**************************************************************/ | |||
/************************************************* | |||
* Name: polyvecl_freeze | |||
* | |||
* Description: Reduce coefficients of polynomials in vector of length L | |||
* to standard representatives. | |||
* | |||
* Arguments: - polyvecl *v: pointer to input/output vector | |||
**************************************************/ | |||
void PQCLEAN_DILITHIUMIII_CLEAN_polyvecl_freeze(polyvecl *v) { | |||
unsigned int i; | |||
for (i = 0; i < L; ++i) { | |||
PQCLEAN_DILITHIUMIII_CLEAN_poly_freeze(v->vec + i); | |||
} | |||
} | |||
/************************************************* | |||
* Name: polyvecl_add | |||
* | |||
* Description: Add vectors of polynomials of length L. | |||
* No modular reduction is performed. | |||
* | |||
* Arguments: - polyvecl *w: pointer to output vector | |||
* - const polyvecl *u: pointer to first summand | |||
* - const polyvecl *v: pointer to second summand | |||
**************************************************/ | |||
void PQCLEAN_DILITHIUMIII_CLEAN_polyvecl_add(polyvecl *w, const polyvecl *u, | |||
const polyvecl *v) { | |||
unsigned int i; | |||
for (i = 0; i < L; ++i) { | |||
PQCLEAN_DILITHIUMIII_CLEAN_poly_add(w->vec + i, u->vec + i, v->vec + i); | |||
} | |||
} | |||
/************************************************* | |||
* Name: polyvecl_ntt | |||
* | |||
* Description: Forward NTT of all polynomials in vector of length L. Output | |||
* coefficients can be up to 16*Q larger than input coefficients. | |||
* | |||
* Arguments: - polyvecl *v: pointer to input/output vector | |||
**************************************************/ | |||
void PQCLEAN_DILITHIUMIII_CLEAN_polyvecl_ntt(polyvecl *v) { | |||
unsigned int i; | |||
for (i = 0; i < L; ++i) { | |||
PQCLEAN_DILITHIUMIII_CLEAN_poly_ntt(v->vec + i); | |||
} | |||
} | |||
/************************************************* | |||
* Name: polyvecl_pointwise_acc_invmontgomery | |||
* | |||
* Description: Pointwise multiply vectors of polynomials of length L, multiply | |||
* resulting vector by 2^{-32} and add (accumulate) polynomials | |||
* in it. Input/output vectors are in NTT domain representation. | |||
* Input coefficients are assumed to be less than 22*Q. Output | |||
* coeffcient are less than 2*L*Q. | |||
* | |||
* Arguments: - poly *w: output polynomial | |||
* - const polyvecl *u: pointer to first input vector | |||
* - const polyvecl *v: pointer to second input vector | |||
**************************************************/ | |||
void PQCLEAN_DILITHIUMIII_CLEAN_polyvecl_pointwise_acc_invmontgomery( | |||
poly *w, const polyvecl *u, const polyvecl *v) { | |||
unsigned int i; | |||
poly t; | |||
PQCLEAN_DILITHIUMIII_CLEAN_poly_pointwise_invmontgomery(w, u->vec + 0, | |||
v->vec + 0); | |||
for (i = 1; i < L; ++i) { | |||
PQCLEAN_DILITHIUMIII_CLEAN_poly_pointwise_invmontgomery(&t, u->vec + i, | |||
v->vec + i); | |||
PQCLEAN_DILITHIUMIII_CLEAN_poly_add(w, w, &t); | |||
} | |||
} | |||
/************************************************* | |||
* Name: polyvecl_chknorm | |||
* | |||
* Description: Check infinity norm of polynomials in vector of length L. | |||
* Assumes input coefficients to be standard representatives. | |||
* | |||
* Arguments: - const polyvecl *v: pointer to vector | |||
* - uint32_t B: norm bound | |||
* | |||
* Returns 0 if norm of all polynomials is strictly smaller than B and 1 | |||
* otherwise. | |||
**************************************************/ | |||
int PQCLEAN_DILITHIUMIII_CLEAN_polyvecl_chknorm(const polyvecl *v, uint32_t bound) { | |||
unsigned int i; | |||
int ret = 0; | |||
for (i = 0; i < L; ++i) { | |||
ret |= PQCLEAN_DILITHIUMIII_CLEAN_poly_chknorm(v->vec + i, bound); | |||
} | |||
return ret; | |||
} | |||
/**************************************************************/ | |||
/************ Vectors of polynomials of length K **************/ | |||
/**************************************************************/ | |||
/************************************************* | |||
* Name: polyveck_reduce | |||
* | |||
* Description: Reduce coefficients of polynomials in vector of length K | |||
* to representatives in [0,2*Q[. | |||
* | |||
* Arguments: - polyveck *v: pointer to input/output vector | |||
**************************************************/ | |||
void PQCLEAN_DILITHIUMIII_CLEAN_polyveck_reduce(polyveck *v) { | |||
unsigned int i; | |||
for (i = 0; i < K; ++i) { | |||
PQCLEAN_DILITHIUMIII_CLEAN_poly_reduce(v->vec + i); | |||
} | |||
} | |||
/************************************************* | |||
* Name: polyveck_csubq | |||
* | |||
* Description: For all coefficients of polynomials in vector of length K | |||
* subtract Q if coefficient is bigger than Q. | |||
* | |||
* Arguments: - polyveck *v: pointer to input/output vector | |||
**************************************************/ | |||
void PQCLEAN_DILITHIUMIII_CLEAN_polyveck_csubq(polyveck *v) { | |||
unsigned int i; | |||
for (i = 0; i < K; ++i) { | |||
PQCLEAN_DILITHIUMIII_CLEAN_poly_csubq(v->vec + i); | |||
} | |||
} | |||
/************************************************* | |||
* Name: polyveck_freeze | |||
* | |||
* Description: Reduce coefficients of polynomials in vector of length K | |||
* to standard representatives. | |||
* | |||
* Arguments: - polyveck *v: pointer to input/output vector | |||
**************************************************/ | |||
void PQCLEAN_DILITHIUMIII_CLEAN_polyveck_freeze(polyveck *v) { | |||
unsigned int i; | |||
for (i = 0; i < K; ++i) { | |||
PQCLEAN_DILITHIUMIII_CLEAN_poly_freeze(v->vec + i); | |||
} | |||
} | |||
/************************************************* | |||
* Name: polyveck_add | |||
* | |||
* Description: Add vectors of polynomials of length K. | |||
* No modular reduction is performed. | |||
* | |||
* Arguments: - polyveck *w: pointer to output vector | |||
* - const polyveck *u: pointer to first summand | |||
* - const polyveck *v: pointer to second summand | |||
**************************************************/ | |||
void PQCLEAN_DILITHIUMIII_CLEAN_polyveck_add(polyveck *w, const polyveck *u, | |||
const polyveck *v) { | |||
unsigned int i; | |||
for (i = 0; i < K; ++i) { | |||
PQCLEAN_DILITHIUMIII_CLEAN_poly_add(w->vec + i, u->vec + i, v->vec + i); | |||
} | |||
} | |||
/************************************************* | |||
* Name: polyveck_sub | |||
* | |||
* Description: Subtract vectors of polynomials of length K. | |||
* Assumes coefficients of polynomials in second input vector | |||
* to be less than 2*Q. No modular reduction is performed. | |||
* | |||
* Arguments: - polyveck *w: pointer to output vector | |||
* - const polyveck *u: pointer to first input vector | |||
* - const polyveck *v: pointer to second input vector to be | |||
* subtracted from first input vector | |||
**************************************************/ | |||
void PQCLEAN_DILITHIUMIII_CLEAN_polyveck_sub(polyveck *w, const polyveck *u, | |||
const polyveck *v) { | |||
unsigned int i; | |||
for (i = 0; i < K; ++i) { | |||
PQCLEAN_DILITHIUMIII_CLEAN_poly_sub(w->vec + i, u->vec + i, v->vec + i); | |||
} | |||
} | |||
/************************************************* | |||
* Name: polyveck_shiftl | |||
* | |||
* Description: Multiply vector of polynomials of Length K by 2^k without | |||
*modular reduction. Assumes input coefficients to be less than 2^{32-k}. | |||
* | |||
* Arguments: - polyveck *v: pointer to input/output vector | |||
* - unsigned int k: exponent | |||
**************************************************/ | |||
void PQCLEAN_DILITHIUMIII_CLEAN_polyveck_shiftl(polyveck *v, unsigned int k) { | |||
unsigned int i; | |||
for (i = 0; i < K; ++i) { | |||
PQCLEAN_DILITHIUMIII_CLEAN_poly_shiftl(v->vec + i, k); | |||
} | |||
} | |||
/************************************************* | |||
* Name: polyveck_ntt | |||
* | |||
* Description: Forward NTT of all polynomials in vector of length K. Output | |||
* coefficients can be up to 16*Q larger than input coefficients. | |||
* | |||
* Arguments: - polyveck *v: pointer to input/output vector | |||
**************************************************/ | |||
void PQCLEAN_DILITHIUMIII_CLEAN_polyveck_ntt(polyveck *v) { | |||
unsigned int i; | |||
for (i = 0; i < K; ++i) { | |||
PQCLEAN_DILITHIUMIII_CLEAN_poly_ntt(v->vec + i); | |||
} | |||
} | |||
/************************************************* | |||
* Name: polyveck_invntt_montgomery | |||
* | |||
* Description: Inverse NTT and multiplication by 2^{32} of polynomials | |||
* in vector of length K. Input coefficients need to be less | |||
* than 2*Q. | |||
* | |||
* Arguments: - polyveck *v: pointer to input/output vector | |||
**************************************************/ | |||
void PQCLEAN_DILITHIUMIII_CLEAN_polyveck_invntt_montgomery(polyveck *v) { | |||
unsigned int i; | |||
for (i = 0; i < K; ++i) { | |||
PQCLEAN_DILITHIUMIII_CLEAN_poly_invntt_montgomery(v->vec + i); | |||
} | |||
} | |||
/************************************************* | |||
* Name: polyveck_chknorm | |||
* | |||
* Description: Check infinity norm of polynomials in vector of length K. | |||
* Assumes input coefficients to be standard representatives. | |||
* | |||
* Arguments: - const polyveck *v: pointer to vector | |||
* - uint32_t B: norm bound | |||
* | |||
* Returns 0 if norm of all polynomials are strictly smaller than B and 1 | |||
* otherwise. | |||
**************************************************/ | |||
int PQCLEAN_DILITHIUMIII_CLEAN_polyveck_chknorm(const polyveck *v, uint32_t bound) { | |||
unsigned int i; | |||
int ret = 0; | |||
for (i = 0; i < K; ++i) { | |||
ret |= PQCLEAN_DILITHIUMIII_CLEAN_poly_chknorm(v->vec + i, bound); | |||
} | |||
return ret; | |||
} | |||
/************************************************* | |||
* Name: polyveck_power2round | |||
* | |||
* Description: For all coefficients a of polynomials in vector of length K, | |||
* compute a0, a1 such that a mod Q = a1*2^D + a0 | |||
* with -2^{D-1} < a0 <= 2^{D-1}. Assumes coefficients to be | |||
* standard representatives. | |||
* | |||
* Arguments: - polyveck *v1: pointer to output vector of polynomials with | |||
* coefficients a1 | |||
* - polyveck *v0: pointer to output vector of polynomials with | |||
* coefficients Q + a0 | |||
* - const polyveck *v: pointer to input vector | |||
**************************************************/ | |||
void PQCLEAN_DILITHIUMIII_CLEAN_polyveck_power2round(polyveck *v1, polyveck *v0, | |||
const polyveck *v) { | |||
unsigned int i; | |||
for (i = 0; i < K; ++i) { | |||
PQCLEAN_DILITHIUMIII_CLEAN_poly_power2round(v1->vec + i, v0->vec + i, | |||
v->vec + i); | |||
} | |||
} | |||
/************************************************* | |||
* Name: polyveck_decompose | |||
* | |||
* Description: For all coefficients a of polynomials in vector of length K, | |||
* compute high and low bits a0, a1 such a mod Q = a1*ALPHA + a0 | |||
* with -ALPHA/2 < a0 <= ALPHA/2 except a1 = (Q-1)/ALPHA where we | |||
* set a1 = 0 and -ALPHA/2 <= a0 = a mod Q - Q < 0. | |||
* Assumes coefficients to be standard representatives. | |||
* | |||
* Arguments: - polyveck *v1: pointer to output vector of polynomials with | |||
* coefficients a1 | |||
* - polyveck *v0: pointer to output vector of polynomials with | |||
* coefficients Q + a0 | |||
* - const polyveck *v: pointer to input vector | |||
**************************************************/ | |||
void PQCLEAN_DILITHIUMIII_CLEAN_polyveck_decompose(polyveck *v1, polyveck *v0, | |||
const polyveck *v) { | |||
unsigned int i; | |||
for (i = 0; i < K; ++i) { | |||
PQCLEAN_DILITHIUMIII_CLEAN_poly_decompose(v1->vec + i, v0->vec + i, | |||
v->vec + i); | |||
} | |||
} | |||
/************************************************* | |||
* Name: polyveck_make_hint | |||
* | |||
* Description: Compute hint vector. | |||
* | |||
* Arguments: - polyveck *h: pointer to output vector | |||
* - const polyveck *u: pointer to first input vector | |||
* - const polyveck *u: pointer to second input vector | |||
* | |||
* Returns number of 1 bits. | |||
**************************************************/ | |||
unsigned int PQCLEAN_DILITHIUMIII_CLEAN_polyveck_make_hint(polyveck *h, | |||
const polyveck *u, | |||
const polyveck *v) { | |||
unsigned int i, s = 0; | |||
for (i = 0; i < K; ++i) { | |||
s += PQCLEAN_DILITHIUMIII_CLEAN_poly_make_hint(h->vec + i, u->vec + i, | |||
v->vec + i); | |||
} | |||
return s; | |||
} | |||
/************************************************* | |||
* Name: polyveck_use_hint | |||
* | |||
* Description: Use hint vector to correct the high bits of input vector. | |||
* | |||
* Arguments: - polyveck *w: pointer to output vector of polynomials with | |||
* corrected high bits | |||
* - const polyveck *u: pointer to input vector | |||
* - const polyveck *h: pointer to input hint vector | |||
**************************************************/ | |||
void PQCLEAN_DILITHIUMIII_CLEAN_polyveck_use_hint(polyveck *w, const polyveck *u, | |||
const polyveck *h) { | |||
unsigned int i; | |||
for (i = 0; i < K; ++i) { | |||
PQCLEAN_DILITHIUMIII_CLEAN_poly_use_hint(w->vec + i, u->vec + i, h->vec + i); | |||
} | |||
} |
@@ -1,54 +0,0 @@ | |||
#ifndef POLYVEC_H | |||
#define POLYVEC_H | |||
#include "params.h" | |||
#include "poly.h" | |||
#include <stdint.h> | |||
/* Vectors of polynomials of length L */ | |||
typedef struct { | |||
poly vec[L]; | |||
} polyvecl; | |||
void PQCLEAN_DILITHIUMIII_CLEAN_polyvecl_freeze(polyvecl *v); | |||
void PQCLEAN_DILITHIUMIII_CLEAN_polyvecl_add(polyvecl *w, const polyvecl *u, | |||
const polyvecl *v); | |||
void PQCLEAN_DILITHIUMIII_CLEAN_polyvecl_ntt(polyvecl *v); | |||
void PQCLEAN_DILITHIUMIII_CLEAN_polyvecl_pointwise_acc_invmontgomery( | |||
poly *w, const polyvecl *u, const polyvecl *v); | |||
int PQCLEAN_DILITHIUMIII_CLEAN_polyvecl_chknorm(const polyvecl *v, uint32_t bound); | |||
/* Vectors of polynomials of length K */ | |||
typedef struct { | |||
poly vec[K]; | |||
} polyveck; | |||
void PQCLEAN_DILITHIUMIII_CLEAN_polyveck_reduce(polyveck *v); | |||
void PQCLEAN_DILITHIUMIII_CLEAN_polyveck_csubq(polyveck *v); | |||
void PQCLEAN_DILITHIUMIII_CLEAN_polyveck_freeze(polyveck *v); | |||
void PQCLEAN_DILITHIUMIII_CLEAN_polyveck_add(polyveck *w, const polyveck *u, | |||
const polyveck *v); | |||
void PQCLEAN_DILITHIUMIII_CLEAN_polyveck_sub(polyveck *w, const polyveck *u, | |||
const polyveck *v); | |||
void PQCLEAN_DILITHIUMIII_CLEAN_polyveck_shiftl(polyveck *v, unsigned int k); | |||
void PQCLEAN_DILITHIUMIII_CLEAN_polyveck_ntt(polyveck *v); | |||
void PQCLEAN_DILITHIUMIII_CLEAN_polyveck_invntt_montgomery(polyveck *v); | |||
int PQCLEAN_DILITHIUMIII_CLEAN_polyveck_chknorm(const polyveck *v, uint32_t bound); | |||
void PQCLEAN_DILITHIUMIII_CLEAN_polyveck_power2round(polyveck *v1, polyveck *v0, | |||
const polyveck *v); | |||
void PQCLEAN_DILITHIUMIII_CLEAN_polyveck_decompose(polyveck *v1, polyveck *v0, | |||
const polyveck *v); | |||
unsigned int PQCLEAN_DILITHIUMIII_CLEAN_polyveck_make_hint(polyveck *h, | |||
const polyveck *u, | |||
const polyveck *v); | |||
void PQCLEAN_DILITHIUMIII_CLEAN_polyveck_use_hint(polyveck *w, const polyveck *u, | |||
const polyveck *h); | |||
#endif |
@@ -1,74 +0,0 @@ | |||
#include "reduce.h" | |||
#include "params.h" | |||
#include <stdint.h> | |||
/************************************************* | |||
* Name: montgomery_reduce | |||
* | |||
* Description: For finite field element a with 0 <= a <= Q*2^32, | |||
* compute r \equiv a*2^{-32} (mod Q) such that 0 <= r < 2*Q. | |||
* | |||
* Arguments: - uint64_t: finite field element a | |||
* | |||
* Returns r. | |||
**************************************************/ | |||
uint32_t PQCLEAN_DILITHIUMIII_CLEAN_montgomery_reduce(uint64_t a) { | |||
uint64_t t; | |||
t = a * QINV; | |||
t &= (1ULL << 32) - 1; | |||
t *= Q; | |||
t = a + t; | |||
t >>= 32; | |||
return t; | |||
} | |||
/************************************************* | |||
* Name: reduce32 | |||
* | |||
* Description: For finite field element a, compute r \equiv a (mod Q) | |||
* such that 0 <= r < 2*Q. | |||
* | |||
* Arguments: - uint32_t: finite field element a | |||
* | |||
* Returns r. | |||
**************************************************/ | |||
uint32_t PQCLEAN_DILITHIUMIII_CLEAN_reduce32(uint32_t a) { | |||
uint32_t t; | |||
t = a & 0x7FFFFF; | |||
a >>= 23; | |||
t += (a << 13) - a; | |||
return t; | |||
} | |||
/************************************************* | |||
* Name: csubq | |||
* | |||
* Description: Subtract Q if input coefficient is bigger than Q. | |||
* | |||
* Arguments: - uint32_t: finite field element a | |||
* | |||
* Returns r. | |||
**************************************************/ | |||
uint32_t PQCLEAN_DILITHIUMIII_CLEAN_csubq(uint32_t a) { | |||
a -= Q; | |||
a += ((int32_t)a >> 31) & Q; | |||
return a; | |||
} | |||
/************************************************* | |||
* Name: freeze | |||
* | |||
* Description: For finite field element a, compute standard | |||
* representative r = a mod Q. | |||
* | |||
* Arguments: - uint32_t: finite field element a | |||
* | |||
* Returns r. | |||
**************************************************/ | |||
uint32_t PQCLEAN_DILITHIUMIII_CLEAN_freeze(uint32_t a) { | |||
a = PQCLEAN_DILITHIUMIII_CLEAN_reduce32(a); | |||
a = PQCLEAN_DILITHIUMIII_CLEAN_csubq(a); | |||
return a; | |||
} |
@@ -1,21 +0,0 @@ | |||
#ifndef REDUCE_H | |||
#define REDUCE_H | |||
#include <stdint.h> | |||
#define MONT 4193792U // 2^32 % Q | |||
#define QINV 4236238847U // -q^(-1) mod 2^32 | |||
/* a <= Q*2^32 => r < 2*Q */ | |||
uint32_t PQCLEAN_DILITHIUMIII_CLEAN_montgomery_reduce(uint64_t a); | |||
/* r < 2*Q */ | |||
uint32_t PQCLEAN_DILITHIUMIII_CLEAN_reduce32(uint32_t a); | |||
/* a < 2*Q => r < Q */ | |||
uint32_t PQCLEAN_DILITHIUMIII_CLEAN_csubq(uint32_t a); | |||
/* r < Q */ | |||
uint32_t PQCLEAN_DILITHIUMIII_CLEAN_freeze(uint32_t a); | |||
#endif |
@@ -1,119 +0,0 @@ | |||
#include "params.h" | |||
#include <stdint.h> | |||
/************************************************* | |||
* Name: power2round | |||
* | |||
* Description: For finite field element a, compute a0, a1 such that | |||
* a mod Q = a1*2^D + a0 with -2^{D-1} < a0 <= 2^{D-1}. | |||
* Assumes a to be standard representative. | |||
* | |||
* Arguments: - uint32_t a: input element | |||
* - uint32_t *a0: pointer to output element Q + a0 | |||
* | |||
* Returns a1. | |||
**************************************************/ | |||
uint32_t PQCLEAN_DILITHIUMIII_CLEAN_power2round(uint32_t a, uint32_t *a0) { | |||
int32_t t; | |||
/* Centralized remainder mod 2^D */ | |||
t = a & ((1 << D) - 1); | |||
t -= (1 << (D - 1)) + 1; | |||
t += (t >> 31) & (1 << D); | |||
t -= (1 << (D - 1)) - 1; | |||
*a0 = Q + t; | |||
a = (a - t) >> D; | |||
return a; | |||
} | |||
/************************************************* | |||
* Name: decompose | |||
* | |||
* Description: For finite field element a, compute high and low bits a0, a1 | |||
*such that a mod Q = a1*ALPHA + a0 with -ALPHA/2 < a0 <= ALPHA/2 except if a1 = | |||
*(Q-1)/ALPHA where we set a1 = 0 and -ALPHA/2 <= a0 = a mod Q - Q < 0. Assumes | |||
*a to be standard representative. | |||
* | |||
* Arguments: - uint32_t a: input element | |||
* - uint32_t *a0: pointer to output element Q + a0 | |||
* | |||
* Returns a1. | |||
**************************************************/ | |||
uint32_t PQCLEAN_DILITHIUMIII_CLEAN_decompose(uint32_t a, uint32_t *a0) { | |||
#if ALPHA != (Q - 1) / 16 | |||
#error "decompose assumes ALPHA == (Q-1)/16" | |||
#endif | |||
int32_t t, u; | |||
/* Centralized remainder mod ALPHA */ | |||
t = a & 0x7FFFF; | |||
t += (a >> 19) << 9; | |||
t -= ALPHA / 2 + 1; | |||
t += (t >> 31) & ALPHA; | |||
t -= ALPHA / 2 - 1; | |||
a -= t; | |||
/* Divide by ALPHA (possible to avoid) */ | |||
u = a - 1; | |||
u >>= 31; | |||
a = (a >> 19) + 1; | |||
a -= u & 1; | |||
/* Border case */ | |||
*a0 = Q + t - (a >> 4); | |||
a &= 0xF; | |||
return a; | |||
} | |||
/************************************************* | |||
* Name: make_hint | |||
* | |||
* Description: Compute hint bit indicating whether or not high bits of two | |||
* finite field elements differ. Assumes input elements to be | |||
* standard representatives. | |||
* | |||
* Arguments: - uint32_t a: first input element | |||
* - uint32_t b: second input element | |||
* | |||
* Returns 1 if high bits of a and b differ and 0 otherwise. | |||
**************************************************/ | |||
unsigned int PQCLEAN_DILITHIUMIII_CLEAN_make_hint(const uint32_t a, | |||
const uint32_t b) { | |||
uint32_t t; | |||
return PQCLEAN_DILITHIUMIII_CLEAN_decompose(a, &t) != | |||
PQCLEAN_DILITHIUMIII_CLEAN_decompose(b, &t); | |||
} | |||
/************************************************* | |||
* Name: use_hint | |||
* | |||
* Description: Correct high bits according to hint. | |||
* | |||
* Arguments: - uint32_t a: input element | |||
* - unsigned int hint: hint bit | |||
* | |||
* Returns corrected high bits. | |||
**************************************************/ | |||
uint32_t PQCLEAN_DILITHIUMIII_CLEAN_use_hint(const uint32_t a, | |||
const unsigned int hint) { | |||
uint32_t a0, a1; | |||
a1 = PQCLEAN_DILITHIUMIII_CLEAN_decompose(a, &a0); | |||
if (hint == 0) { | |||
return a1; | |||
} | |||
if (a0 > Q) { | |||
return (a1 + 1) & 0xF; | |||
} | |||
return (a1 - 1) & 0xF; | |||
/* If decompose does not divide out ALPHA: | |||
if(hint == 0) | |||
return a1; | |||
else if(a0 > Q) | |||
return (a1 + ALPHA) % (Q - 1); | |||
else | |||
return (a1 - ALPHA) % (Q - 1); | |||
*/ | |||
} |
@@ -1,11 +0,0 @@ | |||
#ifndef ROUNDING_H | |||
#define ROUNDING_H | |||
#include <stdint.h> | |||
uint32_t PQCLEAN_DILITHIUMIII_CLEAN_power2round(uint32_t a, uint32_t *a0); | |||
uint32_t PQCLEAN_DILITHIUMIII_CLEAN_decompose(uint32_t a, uint32_t *a0); | |||
unsigned int PQCLEAN_DILITHIUMIII_CLEAN_make_hint(uint32_t a, uint32_t b); | |||
uint32_t PQCLEAN_DILITHIUMIII_CLEAN_use_hint(uint32_t a, unsigned int hint); | |||
#endif |
@@ -1,402 +0,0 @@ | |||
#include "sign.h" | |||
#include "fips202.h" | |||
#include "packing.h" | |||
#include "params.h" | |||
#include "poly.h" | |||
#include "polyvec.h" | |||
#include "randombytes.h" | |||
#include <stdint.h> | |||
/************************************************* | |||
* Name: expand_mat | |||
* | |||
* Description: Implementation of ExpandA. Generates matrix A with uniformly | |||
* random coefficients a_{i,j} by performing rejection | |||
* sampling on the output stream of SHAKE128(rho|i|j). | |||
* | |||
* Arguments: - polyvecl mat[K]: output matrix | |||
* - const unsigned char rho[]: byte array containing seed rho | |||
**************************************************/ | |||
void PQCLEAN_DILITHIUMIII_CLEAN_expand_mat(polyvecl mat[K], | |||
const unsigned char rho[SEEDBYTES]) { | |||
unsigned int i, j; | |||
unsigned char inbuf[SEEDBYTES + 1]; | |||
/* Don't change this to smaller values, | |||
* sampling later assumes sufficient SHAKE output! | |||
* Probability that we need more than 5 blocks: < 2^{-132}. | |||
* Probability that we need more than 6 blocks: < 2^{-546}. */ | |||
unsigned char outbuf[5 * SHAKE128_RATE]; | |||
for (i = 0; i < SEEDBYTES; ++i) { | |||
inbuf[i] = rho[i]; | |||
} | |||
for (i = 0; i < K; ++i) { | |||
for (j = 0; j < L; ++j) { | |||
inbuf[SEEDBYTES] = i + (j << 4); | |||
shake128(outbuf, sizeof(outbuf), inbuf, SEEDBYTES + 1); | |||
PQCLEAN_DILITHIUMIII_CLEAN_poly_uniform(mat[i].vec + j, outbuf); | |||
} | |||
} | |||
} | |||
/************************************************* | |||
* Name: challenge | |||
* | |||
* Description: Implementation of H. Samples polynomial with 60 nonzero | |||
* coefficients in {-1,1} using the output stream of | |||
* SHAKE256(mu|w1). | |||
* | |||
* Arguments: - poly *c: pointer to output polynomial | |||
* - const unsigned char mu[]: byte array containing mu | |||
* - const polyveck *w1: pointer to vector w1 | |||
**************************************************/ | |||
void PQCLEAN_DILITHIUMIII_CLEAN_challenge(poly *c, const unsigned char mu[CRHBYTES], | |||
const polyveck *w1) { | |||
unsigned int i, b, pos; | |||
unsigned char inbuf[CRHBYTES + K * POLW1_SIZE_PACKED]; | |||
unsigned char outbuf[SHAKE256_RATE]; | |||
uint64_t state[25], signs, mask; | |||
for (i = 0; i < CRHBYTES; ++i) { | |||
inbuf[i] = mu[i]; | |||
} | |||
for (i = 0; i < K; ++i) { | |||
PQCLEAN_DILITHIUMIII_CLEAN_polyw1_pack( | |||
inbuf + CRHBYTES + i * POLW1_SIZE_PACKED, w1->vec + i); | |||
} | |||
shake256_absorb(state, inbuf, sizeof(inbuf)); | |||
shake256_squeezeblocks(outbuf, 1, state); | |||
signs = 0; | |||
for (i = 0; i < 8; ++i) { | |||
signs |= (uint64_t)outbuf[i] << 8 * i; | |||
} | |||
pos = 8; | |||
mask = 1; | |||
for (i = 0; i < N; ++i) { | |||
c->coeffs[i] = 0; | |||
} | |||
for (i = 196; i < 256; ++i) { | |||
do { | |||
if (pos >= SHAKE256_RATE) { | |||
shake256_squeezeblocks(outbuf, 1, state); | |||
pos = 0; | |||
} | |||
b = outbuf[pos++]; | |||
} while (b > i); | |||
c->coeffs[i] = c->coeffs[b]; | |||
c->coeffs[b] = (signs & mask) ? Q - 1 : 1; | |||
mask <<= 1; | |||
} | |||
} | |||
/************************************************* | |||
* Name: crypto_sign_keypair | |||
* | |||
* Description: Generates public and private key. | |||
* | |||
* Arguments: - uint8_t *pk: pointer to output public key (allocated | |||
* array of CRYPTO_PUBLICKEYBYTES bytes) | |||
* - uint8_t *sk: pointer to output private key (allocated | |||
* array of CRYPTO_SECRETKEYBYTES bytes) | |||
* | |||
* Returns 0 (success) | |||
**************************************************/ | |||
int PQCLEAN_DILITHIUMIII_CLEAN_crypto_sign_keypair(uint8_t *pk, | |||
uint8_t *sk) { | |||
unsigned int i; | |||
unsigned char seedbuf[3 * SEEDBYTES]; | |||
unsigned char tr[CRHBYTES]; | |||
unsigned char *rho, *rhoprime, *key; | |||
uint16_t nonce = 0; | |||
polyvecl mat[K]; | |||
polyvecl s1, s1hat; | |||
polyveck s2, t, t1, t0; | |||
/* Expand 32 bytes of randomness into rho, rhoprime and key */ | |||
randombytes(seedbuf, SEEDBYTES); | |||
shake256(seedbuf, 3 * SEEDBYTES, seedbuf, SEEDBYTES); | |||
rho = seedbuf; | |||
rhoprime = rho + SEEDBYTES; | |||
key = rho + 2 * SEEDBYTES; | |||
/* Expand matrix */ | |||
PQCLEAN_DILITHIUMIII_CLEAN_expand_mat(mat, rho); | |||
/* Sample short vectors s1 and s2 */ | |||
for (i = 0; i < L; ++i) { | |||
PQCLEAN_DILITHIUMIII_CLEAN_poly_uniform_eta(s1.vec + i, rhoprime, nonce++); | |||
} | |||
for (i = 0; i < K; ++i) { | |||
PQCLEAN_DILITHIUMIII_CLEAN_poly_uniform_eta(s2.vec + i, rhoprime, nonce++); | |||
} | |||
/* Matrix-vector multiplication */ | |||
s1hat = s1; | |||
PQCLEAN_DILITHIUMIII_CLEAN_polyvecl_ntt(&s1hat); | |||
for (i = 0; i < K; ++i) { | |||
PQCLEAN_DILITHIUMIII_CLEAN_polyvecl_pointwise_acc_invmontgomery( | |||
t.vec + i, mat + i, &s1hat); | |||
PQCLEAN_DILITHIUMIII_CLEAN_poly_reduce(t.vec + i); | |||
PQCLEAN_DILITHIUMIII_CLEAN_poly_invntt_montgomery(t.vec + i); | |||
} | |||
/* Add noise vector s2 */ | |||
PQCLEAN_DILITHIUMIII_CLEAN_polyveck_add(&t, &t, &s2); | |||
/* Extract t1 and write public key */ | |||
PQCLEAN_DILITHIUMIII_CLEAN_polyveck_freeze(&t); | |||
PQCLEAN_DILITHIUMIII_CLEAN_polyveck_power2round(&t1, &t0, &t); | |||
PQCLEAN_DILITHIUMIII_CLEAN_pack_pk(pk, rho, &t1); | |||
/* Compute CRH(rho, t1) and write secret key */ | |||
shake256(tr, CRHBYTES, pk, CRYPTO_PUBLICKEYBYTES); | |||
PQCLEAN_DILITHIUMIII_CLEAN_pack_sk(sk, rho, key, tr, &s1, &s2, &t0); | |||
return 0; | |||
} | |||
/************************************************* | |||
* Name: crypto_sign | |||
* | |||
* Description: Compute signed message. | |||
* | |||
* Arguments: - uint8_t *sm: pointer to output signed message (allocated | |||
* array with CRYPTO_BYTES + mlen bytes), | |||
* can be equal to m | |||
* - size_t *smlen: pointer to output length of signed | |||
* message | |||
* - const uint8_t *m: pointer to message to be signed | |||
* - size_t mlen: length of message | |||
* - const uint8_t *sk: pointer to bit-packed secret key | |||
* | |||
* Returns 0 (success) | |||
**************************************************/ | |||
int PQCLEAN_DILITHIUMIII_CLEAN_crypto_sign(uint8_t *sm, | |||
size_t *smlen, | |||
const uint8_t *m, | |||
size_t mlen, | |||
const uint8_t *sk) { | |||
unsigned long long i, j; | |||
unsigned int n; | |||
unsigned char seedbuf[2 * SEEDBYTES + CRHBYTES]; // TODO(thom): nonce in seedbuf (2x) | |||
unsigned char tr[CRHBYTES]; | |||
unsigned char *rho, *key, *mu; | |||
uint16_t nonce = 0; | |||
poly c, chat; | |||
polyvecl mat[K], s1, y, yhat, z; | |||
polyveck s2, t0, w, w1; | |||
polyveck h, wcs2, wcs20, ct0, tmp; | |||
rho = seedbuf; | |||
key = seedbuf + SEEDBYTES; | |||
mu = seedbuf + 2 * SEEDBYTES; | |||
PQCLEAN_DILITHIUMIII_CLEAN_unpack_sk(rho, key, tr, &s1, &s2, &t0, sk); | |||
/* Copy tr and message into the sm buffer, | |||
* backwards since m and sm can be equal in SUPERCOP API */ | |||
for (i = 1; i <= mlen; ++i) { | |||
sm[CRYPTO_BYTES + mlen - i] = m[mlen - i]; | |||
} | |||
for (i = 0; i < CRHBYTES; ++i) { | |||
sm[CRYPTO_BYTES - CRHBYTES + i] = tr[i]; | |||
} | |||
/* Compute CRH(tr, msg) */ | |||
shake256(mu, CRHBYTES, sm + CRYPTO_BYTES - CRHBYTES, CRHBYTES + mlen); | |||
/* Expand matrix and transform vectors */ | |||
PQCLEAN_DILITHIUMIII_CLEAN_expand_mat(mat, rho); | |||
PQCLEAN_DILITHIUMIII_CLEAN_polyvecl_ntt(&s1); | |||
PQCLEAN_DILITHIUMIII_CLEAN_polyveck_ntt(&s2); | |||
PQCLEAN_DILITHIUMIII_CLEAN_polyveck_ntt(&t0); | |||
rej: | |||
/* Sample intermediate vector y */ | |||
for (i = 0; i < L; ++i) { | |||
PQCLEAN_DILITHIUMIII_CLEAN_poly_uniform_gamma1m1(y.vec + i, key, nonce++); | |||
} | |||
/* Matrix-vector multiplication */ | |||
yhat = y; | |||
PQCLEAN_DILITHIUMIII_CLEAN_polyvecl_ntt(&yhat); | |||
for (i = 0; i < K; ++i) { | |||
PQCLEAN_DILITHIUMIII_CLEAN_polyvecl_pointwise_acc_invmontgomery( | |||
w.vec + i, mat + i, &yhat); | |||
PQCLEAN_DILITHIUMIII_CLEAN_poly_reduce(w.vec + i); | |||
PQCLEAN_DILITHIUMIII_CLEAN_poly_invntt_montgomery(w.vec + i); | |||
} | |||
/* Decompose w and call the random oracle */ | |||
PQCLEAN_DILITHIUMIII_CLEAN_polyveck_csubq(&w); | |||
PQCLEAN_DILITHIUMIII_CLEAN_polyveck_decompose(&w1, &tmp, &w); | |||
PQCLEAN_DILITHIUMIII_CLEAN_challenge(&c, mu, &w1); | |||
/* Compute z, reject if it reveals secret */ | |||
chat = c; | |||
PQCLEAN_DILITHIUMIII_CLEAN_poly_ntt(&chat); | |||
for (i = 0; i < L; ++i) { | |||
PQCLEAN_DILITHIUMIII_CLEAN_poly_pointwise_invmontgomery(z.vec + i, &chat, | |||
s1.vec + i); | |||
PQCLEAN_DILITHIUMIII_CLEAN_poly_invntt_montgomery(z.vec + i); | |||
} | |||
PQCLEAN_DILITHIUMIII_CLEAN_polyvecl_add(&z, &z, &y); | |||
PQCLEAN_DILITHIUMIII_CLEAN_polyvecl_freeze(&z); | |||
if (PQCLEAN_DILITHIUMIII_CLEAN_polyvecl_chknorm(&z, GAMMA1 - BETA)) { | |||
goto rej; | |||
} | |||
/* Compute w - cs2, reject if w1 can not be computed from it */ | |||
for (i = 0; i < K; ++i) { | |||
PQCLEAN_DILITHIUMIII_CLEAN_poly_pointwise_invmontgomery(wcs2.vec + i, &chat, | |||
s2.vec + i); | |||
PQCLEAN_DILITHIUMIII_CLEAN_poly_invntt_montgomery(wcs2.vec + i); | |||
} | |||
PQCLEAN_DILITHIUMIII_CLEAN_polyveck_sub(&wcs2, &w, &wcs2); | |||
PQCLEAN_DILITHIUMIII_CLEAN_polyveck_freeze(&wcs2); | |||
PQCLEAN_DILITHIUMIII_CLEAN_polyveck_decompose(&tmp, &wcs20, &wcs2); | |||
PQCLEAN_DILITHIUMIII_CLEAN_polyveck_csubq(&wcs20); | |||
if (PQCLEAN_DILITHIUMIII_CLEAN_polyveck_chknorm(&wcs20, GAMMA2 - BETA)) { | |||
goto rej; | |||
} | |||
for (i = 0; i < K; ++i) { | |||
for (j = 0; j < N; ++j) { | |||
if (tmp.vec[i].coeffs[j] != w1.vec[i].coeffs[j]) { | |||
goto rej; | |||
} | |||
} | |||
} | |||
/* Compute hints for w1 */ | |||
for (i = 0; i < K; ++i) { | |||
PQCLEAN_DILITHIUMIII_CLEAN_poly_pointwise_invmontgomery(ct0.vec + i, &chat, | |||
t0.vec + i); | |||
PQCLEAN_DILITHIUMIII_CLEAN_poly_invntt_montgomery(ct0.vec + i); | |||
} | |||
PQCLEAN_DILITHIUMIII_CLEAN_polyveck_csubq(&ct0); | |||
if (PQCLEAN_DILITHIUMIII_CLEAN_polyveck_chknorm(&ct0, GAMMA2)) { | |||
goto rej; | |||
} | |||
PQCLEAN_DILITHIUMIII_CLEAN_polyveck_add(&tmp, &wcs2, &ct0); | |||
PQCLEAN_DILITHIUMIII_CLEAN_polyveck_csubq(&tmp); | |||
n = PQCLEAN_DILITHIUMIII_CLEAN_polyveck_make_hint(&h, &wcs2, &tmp); | |||
if (n > OMEGA) { | |||
goto rej; | |||
} | |||
/* Write signature */ | |||
PQCLEAN_DILITHIUMIII_CLEAN_pack_sig(sm, &z, &h, &c); | |||
*smlen = mlen + CRYPTO_BYTES; | |||
return 0; | |||
} | |||
/************************************************* | |||
* Name: crypto_sign_open | |||
* | |||
* Description: Verify signed message. | |||
* | |||
* Arguments: - uint8_t *m: pointer to output message (allocated | |||
* array with smlen bytes), can be equal to sm | |||
* - size_t *mlen: pointer to output length of message | |||
* - const uint8_t *sm: pointer to signed message | |||
* - size_t smlen: length of signed message | |||
* - const uint8_t *sk: pointer to bit-packed public key | |||
* | |||
* Returns 0 if signed message could be verified correctly and -1 otherwise | |||
**************************************************/ | |||
int PQCLEAN_DILITHIUMIII_CLEAN_crypto_sign_open(uint8_t *m, | |||
size_t *mlen, | |||
const uint8_t *sm, | |||
size_t smlen, | |||
const uint8_t *pk) { | |||
unsigned long long i; | |||
unsigned char rho[SEEDBYTES]; | |||
unsigned char mu[CRHBYTES]; | |||
poly c, chat, cp; | |||
polyvecl mat[K], z; | |||
polyveck t1, w1, h, tmp1, tmp2; | |||
if (smlen < CRYPTO_BYTES) { | |||
goto badsig; | |||
} | |||
*mlen = smlen - CRYPTO_BYTES; | |||
PQCLEAN_DILITHIUMIII_CLEAN_unpack_pk(rho, &t1, pk); | |||
if (PQCLEAN_DILITHIUMIII_CLEAN_unpack_sig(&z, &h, &c, sm)) { | |||
goto badsig; | |||
} | |||
if (PQCLEAN_DILITHIUMIII_CLEAN_polyvecl_chknorm(&z, GAMMA1 - BETA)) { | |||
goto badsig; | |||
} | |||
/* Compute CRH(CRH(rho, t1), msg) using m as "playground" buffer */ | |||
if (sm != m) { | |||
for (i = 0; i < *mlen; ++i) { | |||
m[CRYPTO_BYTES + i] = sm[CRYPTO_BYTES + i]; | |||
} | |||
} | |||
shake256(m + CRYPTO_BYTES - CRHBYTES, CRHBYTES, pk, CRYPTO_PUBLICKEYBYTES); | |||
shake256(mu, CRHBYTES, m + CRYPTO_BYTES - CRHBYTES, CRHBYTES + *mlen); | |||
/* Matrix-vector multiplication; compute Az - c2^dt1 */ | |||
PQCLEAN_DILITHIUMIII_CLEAN_expand_mat(mat, rho); | |||
PQCLEAN_DILITHIUMIII_CLEAN_polyvecl_ntt(&z); | |||
for (i = 0; i < K; ++i) { | |||
PQCLEAN_DILITHIUMIII_CLEAN_polyvecl_pointwise_acc_invmontgomery(tmp1.vec + i, | |||
mat + i, &z); | |||
} | |||
chat = c; | |||
PQCLEAN_DILITHIUMIII_CLEAN_poly_ntt(&chat); | |||
PQCLEAN_DILITHIUMIII_CLEAN_polyveck_shiftl(&t1, D); | |||
PQCLEAN_DILITHIUMIII_CLEAN_polyveck_ntt(&t1); | |||
for (i = 0; i < K; ++i) { | |||
PQCLEAN_DILITHIUMIII_CLEAN_poly_pointwise_invmontgomery(tmp2.vec + i, &chat, | |||
t1.vec + i); | |||
} | |||
PQCLEAN_DILITHIUMIII_CLEAN_polyveck_sub(&tmp1, &tmp1, &tmp2); | |||
PQCLEAN_DILITHIUMIII_CLEAN_polyveck_reduce(&tmp1); | |||
PQCLEAN_DILITHIUMIII_CLEAN_polyveck_invntt_montgomery(&tmp1); | |||
/* Reconstruct w1 */ | |||
PQCLEAN_DILITHIUMIII_CLEAN_polyveck_csubq(&tmp1); | |||
PQCLEAN_DILITHIUMIII_CLEAN_polyveck_use_hint(&w1, &tmp1, &h); | |||
/* Call random oracle and verify challenge */ | |||
PQCLEAN_DILITHIUMIII_CLEAN_challenge(&cp, mu, &w1); | |||
for (i = 0; i < N; ++i) { | |||
if (c.coeffs[i] != cp.coeffs[i]) { | |||
goto badsig; | |||
} | |||
} | |||
/* All good, copy msg, return 0 */ | |||
for (i = 0; i < *mlen; ++i) { | |||
m[i] = sm[CRYPTO_BYTES + i]; | |||
} | |||
return 0; | |||
/* Signature verification failed */ | |||
badsig: | |||
*mlen = 0; | |||
for (i = 0; i < smlen; ++i) { | |||
m[i] = 0; | |||
} | |||
return -1; | |||
} |
@@ -1,30 +0,0 @@ | |||
#ifndef SIGN_H | |||
#define SIGN_H | |||
#include <stddef.h> | |||
#include <stdint.h> | |||
#include "params.h" | |||
#include "poly.h" | |||
#include "polyvec.h" | |||
void PQCLEAN_DILITHIUMIII_CLEAN_expand_mat(polyvecl mat[K], | |||
const uint8_t rho[SEEDBYTES]); | |||
void PQCLEAN_DILITHIUMIII_CLEAN_challenge(poly *c, const uint8_t mu[CRHBYTES], | |||
const polyveck *w1); | |||
int PQCLEAN_DILITHIUMIII_CLEAN_crypto_sign_keypair(uint8_t *pk, | |||
uint8_t *sk); | |||
int PQCLEAN_DILITHIUMIII_CLEAN_crypto_sign(uint8_t *sm, | |||
size_t *smlen, | |||
const uint8_t *m, | |||
size_t mlen, | |||
const uint8_t *sk); | |||
int PQCLEAN_DILITHIUMIII_CLEAN_crypto_sign_open(uint8_t *m, | |||
size_t *mlen, | |||
const uint8_t *sm, | |||
size_t smlen, | |||
const uint8_t *pk); | |||
#endif |
@@ -0,0 +1,27 @@ | |||
name: SPHINCS+ | |||
type: signature | |||
claimed-nist-level: 1 | |||
length-public-key: 32 | |||
length-signature: 16976 | |||
testvectors-sha256: a14cb8e4f149493fc5979e465e09ce943e8d669186ff5c7c3d11239fa869def6 | |||
principal-submitter: Andreas Hülsing | |||
auxiliary-submitters: | |||
- Jean-Philippe Aumasson | |||
- Daniel J. Bernstein, | |||
- Christoph Dobraunig | |||
- Maria Eichlseder | |||
- Scott Fluhrer | |||
- Stefan-Lukas Gazdag | |||
- Panos Kampanakis | |||
- Stefan Kölbl | |||
- Tanja Lange | |||
- Martin M. Lauridsen | |||
- Florian Mendel | |||
- Ruben Niederhagen | |||
- Christian Rechberger | |||
- Joost Rijneveld | |||
- Peter Schwabe | |||
implementations: | |||
- name: clean | |||
version: https://github.com/sphincs/sphincsplus/commit/492ec4f1f6d3b3dc4b435783bbaaf4e41cdb6f32 | |||
length-secret-key: 64 |
@@ -0,0 +1,116 @@ | |||
CC0 1.0 Universal | |||
Statement of Purpose | |||
The laws of most jurisdictions throughout the world automatically confer | |||
exclusive Copyright and Related Rights (defined below) upon the creator and | |||
subsequent owner(s) (each and all, an "owner") of an original work of | |||
authorship and/or a database (each, a "Work"). | |||
Certain owners wish to permanently relinquish those rights to a Work for the | |||
purpose of contributing to a commons of creative, cultural and scientific | |||
works ("Commons") that the public can reliably and without fear of later | |||
claims of infringement build upon, modify, incorporate in other works, reuse | |||
and redistribute as freely as possible in any form whatsoever and for any | |||
purposes, including without limitation commercial purposes. These owners may | |||
contribute to the Commons to promote the ideal of a free culture and the | |||
further production of creative, cultural and scientific works, or to gain | |||
reputation or greater distribution for their Work in part through the use and | |||
efforts of others. | |||
For these and/or other purposes and motivations, and without any expectation | |||
of additional consideration or compensation, the person associating CC0 with a | |||
Work (the "Affirmer"), to the extent that he or she is an owner of Copyright | |||
and Related Rights in the Work, voluntarily elects to apply CC0 to the Work | |||
and publicly distribute the Work under its terms, with knowledge of his or her | |||
Copyright and Related Rights in the Work and the meaning and intended legal | |||
effect of CC0 on those rights. | |||
1. Copyright and Related Rights. A Work made available under CC0 may be | |||
protected by copyright and related or neighboring rights ("Copyright and | |||
Related Rights"). Copyright and Related Rights include, but are not limited | |||
to, the following: | |||
i. the right to reproduce, adapt, distribute, perform, display, communicate, | |||
and translate a Work; | |||
ii. moral rights retained by the original author(s) and/or performer(s); | |||
iii. publicity and privacy rights pertaining to a person's image or likeness | |||
depicted in a Work; | |||
iv. rights protecting against unfair competition in regards to a Work, | |||
subject to the limitations in paragraph 4(a), below; | |||
v. rights protecting the extraction, dissemination, use and reuse of data in | |||
a Work; | |||
vi. database rights (such as those arising under Directive 96/9/EC of the | |||
European Parliament and of the Council of 11 March 1996 on the legal | |||
protection of databases, and under any national implementation thereof, | |||
including any amended or successor version of such directive); and | |||
vii. other similar, equivalent or corresponding rights throughout the world | |||
based on applicable law or treaty, and any national implementations thereof. | |||
2. Waiver. To the greatest extent permitted by, but not in contravention of, | |||
applicable law, Affirmer hereby overtly, fully, permanently, irrevocably and | |||
unconditionally waives, abandons, and surrenders all of Affirmer's Copyright | |||
and Related Rights and associated claims and causes of action, whether now | |||
known or unknown (including existing as well as future claims and causes of | |||
action), in the Work (i) in all territories worldwide, (ii) for the maximum | |||
duration provided by applicable law or treaty (including future time | |||
extensions), (iii) in any current or future medium and for any number of | |||
copies, and (iv) for any purpose whatsoever, including without limitation | |||
commercial, advertising or promotional purposes (the "Waiver"). Affirmer makes | |||
the Waiver for the benefit of each member of the public at large and to the | |||
detriment of Affirmer's heirs and successors, fully intending that such Waiver | |||
shall not be subject to revocation, rescission, cancellation, termination, or | |||
any other legal or equitable action to disrupt the quiet enjoyment of the Work | |||
by the public as contemplated by Affirmer's express Statement of Purpose. | |||
3. Public License Fallback. Should any part of the Waiver for any reason be | |||
judged legally invalid or ineffective under applicable law, then the Waiver | |||
shall be preserved to the maximum extent permitted taking into account | |||
Affirmer's express Statement of Purpose. In addition, to the extent the Waiver | |||
is so judged Affirmer hereby grants to each affected person a royalty-free, | |||
non transferable, non sublicensable, non exclusive, irrevocable and | |||
unconditional license to exercise Affirmer's Copyright and Related Rights in | |||
the Work (i) in all territories worldwide, (ii) for the maximum duration | |||
provided by applicable law or treaty (including future time extensions), (iii) | |||
in any current or future medium and for any number of copies, and (iv) for any | |||
purpose whatsoever, including without limitation commercial, advertising or | |||
promotional purposes (the "License"). The License shall be deemed effective as | |||
of the date CC0 was applied by Affirmer to the Work. Should any part of the | |||
License for any reason be judged legally invalid or ineffective under | |||
applicable law, such partial invalidity or ineffectiveness shall not | |||
invalidate the remainder of the License, and in such case Affirmer hereby | |||
affirms that he or she will not (i) exercise any of his or her remaining | |||
Copyright and Related Rights in the Work or (ii) assert any associated claims | |||
and causes of action with respect to the Work, in either case contrary to | |||
Affirmer's express Statement of Purpose. | |||
4. Limitations and Disclaimers. | |||
a. No trademark or patent rights held by Affirmer are waived, abandoned, | |||
surrendered, licensed or otherwise affected by this document. | |||
b. Affirmer offers the Work as-is and makes no representations or warranties | |||
of any kind concerning the Work, express, implied, statutory or otherwise, | |||
including without limitation warranties of title, merchantability, fitness | |||
for a particular purpose, non infringement, or the absence of latent or | |||
other defects, accuracy, or the present or absence of errors, whether or not | |||
discoverable, all to the greatest extent permissible under applicable law. | |||
c. Affirmer disclaims responsibility for clearing rights of other persons | |||
that may apply to the Work or any use thereof, including without limitation | |||
any person's Copyright and Related Rights in the Work. Further, Affirmer | |||
disclaims responsibility for obtaining any necessary consents, permissions | |||
or other rights required for any use of the Work. | |||
d. Affirmer understands and acknowledges that Creative Commons is not a | |||
party to this document and has no duty or obligation with respect to this | |||
CC0 or use of the Work. | |||
For more information, please see | |||
<http://creativecommons.org/publicdomain/zero/1.0/> |
@@ -0,0 +1,20 @@ | |||
# This Makefile can be used with GNU Make or BSD Make | |||
LIB=libsphincs-shake256-128f-simple_clean.a | |||
HEADERS = params.h address.h wots.h utils.h fors.h api.h hash.h thash.h | |||
OBJECTS = address.o wots.o utils.o fors.o sign.o hash_shake256.o thash_shake256_simple.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) |
@@ -1,10 +1,10 @@ | |||
# This Makefile can be used with Microsoft Visual Studio's nmake using the command: | |||
# nmake /f Makefile.Microsoft_nmake | |||
LIBRARY=libdilithium-iii_clean.lib | |||
OBJECTS=ntt.obj packing.obj poly.obj polyvec.obj reduce.obj rounding.obj sign.obj | |||
LIBRARY=libsphincs-shake256-128f-simple_clean.lib | |||
OBJECTS=address.obj wots.obj utils.obj fors.obj sign.obj hash_shake256.obj thash_shake256_simple.obj | |||
CFLAGS=/nologo /I ..\..\..\common /W1 /WX # FIXME: ideally would use /W4 instead of /W1, but too many failures in Dilithium right now | |||
CFLAGS=/nologo /I ..\..\..\common /W4 /WX | |||
all: $(LIBRARY) | |||
@@ -0,0 +1,77 @@ | |||
#include <stdint.h> | |||
#include "params.h" | |||
#include "utils.h" | |||
void PQCLEAN_SPHINCSSHAKE256128FSIMPLE_CLEAN_addr_to_bytes( | |||
unsigned char *bytes, const uint32_t addr[8]) { | |||
int i; | |||
for (i = 0; i < 8; i++) { | |||
PQCLEAN_SPHINCSSHAKE256128FSIMPLE_CLEAN_ull_to_bytes( | |||
bytes + i * 4, 4, addr[i]); | |||
} | |||
} | |||
void PQCLEAN_SPHINCSSHAKE256128FSIMPLE_CLEAN_set_layer_addr( | |||
uint32_t addr[8], uint32_t layer) { | |||
addr[0] = layer; | |||
} | |||
void PQCLEAN_SPHINCSSHAKE256128FSIMPLE_CLEAN_set_tree_addr( | |||
uint32_t addr[8], uint64_t tree) { | |||
addr[1] = 0; | |||
addr[2] = (uint32_t) (tree >> 32); | |||
addr[3] = (uint32_t) tree; | |||
} | |||
void PQCLEAN_SPHINCSSHAKE256128FSIMPLE_CLEAN_set_type( | |||
uint32_t addr[8], uint32_t type) { | |||
addr[4] = type; | |||
} | |||
void PQCLEAN_SPHINCSSHAKE256128FSIMPLE_CLEAN_copy_subtree_addr( | |||
uint32_t out[8], const uint32_t in[8]) { | |||
out[0] = in[0]; | |||
out[1] = in[1]; | |||
out[2] = in[2]; | |||
out[3] = in[3]; | |||
} | |||
/* These functions are used for OTS addresses. */ | |||
void PQCLEAN_SPHINCSSHAKE256128FSIMPLE_CLEAN_set_keypair_addr( | |||
uint32_t addr[8], uint32_t keypair) { | |||
addr[5] = keypair; | |||
} | |||
void PQCLEAN_SPHINCSSHAKE256128FSIMPLE_CLEAN_copy_keypair_addr( | |||
uint32_t out[8], const uint32_t in[8]) { | |||
out[0] = in[0]; | |||
out[1] = in[1]; | |||
out[2] = in[2]; | |||
out[3] = in[3]; | |||
out[5] = in[5]; | |||
} | |||
void PQCLEAN_SPHINCSSHAKE256128FSIMPLE_CLEAN_set_chain_addr( | |||
uint32_t addr[8], uint32_t chain) { | |||
addr[6] = chain; | |||
} | |||
void PQCLEAN_SPHINCSSHAKE256128FSIMPLE_CLEAN_set_hash_addr( | |||
uint32_t addr[8], uint32_t hash) { | |||
addr[7] = hash; | |||
} | |||
/* These functions are used for all hash tree addresses (including FORS). */ | |||
void PQCLEAN_SPHINCSSHAKE256128FSIMPLE_CLEAN_set_tree_height( | |||
uint32_t addr[8], uint32_t tree_height) { | |||
addr[6] = tree_height; | |||
} | |||
void PQCLEAN_SPHINCSSHAKE256128FSIMPLE_CLEAN_set_tree_index( | |||
uint32_t addr[8], uint32_t tree_index) { | |||
addr[7] = tree_index; | |||
} |
@@ -0,0 +1,50 @@ | |||
#ifndef SPX_ADDRESS_H | |||
#define SPX_ADDRESS_H | |||
#include <stdint.h> | |||
#define SPX_ADDR_TYPE_WOTS 0 | |||
#define SPX_ADDR_TYPE_WOTSPK 1 | |||
#define SPX_ADDR_TYPE_HASHTREE 2 | |||
#define SPX_ADDR_TYPE_FORSTREE 3 | |||
#define SPX_ADDR_TYPE_FORSPK 4 | |||
void PQCLEAN_SPHINCSSHAKE256128FSIMPLE_CLEAN_addr_to_bytes( | |||
unsigned char *bytes, const uint32_t addr[8]); | |||
void PQCLEAN_SPHINCSSHAKE256128FSIMPLE_CLEAN_set_layer_addr( | |||
uint32_t addr[8], uint32_t layer); | |||
void PQCLEAN_SPHINCSSHAKE256128FSIMPLE_CLEAN_set_tree_addr( | |||
uint32_t addr[8], uint64_t tree); | |||
void PQCLEAN_SPHINCSSHAKE256128FSIMPLE_CLEAN_set_type( | |||
uint32_t addr[8], uint32_t type); | |||
/* Copies the layer and tree part of one address into the other */ | |||
void PQCLEAN_SPHINCSSHAKE256128FSIMPLE_CLEAN_copy_subtree_addr( | |||
uint32_t out[8], const uint32_t in[8]); | |||
/* These functions are used for WOTS and FORS addresses. */ | |||
void PQCLEAN_SPHINCSSHAKE256128FSIMPLE_CLEAN_set_keypair_addr( | |||
uint32_t addr[8], uint32_t keypair); | |||
void PQCLEAN_SPHINCSSHAKE256128FSIMPLE_CLEAN_set_chain_addr( | |||
uint32_t addr[8], uint32_t chain); | |||
void PQCLEAN_SPHINCSSHAKE256128FSIMPLE_CLEAN_set_hash_addr( | |||
uint32_t addr[8], uint32_t hash); | |||
void PQCLEAN_SPHINCSSHAKE256128FSIMPLE_CLEAN_copy_keypair_addr( | |||
uint32_t out[8], const uint32_t in[8]); | |||
/* These functions are used for all hash tree addresses (including FORS). */ | |||
void PQCLEAN_SPHINCSSHAKE256128FSIMPLE_CLEAN_set_tree_height( | |||
uint32_t addr[8], uint32_t tree_height); | |||
void PQCLEAN_SPHINCSSHAKE256128FSIMPLE_CLEAN_set_tree_index( | |||
uint32_t addr[8], uint32_t tree_index); | |||
#endif |
@@ -0,0 +1,78 @@ | |||
#ifndef PQCLEAN_SPHINCSSHAKE256128FSIMPLE_CLEAN_API_H | |||
#define PQCLEAN_SPHINCSSHAKE256128FSIMPLE_CLEAN_API_H | |||
#include <stddef.h> | |||
#include <stdint.h> | |||
#define PQCLEAN_SPHINCSSHAKE256128FSIMPLE_CLEAN_CRYPTO_ALGNAME "SPHINCS+" | |||
#define PQCLEAN_SPHINCSSHAKE256128FSIMPLE_CLEAN_CRYPTO_SECRETKEYBYTES 64 | |||
#define PQCLEAN_SPHINCSSHAKE256128FSIMPLE_CLEAN_CRYPTO_PUBLICKEYBYTES 32 | |||
#define PQCLEAN_SPHINCSSHAKE256128FSIMPLE_CLEAN_CRYPTO_BYTES 16976 | |||
#define PQCLEAN_SPHINCSSHAKE256128FSIMPLE_CLEAN_CRYPTO_SEEDBYTES 48 | |||
/* | |||
* Returns the length of a secret key, in bytes | |||
*/ | |||
size_t PQCLEAN_SPHINCSSHAKE256128FSIMPLE_CLEAN_crypto_sign_secretkeybytes(void); | |||
/* | |||
* Returns the length of a public key, in bytes | |||
*/ | |||
size_t PQCLEAN_SPHINCSSHAKE256128FSIMPLE_CLEAN_crypto_sign_publickeybytes(void); | |||
/* | |||
* Returns the length of a signature, in bytes | |||
*/ | |||
size_t PQCLEAN_SPHINCSSHAKE256128FSIMPLE_CLEAN_crypto_sign_bytes(void); | |||
/* | |||
* Returns the length of the seed required to generate a key pair, in bytes | |||
*/ | |||
size_t PQCLEAN_SPHINCSSHAKE256128FSIMPLE_CLEAN_crypto_sign_seedbytes(void); | |||
/* | |||
* Generates a SPHINCS+ key pair given a seed. | |||
* Format sk: [SK_SEED || SK_PRF || PUB_SEED || root] | |||
* Format pk: [root || PUB_SEED] | |||
*/ | |||
int PQCLEAN_SPHINCSSHAKE256128FSIMPLE_CLEAN_crypto_sign_seed_keypair( | |||
uint8_t *pk, uint8_t *sk, const uint8_t *seed); | |||
/* | |||
* Generates a SPHINCS+ key pair. | |||
* Format sk: [SK_SEED || SK_PRF || PUB_SEED || root] | |||
* Format pk: [root || PUB_SEED] | |||
*/ | |||
int PQCLEAN_SPHINCSSHAKE256128FSIMPLE_CLEAN_crypto_sign_keypair( | |||
uint8_t *pk, uint8_t *sk); | |||
/** | |||
* Returns an array containing a detached signature. | |||
*/ | |||
int PQCLEAN_SPHINCSSHAKE256128FSIMPLE_CLEAN_crypto_sign_signature( | |||
uint8_t *sig, size_t *siglen, | |||
const uint8_t *m, size_t mlen, const uint8_t *sk); | |||
/** | |||
* Verifies a detached signature and message under a given public key. | |||
*/ | |||
int PQCLEAN_SPHINCSSHAKE256128FSIMPLE_CLEAN_crypto_sign_verify( | |||
const uint8_t *sig, size_t siglen, | |||
const uint8_t *m, size_t mlen, const uint8_t *pk); | |||
/** | |||
* Returns an array containing the signature followed by the message. | |||
*/ | |||
int PQCLEAN_SPHINCSSHAKE256128FSIMPLE_CLEAN_crypto_sign( | |||
uint8_t *sm, size_t *smlen, | |||
const uint8_t *m, size_t mlen, const uint8_t *sk); | |||
/** | |||
* Verifies a given signature-message pair under a given public key. | |||
*/ | |||
int PQCLEAN_SPHINCSSHAKE256128FSIMPLE_CLEAN_crypto_sign_open( | |||
uint8_t *m, size_t *mlen, | |||
const uint8_t *sm, size_t smlen, const uint8_t *pk); | |||
#endif |
@@ -0,0 +1,164 @@ | |||
#include <stdint.h> | |||
#include <stdlib.h> | |||
#include <string.h> | |||
#include "address.h" | |||
#include "fors.h" | |||
#include "hash.h" | |||
#include "thash.h" | |||
#include "utils.h" | |||
static void fors_gen_sk(unsigned char *sk, const unsigned char *sk_seed, | |||
uint32_t fors_leaf_addr[8]) { | |||
PQCLEAN_SPHINCSSHAKE256128FSIMPLE_CLEAN_prf_addr( | |||
sk, sk_seed, fors_leaf_addr); | |||
} | |||
static void fors_sk_to_leaf(unsigned char *leaf, const unsigned char *sk, | |||
const unsigned char *pub_seed, | |||
uint32_t fors_leaf_addr[8]) { | |||
PQCLEAN_SPHINCSSHAKE256128FSIMPLE_CLEAN_thash_1( | |||
leaf, sk, pub_seed, fors_leaf_addr); | |||
} | |||
static void fors_gen_leaf(unsigned char *leaf, const unsigned char *sk_seed, | |||
const unsigned char *pub_seed, | |||
uint32_t addr_idx, const uint32_t fors_tree_addr[8]) { | |||
uint32_t fors_leaf_addr[8] = {0}; | |||
/* Only copy the parts that must be kept in fors_leaf_addr. */ | |||
PQCLEAN_SPHINCSSHAKE256128FSIMPLE_CLEAN_copy_keypair_addr( | |||
fors_leaf_addr, fors_tree_addr); | |||
PQCLEAN_SPHINCSSHAKE256128FSIMPLE_CLEAN_set_type( | |||
fors_leaf_addr, SPX_ADDR_TYPE_FORSTREE); | |||
PQCLEAN_SPHINCSSHAKE256128FSIMPLE_CLEAN_set_tree_index( | |||
fors_leaf_addr, addr_idx); | |||
fors_gen_sk(leaf, sk_seed, fors_leaf_addr); | |||
fors_sk_to_leaf(leaf, leaf, pub_seed, fors_leaf_addr); | |||
} | |||
/** | |||
* Interprets m as SPX_FORS_HEIGHT-bit unsigned integers. | |||
* Assumes m contains at least SPX_FORS_HEIGHT * SPX_FORS_TREES bits. | |||
* Assumes indices has space for SPX_FORS_TREES integers. | |||
*/ | |||
static void message_to_indices(uint32_t *indices, const unsigned char *m) { | |||
unsigned int i, j; | |||
unsigned int offset = 0; | |||
for (i = 0; i < SPX_FORS_TREES; i++) { | |||
indices[i] = 0; | |||
for (j = 0; j < SPX_FORS_HEIGHT; j++) { | |||
indices[i] ^= ((m[offset >> 3] >> (offset & 0x7)) & 0x1) << j; | |||
offset++; | |||
} | |||
} | |||
} | |||
/** | |||
* Signs a message m, deriving the secret key from sk_seed and the FTS address. | |||
* Assumes m contains at least SPX_FORS_HEIGHT * SPX_FORS_TREES bits. | |||
*/ | |||
void PQCLEAN_SPHINCSSHAKE256128FSIMPLE_CLEAN_fors_sign( | |||
unsigned char *sig, unsigned char *pk, | |||
const unsigned char *m, | |||
const unsigned char *sk_seed, const unsigned char *pub_seed, | |||
const uint32_t fors_addr[8]) { | |||
uint32_t indices[SPX_FORS_TREES]; | |||
unsigned char roots[SPX_FORS_TREES * SPX_N]; | |||
uint32_t fors_tree_addr[8] = {0}; | |||
uint32_t fors_pk_addr[8] = {0}; | |||
uint32_t idx_offset; | |||
unsigned int i; | |||
PQCLEAN_SPHINCSSHAKE256128FSIMPLE_CLEAN_copy_keypair_addr( | |||
fors_tree_addr, fors_addr); | |||
PQCLEAN_SPHINCSSHAKE256128FSIMPLE_CLEAN_copy_keypair_addr( | |||
fors_pk_addr, fors_addr); | |||
PQCLEAN_SPHINCSSHAKE256128FSIMPLE_CLEAN_set_type( | |||
fors_tree_addr, SPX_ADDR_TYPE_FORSTREE); | |||
PQCLEAN_SPHINCSSHAKE256128FSIMPLE_CLEAN_set_type( | |||
fors_pk_addr, SPX_ADDR_TYPE_FORSPK); | |||
message_to_indices(indices, m); | |||
for (i = 0; i < SPX_FORS_TREES; i++) { | |||
idx_offset = i * (1 << SPX_FORS_HEIGHT); | |||
PQCLEAN_SPHINCSSHAKE256128FSIMPLE_CLEAN_set_tree_height( | |||
fors_tree_addr, 0); | |||
PQCLEAN_SPHINCSSHAKE256128FSIMPLE_CLEAN_set_tree_index( | |||
fors_tree_addr, indices[i] + idx_offset); | |||
/* Include the secret key part that produces the selected leaf node. */ | |||
fors_gen_sk(sig, sk_seed, fors_tree_addr); | |||
sig += SPX_N; | |||
/* Compute the authentication path for this leaf node. */ | |||
PQCLEAN_SPHINCSSHAKE256128FSIMPLE_CLEAN_treehash_FORS_HEIGHT( | |||
roots + i * SPX_N, sig, sk_seed, pub_seed, | |||
indices[i], idx_offset, fors_gen_leaf, fors_tree_addr); | |||
sig += SPX_N * SPX_FORS_HEIGHT; | |||
} | |||
/* Hash horizontally across all tree roots to derive the public key. */ | |||
PQCLEAN_SPHINCSSHAKE256128FSIMPLE_CLEAN_thash_FORS_TREES( | |||
pk, roots, pub_seed, fors_pk_addr); | |||
} | |||
/** | |||
* Derives the FORS public key from a signature. | |||
* This can be used for verification by comparing to a known public key, or to | |||
* subsequently verify a signature on the derived public key. The latter is the | |||
* typical use-case when used as an FTS below an OTS in a hypertree. | |||
* Assumes m contains at least SPX_FORS_HEIGHT * SPX_FORS_TREES bits. | |||
*/ | |||
void PQCLEAN_SPHINCSSHAKE256128FSIMPLE_CLEAN_fors_pk_from_sig( | |||
unsigned char *pk, | |||
const unsigned char *sig, const unsigned char *m, | |||
const unsigned char *pub_seed, const uint32_t fors_addr[8]) { | |||
uint32_t indices[SPX_FORS_TREES]; | |||
unsigned char roots[SPX_FORS_TREES * SPX_N]; | |||
unsigned char leaf[SPX_N]; | |||
uint32_t fors_tree_addr[8] = {0}; | |||
uint32_t fors_pk_addr[8] = {0}; | |||
uint32_t idx_offset; | |||
unsigned int i; | |||
PQCLEAN_SPHINCSSHAKE256128FSIMPLE_CLEAN_copy_keypair_addr( | |||
fors_tree_addr, fors_addr); | |||
PQCLEAN_SPHINCSSHAKE256128FSIMPLE_CLEAN_copy_keypair_addr( | |||
fors_pk_addr, fors_addr); | |||
PQCLEAN_SPHINCSSHAKE256128FSIMPLE_CLEAN_set_type( | |||
fors_tree_addr, SPX_ADDR_TYPE_FORSTREE); | |||
PQCLEAN_SPHINCSSHAKE256128FSIMPLE_CLEAN_set_type( | |||
fors_pk_addr, SPX_ADDR_TYPE_FORSPK); | |||
message_to_indices(indices, m); | |||
for (i = 0; i < SPX_FORS_TREES; i++) { | |||
idx_offset = i * (1 << SPX_FORS_HEIGHT); | |||
PQCLEAN_SPHINCSSHAKE256128FSIMPLE_CLEAN_set_tree_height( | |||
fors_tree_addr, 0); | |||
PQCLEAN_SPHINCSSHAKE256128FSIMPLE_CLEAN_set_tree_index( | |||
fors_tree_addr, indices[i] + idx_offset); | |||
/* Derive the leaf from the included secret key part. */ | |||
fors_sk_to_leaf(leaf, sig, pub_seed, fors_tree_addr); | |||
sig += SPX_N; | |||
/* Derive the corresponding root node of this tree. */ | |||
PQCLEAN_SPHINCSSHAKE256128FSIMPLE_CLEAN_compute_root( | |||
roots + i * SPX_N, leaf, indices[i], idx_offset, sig, | |||
SPX_FORS_HEIGHT, pub_seed, fors_tree_addr); | |||
sig += SPX_N * SPX_FORS_HEIGHT; | |||
} | |||
/* Hash horizontally across all tree roots to derive the public key. */ | |||
PQCLEAN_SPHINCSSHAKE256128FSIMPLE_CLEAN_thash_FORS_TREES( | |||
pk, roots, pub_seed, fors_pk_addr); | |||
} |
@@ -0,0 +1,30 @@ | |||
#ifndef SPX_FORS_H | |||
#define SPX_FORS_H | |||
#include <stdint.h> | |||
#include "params.h" | |||
/** | |||
* Signs a message m, deriving the secret key from sk_seed and the FTS address. | |||
* Assumes m contains at least SPX_FORS_HEIGHT * SPX_FORS_TREES bits. | |||
*/ | |||
void PQCLEAN_SPHINCSSHAKE256128FSIMPLE_CLEAN_fors_sign( | |||
unsigned char *sig, unsigned char *pk, | |||
const unsigned char *m, | |||
const unsigned char *sk_seed, const unsigned char *pub_seed, | |||
const uint32_t fors_addr[8]); | |||
/** | |||
* Derives the FORS public key from a signature. | |||
* This can be used for verification by comparing to a known public key, or to | |||
* subsequently verify a signature on the derived public key. The latter is the | |||
* typical use-case when used as an FTS below an OTS in a hypertree. | |||
* Assumes m contains at least SPX_FORS_HEIGHT * SPX_FORS_TREES bits. | |||
*/ | |||
void PQCLEAN_SPHINCSSHAKE256128FSIMPLE_CLEAN_fors_pk_from_sig( | |||
unsigned char *pk, | |||
const unsigned char *sig, const unsigned char *m, | |||
const unsigned char *pub_seed, const uint32_t fors_addr[8]); | |||
#endif |
@@ -0,0 +1,22 @@ | |||
#ifndef SPX_HASH_H | |||
#define SPX_HASH_H | |||
#include <stdint.h> | |||
void PQCLEAN_SPHINCSSHAKE256128FSIMPLE_CLEAN_initialize_hash_function( | |||
const unsigned char *pub_seed, const unsigned char *sk_seed); | |||
void PQCLEAN_SPHINCSSHAKE256128FSIMPLE_CLEAN_prf_addr( | |||
unsigned char *out, const unsigned char *key, const uint32_t addr[8]); | |||
void PQCLEAN_SPHINCSSHAKE256128FSIMPLE_CLEAN_gen_message_random( | |||
unsigned char *R, | |||
const unsigned char *sk_prf, const unsigned char *optrand, | |||
const unsigned char *m, size_t mlen); | |||
void PQCLEAN_SPHINCSSHAKE256128FSIMPLE_CLEAN_hash_message( | |||
unsigned char *digest, uint64_t *tree, uint32_t *leaf_idx, | |||
const unsigned char *R, const unsigned char *pk, | |||
const unsigned char *m, size_t mlen); | |||
#endif |
@@ -0,0 +1,86 @@ | |||
#include <stdint.h> | |||
#include <string.h> | |||
#include "address.h" | |||
#include "fips202.h" | |||
#include "hash.h" | |||
#include "params.h" | |||
#include "utils.h" | |||
/* For SHAKE256, there is no immediate reason to initialize at the start, | |||
so this function is an empty operation. */ | |||
void PQCLEAN_SPHINCSSHAKE256128FSIMPLE_CLEAN_initialize_hash_function( | |||
const unsigned char *pub_seed, const unsigned char *sk_seed) { | |||
(void)pub_seed; /* Suppress an 'unused parameter' warning. */ | |||
(void)sk_seed; /* Suppress an 'unused parameter' warning. */ | |||
} | |||
/* | |||
* Computes PRF(key, addr), given a secret key of SPX_N bytes and an address | |||
*/ | |||
void PQCLEAN_SPHINCSSHAKE256128FSIMPLE_CLEAN_prf_addr( | |||
unsigned char *out, const unsigned char *key, const uint32_t addr[8]) { | |||
unsigned char buf[SPX_N + SPX_ADDR_BYTES]; | |||
memcpy(buf, key, SPX_N); | |||
PQCLEAN_SPHINCSSHAKE256128FSIMPLE_CLEAN_addr_to_bytes(buf + SPX_N, addr); | |||
shake256(out, SPX_N, buf, SPX_N + SPX_ADDR_BYTES); | |||
} | |||
/** | |||
* Computes the message-dependent randomness R, using a secret seed and an | |||
* optional randomization value as well as the message. | |||
*/ | |||
void PQCLEAN_SPHINCSSHAKE256128FSIMPLE_CLEAN_gen_message_random( | |||
unsigned char *R, | |||
const unsigned char *sk_prf, const unsigned char *optrand, | |||
const unsigned char *m, size_t mlen) { | |||
uint64_t s_inc[26]; | |||
shake256_inc_init(s_inc); | |||
shake256_inc_absorb(s_inc, sk_prf, SPX_N); | |||
shake256_inc_absorb(s_inc, optrand, SPX_N); | |||
shake256_inc_absorb(s_inc, m, mlen); | |||
shake256_inc_finalize(s_inc); | |||
shake256_inc_squeeze(R, SPX_N, s_inc); | |||
} | |||
/** | |||
* Computes the message hash using R, the public key, and the message. | |||
* Outputs the message digest and the index of the leaf. The index is split in | |||
* the tree index and the leaf index, for convenient copying to an address. | |||
*/ | |||
void PQCLEAN_SPHINCSSHAKE256128FSIMPLE_CLEAN_hash_message( | |||
unsigned char *digest, uint64_t *tree, uint32_t *leaf_idx, | |||
const unsigned char *R, const unsigned char *pk, | |||
const unsigned char *m, size_t mlen) { | |||
#define SPX_TREE_BITS (SPX_TREE_HEIGHT * (SPX_D - 1)) | |||
#define SPX_TREE_BYTES ((SPX_TREE_BITS + 7) / 8) | |||
#define SPX_LEAF_BITS SPX_TREE_HEIGHT | |||
#define SPX_LEAF_BYTES ((SPX_LEAF_BITS + 7) / 8) | |||
#define SPX_DGST_BYTES (SPX_FORS_MSG_BYTES + SPX_TREE_BYTES + SPX_LEAF_BYTES) | |||
unsigned char buf[SPX_DGST_BYTES]; | |||
unsigned char *bufp = buf; | |||
uint64_t s_inc[26]; | |||
shake256_inc_init(s_inc); | |||
shake256_inc_absorb(s_inc, R, SPX_N); | |||
shake256_inc_absorb(s_inc, pk, SPX_PK_BYTES); | |||
shake256_inc_absorb(s_inc, m, mlen); | |||
shake256_inc_finalize(s_inc); | |||
shake256_inc_squeeze(buf, SPX_DGST_BYTES, s_inc); | |||
memcpy(digest, bufp, SPX_FORS_MSG_BYTES); | |||
bufp += SPX_FORS_MSG_BYTES; | |||
*tree = PQCLEAN_SPHINCSSHAKE256128FSIMPLE_CLEAN_bytes_to_ull( | |||
bufp, SPX_TREE_BYTES); | |||
*tree &= (~(uint64_t)0) >> (64 - SPX_TREE_BITS); | |||
bufp += SPX_TREE_BYTES; | |||
*leaf_idx = (uint32_t)PQCLEAN_SPHINCSSHAKE256128FSIMPLE_CLEAN_bytes_to_ull( | |||
bufp, SPX_LEAF_BYTES); | |||
*leaf_idx &= (~(uint32_t)0) >> (32 - SPX_LEAF_BITS); | |||
} |
@@ -0,0 +1,53 @@ | |||
#ifndef SPX_PARAMS_H | |||
#define SPX_PARAMS_H | |||
/* Hash output length in bytes. */ | |||
#define SPX_N 16 | |||
/* Height of the hypertree. */ | |||
#define SPX_FULL_HEIGHT 60 | |||
/* Number of subtree layer. */ | |||
#define SPX_D 20 | |||
/* FORS tree dimensions. */ | |||
#define SPX_FORS_HEIGHT 9 | |||
#define SPX_FORS_TREES 30 | |||
/* Winternitz parameter, */ | |||
#define SPX_WOTS_W 16 | |||
/* The hash function is defined by linking a different hash.c file, as opposed | |||
to setting a #define constant. */ | |||
/* For clarity */ | |||
#define SPX_ADDR_BYTES 32 | |||
/* WOTS parameters. */ | |||
#define SPX_WOTS_LOGW 4 | |||
#define SPX_WOTS_LEN1 (8 * SPX_N / SPX_WOTS_LOGW) | |||
/* SPX_WOTS_LEN2 is floor(log(len_1 * (w - 1)) / log(w)) + 1; we precompute */ | |||
#define SPX_WOTS_LEN2 3 | |||
#define SPX_WOTS_LEN (SPX_WOTS_LEN1 + SPX_WOTS_LEN2) | |||
#define SPX_WOTS_BYTES (SPX_WOTS_LEN * SPX_N) | |||
#define SPX_WOTS_PK_BYTES SPX_WOTS_BYTES | |||
/* Subtree size. */ | |||
#define SPX_TREE_HEIGHT (SPX_FULL_HEIGHT / SPX_D) | |||
/* FORS parameters. */ | |||
#define SPX_FORS_MSG_BYTES ((SPX_FORS_HEIGHT * SPX_FORS_TREES + 7) / 8) | |||
#define SPX_FORS_BYTES ((SPX_FORS_HEIGHT + 1) * SPX_FORS_TREES * SPX_N) | |||
#define SPX_FORS_PK_BYTES SPX_N | |||
/* Resulting SPX sizes. */ | |||
#define SPX_BYTES (SPX_N + SPX_FORS_BYTES + SPX_D * SPX_WOTS_BYTES +\ | |||
SPX_FULL_HEIGHT * SPX_N) | |||
#define SPX_PK_BYTES (2 * SPX_N) | |||
#define SPX_SK_BYTES (2 * SPX_N + SPX_PK_BYTES) | |||
/* Optionally, signing can be made non-deterministic using optrand. | |||
This can help counter side-channel attacks that would benefit from | |||
getting a large number of traces when the signer uses the same nodes. */ | |||
#define SPX_OPTRAND_BYTES 32 | |||
#endif |
@@ -0,0 +1,344 @@ | |||
#include <stddef.h> | |||
#include <stdint.h> | |||
#include <string.h> | |||
#include "address.h" | |||
#include "api.h" | |||
#include "fors.h" | |||
#include "hash.h" | |||
#include "params.h" | |||
#include "randombytes.h" | |||
#include "thash.h" | |||
#include "utils.h" | |||
#include "wots.h" | |||
/** | |||
* Computes the leaf at a given address. First generates the WOTS key pair, | |||
* then computes leaf by hashing horizontally. | |||
*/ | |||
static void wots_gen_leaf(unsigned char *leaf, const unsigned char *sk_seed, | |||
const unsigned char *pub_seed, | |||
uint32_t addr_idx, const uint32_t tree_addr[8]) { | |||
unsigned char pk[SPX_WOTS_BYTES]; | |||
uint32_t wots_addr[8] = {0}; | |||
uint32_t wots_pk_addr[8] = {0}; | |||
PQCLEAN_SPHINCSSHAKE256128FSIMPLE_CLEAN_set_type( | |||
wots_addr, SPX_ADDR_TYPE_WOTS); | |||
PQCLEAN_SPHINCSSHAKE256128FSIMPLE_CLEAN_set_type( | |||
wots_pk_addr, SPX_ADDR_TYPE_WOTSPK); | |||
PQCLEAN_SPHINCSSHAKE256128FSIMPLE_CLEAN_copy_subtree_addr( | |||
wots_addr, tree_addr); | |||
PQCLEAN_SPHINCSSHAKE256128FSIMPLE_CLEAN_set_keypair_addr( | |||
wots_addr, addr_idx); | |||
PQCLEAN_SPHINCSSHAKE256128FSIMPLE_CLEAN_wots_gen_pk( | |||
pk, sk_seed, pub_seed, wots_addr); | |||
PQCLEAN_SPHINCSSHAKE256128FSIMPLE_CLEAN_copy_keypair_addr( | |||
wots_pk_addr, wots_addr); | |||
PQCLEAN_SPHINCSSHAKE256128FSIMPLE_CLEAN_thash_WOTS_LEN( | |||
leaf, pk, pub_seed, wots_pk_addr); | |||
} | |||
/* | |||
* Returns the length of a secret key, in bytes | |||
*/ | |||
size_t PQCLEAN_SPHINCSSHAKE256128FSIMPLE_CLEAN_crypto_sign_secretkeybytes(void) { | |||
return PQCLEAN_SPHINCSSHAKE256128FSIMPLE_CLEAN_CRYPTO_SECRETKEYBYTES; | |||
} | |||
/* | |||
* Returns the length of a public key, in bytes | |||
*/ | |||
size_t PQCLEAN_SPHINCSSHAKE256128FSIMPLE_CLEAN_crypto_sign_publickeybytes(void) { | |||
return PQCLEAN_SPHINCSSHAKE256128FSIMPLE_CLEAN_CRYPTO_PUBLICKEYBYTES; | |||
} | |||
/* | |||
* Returns the length of a signature, in bytes | |||
*/ | |||
size_t PQCLEAN_SPHINCSSHAKE256128FSIMPLE_CLEAN_crypto_sign_bytes(void) { | |||
return PQCLEAN_SPHINCSSHAKE256128FSIMPLE_CLEAN_CRYPTO_BYTES; | |||
} | |||
/* | |||
* Returns the length of the seed required to generate a key pair, in bytes | |||
*/ | |||
size_t PQCLEAN_SPHINCSSHAKE256128FSIMPLE_CLEAN_crypto_sign_seedbytes(void) { | |||
return PQCLEAN_SPHINCSSHAKE256128FSIMPLE_CLEAN_CRYPTO_SEEDBYTES; | |||
} | |||
/* | |||
* Generates an SPX key pair given a seed of length | |||
* Format sk: [SK_SEED || SK_PRF || PUB_SEED || root] | |||
* Format pk: [PUB_SEED || root] | |||
*/ | |||
int PQCLEAN_SPHINCSSHAKE256128FSIMPLE_CLEAN_crypto_sign_seed_keypair( | |||
uint8_t *pk, uint8_t *sk, const uint8_t *seed) { | |||
/* We do not need the auth path in key generation, but it simplifies the | |||
code to have just one treehash routine that computes both root and path | |||
in one function. */ | |||
unsigned char auth_path[SPX_TREE_HEIGHT * SPX_N]; | |||
uint32_t top_tree_addr[8] = {0}; | |||
PQCLEAN_SPHINCSSHAKE256128FSIMPLE_CLEAN_set_layer_addr( | |||
top_tree_addr, SPX_D - 1); | |||
PQCLEAN_SPHINCSSHAKE256128FSIMPLE_CLEAN_set_type( | |||
top_tree_addr, SPX_ADDR_TYPE_HASHTREE); | |||
/* Initialize SK_SEED, SK_PRF and PUB_SEED from seed. */ | |||
memcpy(sk, seed, PQCLEAN_SPHINCSSHAKE256128FSIMPLE_CLEAN_CRYPTO_SEEDBYTES); | |||
memcpy(pk, sk + 2 * SPX_N, SPX_N); | |||
/* This hook allows the hash function instantiation to do whatever | |||
preparation or computation it needs, based on the public seed. */ | |||
PQCLEAN_SPHINCSSHAKE256128FSIMPLE_CLEAN_initialize_hash_function(pk, sk); | |||
/* Compute root node of the top-most subtree. */ | |||
PQCLEAN_SPHINCSSHAKE256128FSIMPLE_CLEAN_treehash_TREE_HEIGHT( | |||
sk + 3 * SPX_N, auth_path, sk, sk + 2 * SPX_N, 0, 0, | |||
wots_gen_leaf, top_tree_addr); | |||
memcpy(pk + SPX_N, sk + 3 * SPX_N, SPX_N); | |||
return 0; | |||
} | |||
/* | |||
* Generates an SPX key pair. | |||
* Format sk: [SK_SEED || SK_PRF || PUB_SEED || root] | |||
* Format pk: [PUB_SEED || root] | |||
*/ | |||
int PQCLEAN_SPHINCSSHAKE256128FSIMPLE_CLEAN_crypto_sign_keypair( | |||
uint8_t *pk, uint8_t *sk) { | |||
unsigned char seed[PQCLEAN_SPHINCSSHAKE256128FSIMPLE_CLEAN_CRYPTO_SEEDBYTES]; | |||
randombytes(seed, PQCLEAN_SPHINCSSHAKE256128FSIMPLE_CLEAN_CRYPTO_SEEDBYTES); | |||
PQCLEAN_SPHINCSSHAKE256128FSIMPLE_CLEAN_crypto_sign_seed_keypair( | |||
pk, sk, seed); | |||
return 0; | |||
} | |||
/** | |||
* Returns an array containing a detached signature. | |||
*/ | |||
int PQCLEAN_SPHINCSSHAKE256128FSIMPLE_CLEAN_crypto_sign_signature( | |||
uint8_t *sig, size_t *siglen, | |||
const uint8_t *m, size_t mlen, const uint8_t *sk) { | |||
const unsigned char *sk_seed = sk; | |||
const unsigned char *sk_prf = sk + SPX_N; | |||
const unsigned char *pk = sk + 2 * SPX_N; | |||
const unsigned char *pub_seed = pk; | |||
unsigned char optrand[SPX_N]; | |||
unsigned char mhash[SPX_FORS_MSG_BYTES]; | |||
unsigned char root[SPX_N]; | |||
uint32_t i; | |||
uint64_t tree; | |||
uint32_t idx_leaf; | |||
uint32_t wots_addr[8] = {0}; | |||
uint32_t tree_addr[8] = {0}; | |||
/* This hook allows the hash function instantiation to do whatever | |||
preparation or computation it needs, based on the public seed. */ | |||
PQCLEAN_SPHINCSSHAKE256128FSIMPLE_CLEAN_initialize_hash_function( | |||
pub_seed, sk_seed); | |||
PQCLEAN_SPHINCSSHAKE256128FSIMPLE_CLEAN_set_type( | |||
wots_addr, SPX_ADDR_TYPE_WOTS); | |||
PQCLEAN_SPHINCSSHAKE256128FSIMPLE_CLEAN_set_type( | |||
tree_addr, SPX_ADDR_TYPE_HASHTREE); | |||
/* Optionally, signing can be made non-deterministic using optrand. | |||
This can help counter side-channel attacks that would benefit from | |||
getting a large number of traces when the signer uses the same nodes. */ | |||
randombytes(optrand, SPX_N); | |||
/* Compute the digest randomization value. */ | |||
PQCLEAN_SPHINCSSHAKE256128FSIMPLE_CLEAN_gen_message_random( | |||
sig, sk_prf, optrand, m, mlen); | |||
/* Derive the message digest and leaf index from R, PK and M. */ | |||
PQCLEAN_SPHINCSSHAKE256128FSIMPLE_CLEAN_hash_message( | |||
mhash, &tree, &idx_leaf, sig, pk, m, mlen); | |||
sig += SPX_N; | |||
PQCLEAN_SPHINCSSHAKE256128FSIMPLE_CLEAN_set_tree_addr(wots_addr, tree); | |||
PQCLEAN_SPHINCSSHAKE256128FSIMPLE_CLEAN_set_keypair_addr( | |||
wots_addr, idx_leaf); | |||
/* Sign the message hash using FORS. */ | |||
PQCLEAN_SPHINCSSHAKE256128FSIMPLE_CLEAN_fors_sign( | |||
sig, root, mhash, sk_seed, pub_seed, wots_addr); | |||
sig += SPX_FORS_BYTES; | |||
for (i = 0; i < SPX_D; i++) { | |||
PQCLEAN_SPHINCSSHAKE256128FSIMPLE_CLEAN_set_layer_addr(tree_addr, i); | |||
PQCLEAN_SPHINCSSHAKE256128FSIMPLE_CLEAN_set_tree_addr(tree_addr, tree); | |||
PQCLEAN_SPHINCSSHAKE256128FSIMPLE_CLEAN_copy_subtree_addr( | |||
wots_addr, tree_addr); | |||
PQCLEAN_SPHINCSSHAKE256128FSIMPLE_CLEAN_set_keypair_addr( | |||
wots_addr, idx_leaf); | |||
/* Compute a WOTS signature. */ | |||
PQCLEAN_SPHINCSSHAKE256128FSIMPLE_CLEAN_wots_sign( | |||
sig, root, sk_seed, pub_seed, wots_addr); | |||
sig += SPX_WOTS_BYTES; | |||
/* Compute the authentication path for the used WOTS leaf. */ | |||
PQCLEAN_SPHINCSSHAKE256128FSIMPLE_CLEAN_treehash_TREE_HEIGHT( | |||
root, sig, sk_seed, pub_seed, idx_leaf, 0, | |||
wots_gen_leaf, tree_addr); | |||
sig += SPX_TREE_HEIGHT * SPX_N; | |||
/* Update the indices for the next layer. */ | |||
idx_leaf = (tree & ((1 << SPX_TREE_HEIGHT) - 1)); | |||
tree = tree >> SPX_TREE_HEIGHT; | |||
} | |||
*siglen = SPX_BYTES; | |||
return 0; | |||
} | |||
/** | |||
* Verifies a detached signature and message under a given public key. | |||
*/ | |||
int PQCLEAN_SPHINCSSHAKE256128FSIMPLE_CLEAN_crypto_sign_verify( | |||
const uint8_t *sig, size_t siglen, | |||
const uint8_t *m, size_t mlen, const uint8_t *pk) { | |||
const unsigned char *pub_seed = pk; | |||
const unsigned char *pub_root = pk + SPX_N; | |||
unsigned char mhash[SPX_FORS_MSG_BYTES]; | |||
unsigned char wots_pk[SPX_WOTS_BYTES]; | |||
unsigned char root[SPX_N]; | |||
unsigned char leaf[SPX_N]; | |||
unsigned int i; | |||
uint64_t tree; | |||
uint32_t idx_leaf; | |||
uint32_t wots_addr[8] = {0}; | |||
uint32_t tree_addr[8] = {0}; | |||
uint32_t wots_pk_addr[8] = {0}; | |||
if (siglen != SPX_BYTES) { | |||
return -1; | |||
} | |||
/* This hook allows the hash function instantiation to do whatever | |||
preparation or computation it needs, based on the public seed. */ | |||
PQCLEAN_SPHINCSSHAKE256128FSIMPLE_CLEAN_initialize_hash_function( | |||
pub_seed, NULL); | |||
PQCLEAN_SPHINCSSHAKE256128FSIMPLE_CLEAN_set_type( | |||
wots_addr, SPX_ADDR_TYPE_WOTS); | |||
PQCLEAN_SPHINCSSHAKE256128FSIMPLE_CLEAN_set_type( | |||
tree_addr, SPX_ADDR_TYPE_HASHTREE); | |||
PQCLEAN_SPHINCSSHAKE256128FSIMPLE_CLEAN_set_type( | |||
wots_pk_addr, SPX_ADDR_TYPE_WOTSPK); | |||
/* Derive the message digest and leaf index from R || PK || M. */ | |||
/* The additional SPX_N is a result of the hash domain separator. */ | |||
PQCLEAN_SPHINCSSHAKE256128FSIMPLE_CLEAN_hash_message( | |||
mhash, &tree, &idx_leaf, sig, pk, m, mlen); | |||
sig += SPX_N; | |||
/* Layer correctly defaults to 0, so no need to set_layer_addr */ | |||
PQCLEAN_SPHINCSSHAKE256128FSIMPLE_CLEAN_set_tree_addr(wots_addr, tree); | |||
PQCLEAN_SPHINCSSHAKE256128FSIMPLE_CLEAN_set_keypair_addr( | |||
wots_addr, idx_leaf); | |||
PQCLEAN_SPHINCSSHAKE256128FSIMPLE_CLEAN_fors_pk_from_sig( | |||
root, sig, mhash, pub_seed, wots_addr); | |||
sig += SPX_FORS_BYTES; | |||
/* For each subtree.. */ | |||
for (i = 0; i < SPX_D; i++) { | |||
PQCLEAN_SPHINCSSHAKE256128FSIMPLE_CLEAN_set_layer_addr(tree_addr, i); | |||
PQCLEAN_SPHINCSSHAKE256128FSIMPLE_CLEAN_set_tree_addr(tree_addr, tree); | |||
PQCLEAN_SPHINCSSHAKE256128FSIMPLE_CLEAN_copy_subtree_addr( | |||
wots_addr, tree_addr); | |||
PQCLEAN_SPHINCSSHAKE256128FSIMPLE_CLEAN_set_keypair_addr( | |||
wots_addr, idx_leaf); | |||
PQCLEAN_SPHINCSSHAKE256128FSIMPLE_CLEAN_copy_keypair_addr( | |||
wots_pk_addr, wots_addr); | |||
/* The WOTS public key is only correct if the signature was correct. */ | |||
/* Initially, root is the FORS pk, but on subsequent iterations it is | |||
the root of the subtree below the currently processed subtree. */ | |||
PQCLEAN_SPHINCSSHAKE256128FSIMPLE_CLEAN_wots_pk_from_sig( | |||
wots_pk, sig, root, pub_seed, wots_addr); | |||
sig += SPX_WOTS_BYTES; | |||
/* Compute the leaf node using the WOTS public key. */ | |||
PQCLEAN_SPHINCSSHAKE256128FSIMPLE_CLEAN_thash_WOTS_LEN( | |||
leaf, wots_pk, pub_seed, wots_pk_addr); | |||
/* Compute the root node of this subtree. */ | |||
PQCLEAN_SPHINCSSHAKE256128FSIMPLE_CLEAN_compute_root( | |||
root, leaf, idx_leaf, 0, sig, SPX_TREE_HEIGHT, | |||
pub_seed, tree_addr); | |||
sig += SPX_TREE_HEIGHT * SPX_N; | |||
/* Update the indices for the next layer. */ | |||
idx_leaf = (tree & ((1 << SPX_TREE_HEIGHT) - 1)); | |||
tree = tree >> SPX_TREE_HEIGHT; | |||
} | |||
/* Check if the root node equals the root node in the public key. */ | |||
if (memcmp(root, pub_root, SPX_N) != 0) { | |||
return -1; | |||
} | |||
return 0; | |||
} | |||
/** | |||
* Returns an array containing the signature followed by the message. | |||
*/ | |||
int PQCLEAN_SPHINCSSHAKE256128FSIMPLE_CLEAN_crypto_sign( | |||
uint8_t *sm, size_t *smlen, | |||
const uint8_t *m, size_t mlen, const uint8_t *sk) { | |||
size_t siglen; | |||
PQCLEAN_SPHINCSSHAKE256128FSIMPLE_CLEAN_crypto_sign_signature( | |||
sm, &siglen, m, mlen, sk); | |||
memmove(sm + SPX_BYTES, m, mlen); | |||
*smlen = siglen + mlen; | |||
return 0; | |||
} | |||
/** | |||
* Verifies a given signature-message pair under a given public key. | |||
*/ | |||
int PQCLEAN_SPHINCSSHAKE256128FSIMPLE_CLEAN_crypto_sign_open( | |||
uint8_t *m, size_t *mlen, | |||
const uint8_t *sm, size_t smlen, const uint8_t *pk) { | |||
/* The API caller does not necessarily know what size a signature should be | |||
but SPHINCS+ signatures are always exactly SPX_BYTES. */ | |||
if (smlen < SPX_BYTES) { | |||
memset(m, 0, smlen); | |||
*mlen = 0; | |||
return -1; | |||
} | |||
*mlen = smlen - SPX_BYTES; | |||
if (PQCLEAN_SPHINCSSHAKE256128FSIMPLE_CLEAN_crypto_sign_verify( | |||
sm, SPX_BYTES, sm + SPX_BYTES, *mlen, pk)) { | |||
memset(m, 0, smlen); | |||
*mlen = 0; | |||
return -1; | |||
} | |||
/* If verification was successful, move the message to the right place. */ | |||
memmove(m, sm + SPX_BYTES, *mlen); | |||
return 0; | |||
} |
@@ -0,0 +1,22 @@ | |||
#ifndef SPX_THASH_H | |||
#define SPX_THASH_H | |||
#include <stdint.h> | |||
void PQCLEAN_SPHINCSSHAKE256128FSIMPLE_CLEAN_thash_1( | |||
unsigned char *out, const unsigned char *in, | |||
const unsigned char *pub_seed, uint32_t addr[8]); | |||
void PQCLEAN_SPHINCSSHAKE256128FSIMPLE_CLEAN_thash_2( | |||
unsigned char *out, const unsigned char *in, | |||
const unsigned char *pub_seed, uint32_t addr[8]); | |||
void PQCLEAN_SPHINCSSHAKE256128FSIMPLE_CLEAN_thash_WOTS_LEN( | |||
unsigned char *out, const unsigned char *in, | |||
const unsigned char *pub_seed, uint32_t addr[8]); | |||
void PQCLEAN_SPHINCSSHAKE256128FSIMPLE_CLEAN_thash_FORS_TREES( | |||
unsigned char *out, const unsigned char *in, | |||
const unsigned char *pub_seed, uint32_t addr[8]); | |||
#endif |
@@ -0,0 +1,61 @@ | |||
#include <stdint.h> | |||
#include <string.h> | |||
#include "thash.h" | |||
#include "address.h" | |||
#include "params.h" | |||
#include "fips202.h" | |||
/** | |||
* Takes an array of inblocks concatenated arrays of SPX_N bytes. | |||
*/ | |||
static void PQCLEAN_SPHINCSSHAKE256128FSIMPLE_CLEAN_thash( | |||
unsigned char *out, unsigned char *buf, | |||
const unsigned char *in, unsigned int inblocks, | |||
const unsigned char *pub_seed, uint32_t addr[8]) { | |||
memcpy(buf, pub_seed, SPX_N); | |||
PQCLEAN_SPHINCSSHAKE256128FSIMPLE_CLEAN_addr_to_bytes(buf + SPX_N, addr); | |||
memcpy(buf + SPX_N + SPX_ADDR_BYTES, in, inblocks * SPX_N); | |||
shake256(out, SPX_N, buf, SPX_N + SPX_ADDR_BYTES + inblocks * SPX_N); | |||
} | |||
/* The wrappers below ensure that we use fixed-size buffers on the stack */ | |||
void PQCLEAN_SPHINCSSHAKE256128FSIMPLE_CLEAN_thash_1( | |||
unsigned char *out, const unsigned char *in, | |||
const unsigned char *pub_seed, uint32_t addr[8]) { | |||
unsigned char buf[SPX_N + SPX_ADDR_BYTES + 1 * SPX_N]; | |||
PQCLEAN_SPHINCSSHAKE256128FSIMPLE_CLEAN_thash( | |||
out, buf, in, 1, pub_seed, addr); | |||
} | |||
void PQCLEAN_SPHINCSSHAKE256128FSIMPLE_CLEAN_thash_2( | |||
unsigned char *out, const unsigned char *in, | |||
const unsigned char *pub_seed, uint32_t addr[8]) { | |||
unsigned char buf[SPX_N + SPX_ADDR_BYTES + 2 * SPX_N]; | |||
PQCLEAN_SPHINCSSHAKE256128FSIMPLE_CLEAN_thash( | |||
out, buf, in, 2, pub_seed, addr); | |||
} | |||
void PQCLEAN_SPHINCSSHAKE256128FSIMPLE_CLEAN_thash_WOTS_LEN( | |||
unsigned char *out, const unsigned char *in, | |||
const unsigned char *pub_seed, uint32_t addr[8]) { | |||
unsigned char buf[SPX_N + SPX_ADDR_BYTES + SPX_WOTS_LEN * SPX_N]; | |||
PQCLEAN_SPHINCSSHAKE256128FSIMPLE_CLEAN_thash( | |||
out, buf, in, SPX_WOTS_LEN, pub_seed, addr); | |||
} | |||
void PQCLEAN_SPHINCSSHAKE256128FSIMPLE_CLEAN_thash_FORS_TREES( | |||
unsigned char *out, const unsigned char *in, | |||
const unsigned char *pub_seed, uint32_t addr[8]) { | |||
unsigned char buf[SPX_N + SPX_ADDR_BYTES + SPX_FORS_TREES * SPX_N]; | |||
PQCLEAN_SPHINCSSHAKE256128FSIMPLE_CLEAN_thash( | |||
out, buf, in, SPX_FORS_TREES, pub_seed, addr); | |||
} |
@@ -0,0 +1,192 @@ | |||
#include <stddef.h> | |||
#include <string.h> | |||
#include "address.h" | |||
#include "hash.h" | |||
#include "params.h" | |||
#include "thash.h" | |||
#include "utils.h" | |||
/** | |||
* Converts the value of 'in' to 'outlen' bytes in big-endian byte order. | |||
*/ | |||
void PQCLEAN_SPHINCSSHAKE256128FSIMPLE_CLEAN_ull_to_bytes( | |||
unsigned char *out, size_t outlen, unsigned long long in) { | |||
/* Iterate over out in decreasing order, for big-endianness. */ | |||
for (size_t i = outlen; i > 0; i--) { | |||
out[i - 1] = in & 0xff; | |||
in = in >> 8; | |||
} | |||
} | |||
/** | |||
* Converts the inlen bytes in 'in' from big-endian byte order to an integer. | |||
*/ | |||
unsigned long long PQCLEAN_SPHINCSSHAKE256128FSIMPLE_CLEAN_bytes_to_ull( | |||
const unsigned char *in, size_t inlen) { | |||
unsigned long long retval = 0; | |||
for (size_t i = 0; i < inlen; i++) { | |||
retval |= ((unsigned long long)in[i]) << (8 * (inlen - 1 - i)); | |||
} | |||
return retval; | |||
} | |||
/** | |||
* Computes a root node given a leaf and an auth path. | |||
* Expects address to be complete other than the tree_height and tree_index. | |||
*/ | |||
void PQCLEAN_SPHINCSSHAKE256128FSIMPLE_CLEAN_compute_root( | |||
unsigned char *root, const unsigned char *leaf, | |||
uint32_t leaf_idx, uint32_t idx_offset, | |||
const unsigned char *auth_path, uint32_t tree_height, | |||
const unsigned char *pub_seed, uint32_t addr[8]) { | |||
uint32_t i; | |||
unsigned char buffer[2 * SPX_N]; | |||
/* If leaf_idx is odd (last bit = 1), current path element is a right child | |||
and auth_path has to go left. Otherwise it is the other way around. */ | |||
if (leaf_idx & 1) { | |||
memcpy(buffer + SPX_N, leaf, SPX_N); | |||
memcpy(buffer, auth_path, SPX_N); | |||
} else { | |||
memcpy(buffer, leaf, SPX_N); | |||
memcpy(buffer + SPX_N, auth_path, SPX_N); | |||
} | |||
auth_path += SPX_N; | |||
for (i = 0; i < tree_height - 1; i++) { | |||
leaf_idx >>= 1; | |||
idx_offset >>= 1; | |||
/* Set the address of the node we're creating. */ | |||
PQCLEAN_SPHINCSSHAKE256128FSIMPLE_CLEAN_set_tree_height(addr, i + 1); | |||
PQCLEAN_SPHINCSSHAKE256128FSIMPLE_CLEAN_set_tree_index( | |||
addr, leaf_idx + idx_offset); | |||
/* Pick the right or left neighbor, depending on parity of the node. */ | |||
if (leaf_idx & 1) { | |||
PQCLEAN_SPHINCSSHAKE256128FSIMPLE_CLEAN_thash_2( | |||
buffer + SPX_N, buffer, pub_seed, addr); | |||
memcpy(buffer, auth_path, SPX_N); | |||
} else { | |||
PQCLEAN_SPHINCSSHAKE256128FSIMPLE_CLEAN_thash_2( | |||
buffer, buffer, pub_seed, addr); | |||
memcpy(buffer + SPX_N, auth_path, SPX_N); | |||
} | |||
auth_path += SPX_N; | |||
} | |||
/* The last iteration is exceptional; we do not copy an auth_path node. */ | |||
leaf_idx >>= 1; | |||
idx_offset >>= 1; | |||
PQCLEAN_SPHINCSSHAKE256128FSIMPLE_CLEAN_set_tree_height(addr, tree_height); | |||
PQCLEAN_SPHINCSSHAKE256128FSIMPLE_CLEAN_set_tree_index( | |||
addr, leaf_idx + idx_offset); | |||
PQCLEAN_SPHINCSSHAKE256128FSIMPLE_CLEAN_thash_2( | |||
root, buffer, pub_seed, addr); | |||
} | |||
/** | |||
* For a given leaf index, computes the authentication path and the resulting | |||
* root node using Merkle's TreeHash algorithm. | |||
* Expects the layer and tree parts of the tree_addr to be set, as well as the | |||
* tree type (i.e. SPX_ADDR_TYPE_HASHTREE or SPX_ADDR_TYPE_FORSTREE). | |||
* Applies the offset idx_offset to indices before building addresses, so that | |||
* it is possible to continue counting indices across trees. | |||
*/ | |||
static void PQCLEAN_SPHINCSSHAKE256128FSIMPLE_CLEAN_treehash( | |||
unsigned char *root, unsigned char *auth_path, | |||
unsigned char *stack, unsigned int *heights, | |||
const unsigned char *sk_seed, const unsigned char *pub_seed, | |||
uint32_t leaf_idx, uint32_t idx_offset, uint32_t tree_height, | |||
void (*gen_leaf)( | |||
unsigned char * /* leaf */, | |||
const unsigned char * /* sk_seed */, | |||
const unsigned char * /* pub_seed */, | |||
uint32_t /* addr_idx */, const uint32_t[8] /* tree_addr */), | |||
uint32_t tree_addr[8]) { | |||
unsigned int offset = 0; | |||
uint32_t idx; | |||
uint32_t tree_idx; | |||
for (idx = 0; idx < (uint32_t)(1 << tree_height); idx++) { | |||
/* Add the next leaf node to the stack. */ | |||
gen_leaf(stack + offset * SPX_N, | |||
sk_seed, pub_seed, idx + idx_offset, tree_addr); | |||
offset++; | |||
heights[offset - 1] = 0; | |||
/* If this is a node we need for the auth path.. */ | |||
if ((leaf_idx ^ 0x1) == idx) { | |||
memcpy(auth_path, stack + (offset - 1)*SPX_N, SPX_N); | |||
} | |||
/* While the top-most nodes are of equal height.. */ | |||
while (offset >= 2 && heights[offset - 1] == heights[offset - 2]) { | |||
/* Compute index of the new node, in the next layer. */ | |||
tree_idx = (idx >> (heights[offset - 1] + 1)); | |||
/* Set the address of the node we're creating. */ | |||
PQCLEAN_SPHINCSSHAKE256128FSIMPLE_CLEAN_set_tree_height( | |||
tree_addr, heights[offset - 1] + 1); | |||
PQCLEAN_SPHINCSSHAKE256128FSIMPLE_CLEAN_set_tree_index( | |||
tree_addr, tree_idx + (idx_offset >> (heights[offset - 1] + 1))); | |||
/* Hash the top-most nodes from the stack together. */ | |||
PQCLEAN_SPHINCSSHAKE256128FSIMPLE_CLEAN_thash_2( | |||
stack + (offset - 2)*SPX_N, stack + (offset - 2)*SPX_N, | |||
pub_seed, tree_addr); | |||
offset--; | |||
/* Note that the top-most node is now one layer higher. */ | |||
heights[offset - 1]++; | |||
/* If this is a node we need for the auth path.. */ | |||
if (((leaf_idx >> heights[offset - 1]) ^ 0x1) == tree_idx) { | |||
memcpy(auth_path + heights[offset - 1]*SPX_N, | |||
stack + (offset - 1)*SPX_N, SPX_N); | |||
} | |||
} | |||
} | |||
memcpy(root, stack, SPX_N); | |||
} | |||
/* The wrappers below ensure that we use fixed-size buffers on the stack */ | |||
void PQCLEAN_SPHINCSSHAKE256128FSIMPLE_CLEAN_treehash_FORS_HEIGHT( | |||
unsigned char *root, unsigned char *auth_path, | |||
const unsigned char *sk_seed, const unsigned char *pub_seed, | |||
uint32_t leaf_idx, uint32_t idx_offset, | |||
void (*gen_leaf)( | |||
unsigned char * /* leaf */, | |||
const unsigned char * /* sk_seed */, | |||
const unsigned char * /* pub_seed */, | |||
uint32_t /* addr_idx */, const uint32_t[8] /* tree_addr */), | |||
uint32_t tree_addr[8]) { | |||
unsigned char stack[(SPX_FORS_HEIGHT + 1)*SPX_N]; | |||
unsigned int heights[SPX_FORS_HEIGHT + 1]; | |||
PQCLEAN_SPHINCSSHAKE256128FSIMPLE_CLEAN_treehash( | |||
root, auth_path, stack, heights, sk_seed, pub_seed, | |||
leaf_idx, idx_offset, SPX_FORS_HEIGHT, gen_leaf, tree_addr); | |||
} | |||
void PQCLEAN_SPHINCSSHAKE256128FSIMPLE_CLEAN_treehash_TREE_HEIGHT( | |||
unsigned char *root, unsigned char *auth_path, | |||
const unsigned char *sk_seed, const unsigned char *pub_seed, | |||
uint32_t leaf_idx, uint32_t idx_offset, | |||
void (*gen_leaf)( | |||
unsigned char * /* leaf */, | |||
const unsigned char * /* sk_seed */, | |||
const unsigned char * /* pub_seed */, | |||
uint32_t /* addr_idx */, const uint32_t[8] /* tree_addr */), | |||
uint32_t tree_addr[8]) { | |||
unsigned char stack[(SPX_TREE_HEIGHT + 1)*SPX_N]; | |||
unsigned int heights[SPX_TREE_HEIGHT + 1]; | |||
PQCLEAN_SPHINCSSHAKE256128FSIMPLE_CLEAN_treehash( | |||
root, auth_path, stack, heights, sk_seed, pub_seed, | |||
leaf_idx, idx_offset, SPX_TREE_HEIGHT, gen_leaf, tree_addr); | |||
} |
@@ -0,0 +1,60 @@ | |||
#ifndef SPX_UTILS_H | |||
#define SPX_UTILS_H | |||
#include "params.h" | |||
#include <stddef.h> | |||
#include <stdint.h> | |||
/** | |||
* Converts the value of 'in' to 'outlen' bytes in big-endian byte order. | |||
*/ | |||
void PQCLEAN_SPHINCSSHAKE256128FSIMPLE_CLEAN_ull_to_bytes( | |||
unsigned char *out, size_t outlen, unsigned long long in); | |||
/** | |||
* Converts the inlen bytes in 'in' from big-endian byte order to an integer. | |||
*/ | |||
unsigned long long PQCLEAN_SPHINCSSHAKE256128FSIMPLE_CLEAN_bytes_to_ull( | |||
const unsigned char *in, size_t inlen); | |||
/** | |||
* Computes a root node given a leaf and an auth path. | |||
* Expects address to be complete other than the tree_height and tree_index. | |||
*/ | |||
void PQCLEAN_SPHINCSSHAKE256128FSIMPLE_CLEAN_compute_root( | |||
unsigned char *root, const unsigned char *leaf, | |||
uint32_t leaf_idx, uint32_t idx_offset, | |||
const unsigned char *auth_path, uint32_t tree_height, | |||
const unsigned char *pub_seed, uint32_t addr[8]); | |||
/** | |||
* For a given leaf index, computes the authentication path and the resulting | |||
* root node using Merkle's TreeHash algorithm. | |||
* Expects the layer and tree parts of the tree_addr to be set, as well as the | |||
* tree type (i.e. SPX_ADDR_TYPE_HASHTREE or SPX_ADDR_TYPE_FORSTREE). | |||
* Applies the offset idx_offset to indices before building addresses, so that | |||
* it is possible to continue counting indices across trees. | |||
*/ | |||
void PQCLEAN_SPHINCSSHAKE256128FSIMPLE_CLEAN_treehash_FORS_HEIGHT( | |||
unsigned char *root, unsigned char *auth_path, | |||
const unsigned char *sk_seed, const unsigned char *pub_seed, | |||
uint32_t leaf_idx, uint32_t idx_offset, | |||
void (*gen_leaf)( | |||
unsigned char * /* leaf */, | |||
const unsigned char * /* sk_seed */, | |||
const unsigned char * /* pub_seed */, | |||
uint32_t /* addr_idx */, const uint32_t[8] /* tree_addr */), | |||
uint32_t tree_addr[8]); | |||
void PQCLEAN_SPHINCSSHAKE256128FSIMPLE_CLEAN_treehash_TREE_HEIGHT( | |||
unsigned char *root, unsigned char *auth_path, | |||
const unsigned char *sk_seed, const unsigned char *pub_seed, | |||
uint32_t leaf_idx, uint32_t idx_offset, | |||
void (*gen_leaf)( | |||
unsigned char * /* leaf */, | |||
const unsigned char * /* sk_seed */, | |||
const unsigned char * /* pub_seed */, | |||
uint32_t /* addr_idx */, const uint32_t[8] /* tree_addr */), | |||
uint32_t tree_addr[8]); | |||
#endif |
@@ -0,0 +1,159 @@ | |||
#include <stdint.h> | |||
#include <string.h> | |||
#include "utils.h" | |||
#include "address.h" | |||
#include "hash.h" | |||
#include "params.h" | |||
#include "thash.h" | |||
#include "wots.h" | |||
// TODO clarify address expectations, and make them more uniform. | |||
// TODO i.e. do we expect types to be set already? | |||
// TODO and do we expect modifications or copies? | |||
/** | |||
* Computes the starting value for a chain, i.e. the secret key. | |||
* Expects the address to be complete up to the chain address. | |||
*/ | |||
static void wots_gen_sk(unsigned char *sk, const unsigned char *sk_seed, | |||
uint32_t wots_addr[8]) { | |||
/* Make sure that the hash address is actually zeroed. */ | |||
PQCLEAN_SPHINCSSHAKE256128FSIMPLE_CLEAN_set_hash_addr(wots_addr, 0); | |||
/* Generate sk element. */ | |||
PQCLEAN_SPHINCSSHAKE256128FSIMPLE_CLEAN_prf_addr(sk, sk_seed, wots_addr); | |||
} | |||
/** | |||
* Computes the chaining function. | |||
* out and in have to be n-byte arrays. | |||
* | |||
* Interprets in as start-th value of the chain. | |||
* addr has to contain the address of the chain. | |||
*/ | |||
static void gen_chain(unsigned char *out, const unsigned char *in, | |||
unsigned int start, unsigned int steps, | |||
const unsigned char *pub_seed, uint32_t addr[8]) { | |||
uint32_t i; | |||
/* Initialize out with the value at position 'start'. */ | |||
memcpy(out, in, SPX_N); | |||
/* Iterate 'steps' calls to the hash function. */ | |||
for (i = start; i < (start + steps) && i < SPX_WOTS_W; i++) { | |||
PQCLEAN_SPHINCSSHAKE256128FSIMPLE_CLEAN_set_hash_addr(addr, i); | |||
PQCLEAN_SPHINCSSHAKE256128FSIMPLE_CLEAN_thash_1( | |||
out, out, pub_seed, addr); | |||
} | |||
} | |||
/** | |||
* base_w algorithm as described in draft. | |||
* Interprets an array of bytes as integers in base w. | |||
* This only works when log_w is a divisor of 8. | |||
*/ | |||
static void base_w(int *output, const int out_len, const unsigned char *input) { | |||
int in = 0; | |||
int out = 0; | |||
unsigned char total = 0; | |||
int bits = 0; | |||
int consumed; | |||
for (consumed = 0; consumed < out_len; consumed++) { | |||
if (bits == 0) { | |||
total = input[in]; | |||
in++; | |||
bits += 8; | |||
} | |||
bits -= SPX_WOTS_LOGW; | |||
output[out] = (total >> bits) & (SPX_WOTS_W - 1); | |||
out++; | |||
} | |||
} | |||
/* Computes the WOTS+ checksum over a message (in base_w). */ | |||
static void wots_checksum(int *csum_base_w, const int *msg_base_w) { | |||
int csum = 0; | |||
unsigned char csum_bytes[(SPX_WOTS_LEN2 * SPX_WOTS_LOGW + 7) / 8]; | |||
unsigned int i; | |||
/* Compute checksum. */ | |||
for (i = 0; i < SPX_WOTS_LEN1; i++) { | |||
csum += SPX_WOTS_W - 1 - msg_base_w[i]; | |||
} | |||
/* Convert checksum to base_w. */ | |||
/* Make sure expected empty zero bits are the least significant bits. */ | |||
csum = csum << (8 - ((SPX_WOTS_LEN2 * SPX_WOTS_LOGW) % 8)); | |||
PQCLEAN_SPHINCSSHAKE256128FSIMPLE_CLEAN_ull_to_bytes( | |||
csum_bytes, sizeof(csum_bytes), csum); | |||
base_w(csum_base_w, SPX_WOTS_LEN2, csum_bytes); | |||
} | |||
/* Takes a message and derives the matching chain lengths. */ | |||
static void chain_lengths(int *lengths, const unsigned char *msg) { | |||
base_w(lengths, SPX_WOTS_LEN1, msg); | |||
wots_checksum(lengths + SPX_WOTS_LEN1, lengths); | |||
} | |||
/** | |||
* WOTS key generation. Takes a 32 byte sk_seed, expands it to WOTS private key | |||
* elements and computes the corresponding public key. | |||
* It requires the seed pub_seed (used to generate bitmasks and hash keys) | |||
* and the address of this WOTS key pair. | |||
* | |||
* Writes the computed public key to 'pk'. | |||
*/ | |||
void PQCLEAN_SPHINCSSHAKE256128FSIMPLE_CLEAN_wots_gen_pk( | |||
unsigned char *pk, const unsigned char *sk_seed, | |||
const unsigned char *pub_seed, uint32_t addr[8]) { | |||
uint32_t i; | |||
for (i = 0; i < SPX_WOTS_LEN; i++) { | |||
PQCLEAN_SPHINCSSHAKE256128FSIMPLE_CLEAN_set_chain_addr(addr, i); | |||
wots_gen_sk(pk + i * SPX_N, sk_seed, addr); | |||
gen_chain(pk + i * SPX_N, pk + i * SPX_N, | |||
0, SPX_WOTS_W - 1, pub_seed, addr); | |||
} | |||
} | |||
/** | |||
* Takes a n-byte message and the 32-byte sk_see to compute a signature 'sig'. | |||
*/ | |||
void PQCLEAN_SPHINCSSHAKE256128FSIMPLE_CLEAN_wots_sign( | |||
unsigned char *sig, const unsigned char *msg, | |||
const unsigned char *sk_seed, const unsigned char *pub_seed, | |||
uint32_t addr[8]) { | |||
int lengths[SPX_WOTS_LEN]; | |||
uint32_t i; | |||
chain_lengths(lengths, msg); | |||
for (i = 0; i < SPX_WOTS_LEN; i++) { | |||
PQCLEAN_SPHINCSSHAKE256128FSIMPLE_CLEAN_set_chain_addr(addr, i); | |||
wots_gen_sk(sig + i * SPX_N, sk_seed, addr); | |||
gen_chain(sig + i * SPX_N, sig + i * SPX_N, 0, lengths[i], pub_seed, addr); | |||
} | |||
} | |||
/** | |||
* Takes a WOTS signature and an n-byte message, computes a WOTS public key. | |||
* | |||
* Writes the computed public key to 'pk'. | |||
*/ | |||
void PQCLEAN_SPHINCSSHAKE256128FSIMPLE_CLEAN_wots_pk_from_sig( | |||
unsigned char *pk, | |||
const unsigned char *sig, const unsigned char *msg, | |||
const unsigned char *pub_seed, uint32_t addr[8]) { | |||
int lengths[SPX_WOTS_LEN]; | |||
uint32_t i; | |||
chain_lengths(lengths, msg); | |||
for (i = 0; i < SPX_WOTS_LEN; i++) { | |||
PQCLEAN_SPHINCSSHAKE256128FSIMPLE_CLEAN_set_chain_addr(addr, i); | |||
gen_chain(pk + i * SPX_N, sig + i * SPX_N, | |||
lengths[i], SPX_WOTS_W - 1 - lengths[i], pub_seed, addr); | |||
} | |||
} |
@@ -0,0 +1,38 @@ | |||
#ifndef SPX_WOTS_H | |||
#define SPX_WOTS_H | |||
#include "params.h" | |||
#include <stdint.h> | |||
/** | |||
* WOTS key generation. Takes a 32 byte seed for the private key, expands it to | |||
* a full WOTS private key and computes the corresponding public key. | |||
* It requires the seed pub_seed (used to generate bitmasks and hash keys) | |||
* and the address of this WOTS key pair. | |||
* | |||
* Writes the computed public key to 'pk'. | |||
*/ | |||
void PQCLEAN_SPHINCSSHAKE256128FSIMPLE_CLEAN_wots_gen_pk( | |||
unsigned char *pk, const unsigned char *sk_seed, | |||
const unsigned char *pub_seed, uint32_t addr[8]); | |||
/** | |||
* Takes a n-byte message and the 32-byte seed for the private key to compute a | |||
* signature that is placed at 'sig'. | |||
*/ | |||
void PQCLEAN_SPHINCSSHAKE256128FSIMPLE_CLEAN_wots_sign( | |||
unsigned char *sig, const unsigned char *msg, | |||
const unsigned char *sk_seed, const unsigned char *pub_seed, | |||
uint32_t addr[8]); | |||
/** | |||
* Takes a WOTS signature and an n-byte message, computes a WOTS public key. | |||
* | |||
* Writes the computed public key to 'pk'. | |||
*/ | |||
void PQCLEAN_SPHINCSSHAKE256128FSIMPLE_CLEAN_wots_pk_from_sig( | |||
unsigned char *pk, | |||
const unsigned char *sig, const unsigned char *msg, | |||
const unsigned char *pub_seed, uint32_t addr[8]); | |||
#endif |
@@ -15,7 +15,10 @@ COMMON_HEADERS=$(COMMON_DIR)/*.h | |||
DEST_DIR=../bin | |||
# This -Wall was supported by the European Commission through the ERC Starting Grant 805031 (EPOQUE) | |||
CFLAGS=-Wall -Wextra -Wpedantic -Werror -Wundef -std=c99 -I$(COMMON_DIR) $(EXTRAFLAGS) | |||
CFLAGS=-Wall -Wextra -Wpedantic -Werror -std=c99 \ | |||
-Wundef -Wshadow -Wcast-align -Wpointer-arith \ | |||
-fstrict-aliasing -fno-common -pipe \ | |||
-I$(COMMON_DIR) $(EXTRAFLAGS) | |||
all: $(DEST_DIR)/functest_$(SCHEME)_$(IMPLEMENTATION) \ | |||
$(DEST_DIR)/testvectors_$(SCHEME)_$(IMPLEMENTATION) \ | |||
@@ -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) | |||
@@ -5,7 +5,7 @@ | |||
#include "api.h" | |||
#include "randombytes.h" | |||
#define NTESTS 10 | |||
#define NTESTS 5 | |||
const uint8_t canary[8] = { | |||
0x01, 0x23, 0x45, 0x67, 0x89, 0xAB, 0xCD, 0xEF | |||
@@ -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; | |||
} | |||
} | |||
@@ -6,7 +6,7 @@ | |||
#include "api.h" | |||
#include "randombytes.h" | |||
#define NTESTS 100 | |||
#define NTESTS 5 | |||
static void printbytes(const uint8_t *x, size_t xlen) { | |||
size_t i; | |||
@@ -6,7 +6,7 @@ | |||
#include "api.h" | |||
#include "randombytes.h" | |||
#define NTESTS 15 | |||
#define NTESTS 5 | |||
#define MLEN 32 | |||
const uint8_t canary[8] = { | |||
@@ -44,6 +44,8 @@ static int check_canary(const uint8_t *d) { | |||
#define crypto_sign_keypair NAMESPACE(crypto_sign_keypair) | |||
#define crypto_sign NAMESPACE(crypto_sign) | |||
#define crypto_sign_open NAMESPACE(crypto_sign_open) | |||
#define crypto_sign_signature NAMESPACE(crypto_sign_signature) | |||
#define crypto_sign_verify NAMESPACE(crypto_sign_verify) | |||
#define RETURNS_ZERO(f) \ | |||
if ((f) != 0) { \ | |||
@@ -133,6 +135,74 @@ static int test_sign(void) { | |||
return 0; | |||
} | |||
static int test_sign_detached(void) { | |||
/* | |||
* This is most likely going to be aligned by the compiler. | |||
* 16 extra bytes for canary | |||
* 1 extra byte for unalignment | |||
*/ | |||
uint8_t pk_aligned[CRYPTO_PUBLICKEYBYTES + 16 + 1]; | |||
uint8_t sk_aligned[CRYPTO_SECRETKEYBYTES + 16 + 1]; | |||
uint8_t sig_aligned[CRYPTO_BYTES + 16 + 1]; | |||
uint8_t m_aligned[MLEN + 16 + 1]; | |||
/* | |||
* Make sure all pointers are odd. | |||
* This ensures that the implementation does not assume anything about the | |||
* data alignment. For example this would catch if an implementation | |||
* directly uses these pointers to load into vector registers using movdqa. | |||
*/ | |||
uint8_t *pk = (uint8_t *) ((uintptr_t) pk_aligned|(uintptr_t) 1); | |||
uint8_t *sk = (uint8_t *) ((uintptr_t) sk_aligned|(uintptr_t) 1); | |||
uint8_t *sig = (uint8_t *) ((uintptr_t) sig_aligned|(uintptr_t) 1); | |||
uint8_t *m = (uint8_t *) ((uintptr_t) m_aligned|(uintptr_t) 1); | |||
size_t siglen; | |||
int returncode; | |||
int i; | |||
/* | |||
* Write 8 byte canary before and after the actual memory regions. | |||
* This is used to validate that the implementation does not assume | |||
* anything about the placement of data in memory | |||
* (e.g., assuming that the pk is always behind the sk) | |||
*/ | |||
write_canary(pk); | |||
write_canary(pk + CRYPTO_PUBLICKEYBYTES + 8); | |||
write_canary(sk); | |||
write_canary(sk + CRYPTO_SECRETKEYBYTES + 8); | |||
write_canary(sig); | |||
write_canary(sig + CRYPTO_BYTES + 8); | |||
write_canary(m); | |||
write_canary(m + MLEN + 8); | |||
for (i = 0; i < NTESTS; i++) { | |||
RETURNS_ZERO(crypto_sign_keypair(pk + 8, sk + 8)); | |||
randombytes(m + 8, MLEN); | |||
RETURNS_ZERO(crypto_sign_signature(sig + 8, &siglen, m + 8, MLEN, sk + 8)); | |||
if ((returncode = | |||
crypto_sign_verify(sig + 8, siglen, m + 8, MLEN, pk + 8)) != 0) { | |||
fprintf(stderr, "ERROR Signature did not verify correctly!\n"); | |||
if (returncode > 0) { | |||
fprintf(stderr, "ERROR return code should be < 0 on failure"); | |||
} | |||
return 1; | |||
} | |||
// Validate that the implementation did not touch the canary | |||
if (check_canary(pk) || check_canary(pk + CRYPTO_PUBLICKEYBYTES + 8) || | |||
check_canary(sk) || check_canary(sk + CRYPTO_SECRETKEYBYTES + 8) || | |||
check_canary(sig) || check_canary(sig + CRYPTO_BYTES + 8) || | |||
check_canary(m) || check_canary(m + MLEN + 8)) { | |||
fprintf(stderr, "ERROR canary overwritten\n"); | |||
return 1; | |||
} | |||
} | |||
return 0; | |||
} | |||
static int test_wrong_pk(void) { | |||
uint8_t pk[CRYPTO_PUBLICKEYBYTES]; | |||
uint8_t pk2[CRYPTO_PUBLICKEYBYTES]; | |||
@@ -175,6 +245,7 @@ int main(void) { | |||
puts(CRYPTO_ALGNAME); | |||
int result = 0; | |||
result += test_sign(); | |||
result += test_sign_detached(); | |||
result += test_wrong_pk(); | |||
return result; | |||
@@ -6,7 +6,6 @@ | |||
#include "api.h" | |||
#include "randombytes.h" | |||
#define NTESTS 100 | |||
#define MAXMLEN 2048 | |||
static void printbytes(const uint8_t *x, size_t xlen) { | |||
@@ -29,6 +28,8 @@ static void printbytes(const uint8_t *x, size_t xlen) { | |||
#define crypto_sign_keypair NAMESPACE(crypto_sign_keypair) | |||
#define crypto_sign NAMESPACE(crypto_sign) | |||
#define crypto_sign_open NAMESPACE(crypto_sign_open) | |||
#define crypto_sign_signature NAMESPACE(crypto_sign_signature) | |||
#define crypto_sign_verify NAMESPACE(crypto_sign_verify) | |||
int main(void) { | |||
uint8_t sk[CRYPTO_SECRETKEYBYTES]; | |||
@@ -36,14 +37,17 @@ int main(void) { | |||
uint8_t mi[MAXMLEN]; | |||
uint8_t sm[MAXMLEN + CRYPTO_BYTES]; | |||
uint8_t sig[CRYPTO_BYTES]; | |||
size_t smlen; | |||
size_t siglen; | |||
size_t mlen; | |||
int r; | |||
size_t i, k; | |||
for (i = 0; i < MAXMLEN; i = (i == 0) ? i + 1 : i << 1) { | |||
/* i = 0, 1, 4, 16, 64, 256, 1024 */ | |||
for (i = 0; i < MAXMLEN; i = (i == 0) ? i + 1 : i << 2) { | |||
randombytes(mi, i); | |||
crypto_sign_keypair(pk, sk); | |||
@@ -52,12 +56,15 @@ int main(void) { | |||
printbytes(sk, CRYPTO_SECRETKEYBYTES); | |||
crypto_sign(sm, &smlen, mi, i, sk); | |||
crypto_sign_signature(sig, &siglen, mi, i, sk); | |||
printbytes(sm, smlen); | |||
printbytes(sig, siglen); | |||
// By relying on m == sm we prevent having to allocate CRYPTO_BYTES | |||
// twice | |||
r = crypto_sign_open(sm, &mlen, sm, smlen, pk); | |||
r |= crypto_sign_verify(sig, siglen, mi, i, pk); | |||
if (r) { | |||
printf("ERROR: signature verification failed\n"); | |||
@@ -0,0 +1,9 @@ | |||
source: | |||
scheme: frodokem640shake | |||
implementation: clean | |||
files: | |||
- common.h | |||
- kem.c | |||
- matrix_shake.c | |||
- noise.c | |||
- util.c |
@@ -0,0 +1,9 @@ | |||
source: | |||
scheme: frodokem640shake | |||
implementation: clean | |||
files: | |||
- common.h | |||
- kem.c | |||
- matrix_shake.c | |||
- noise.c | |||
- util.c |
@@ -40,14 +40,15 @@ class Scheme: | |||
def all_schemes_of_type(type: str) -> list: | |||
schemes = [] | |||
p = os.path.join('..', 'crypto_' + type) | |||
for d in os.listdir(p): | |||
if os.path.isdir(os.path.join(p, d)): | |||
if type == 'kem': | |||
schemes.append(KEM(d)) | |||
elif type == 'sign': | |||
schemes.append(Signature(d)) | |||
else: | |||
assert('Unknown type') | |||
if os.path.isdir(p): | |||
for d in os.listdir(p): | |||
if os.path.isdir(os.path.join(p, d)): | |||
if type == 'kem': | |||
schemes.append(KEM(d)) | |||
elif type == 'sign': | |||
schemes.append(Signature(d)) | |||
else: | |||
assert('Unknown type') | |||
return schemes | |||
def metadata(self): | |||
@@ -69,10 +70,18 @@ class Implementation: | |||
def __init__(self, scheme, name): | |||
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) | |||
@@ -0,0 +1,32 @@ | |||
import os | |||
import re | |||
import pqclean | |||
def test_preprocessor(): | |||
for scheme in pqclean.Scheme.all_schemes(): | |||
for implementation in scheme.implementations: | |||
yield check_preprocessor, implementation | |||
def check_preprocessor(implementation: pqclean.Implementation): | |||
apipath = os.path.join(implementation.path(), 'api.h') | |||
errors = [] | |||
p = re.compile(r'^\s*#include\s*"') | |||
with open(apipath) as f: | |||
for i, line in enumerate(f): | |||
if p.match(line): | |||
errors.append("\n at {}:{}".format(apipath, i+1)) | |||
if errors: | |||
raise AssertionError( | |||
"Prohibited external include in api.h" + "".join(errors) | |||
) | |||
if __name__ == "__main__": | |||
try: | |||
import nose2 | |||
nose2.main() | |||
except ImportError: | |||
import nose | |||
nose.runmodule() |
@@ -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 | |||
@@ -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() |
@@ -29,6 +29,8 @@ def check_metadata(scheme): | |||
implementation_names_in_yaml = set( | |||
i['name'] for i in metadata['implementations']) | |||
if len(implementation_names_in_yaml) != len(metadata['implementations']): | |||
raise AssertionError("Implementations in YAML file are not distinct") | |||
implementations_on_disk = set(i.name for i in scheme.implementations) | |||
if implementation_names_in_yaml != implementations_on_disk: | |||
raise AssertionError("Implementations in YAML file {} and " | |||
@@ -42,7 +44,6 @@ EXPECTED_FIELDS = { | |||
'type': {'type': str}, | |||
'claimed-nist-level': {'type': int, 'min': 1, 'max': 5}, | |||
'length-public-key': {'type': int, 'min': 1}, | |||
'length-secret-key': {'type': int, 'min': 1}, | |||
'testvectors-sha256': {'type': str, 'length': 64}, | |||
'principal-submitter': {'type': str}, | |||
'auxiliary-submitters': {'type': list, 'elements': {'type': str}}, | |||
@@ -53,6 +54,7 @@ EXPECTED_FIELDS = { | |||
'spec': { | |||
'name': {'type': str}, | |||
'version': {'type': str}, | |||
'length-secret-key': {'type': int, 'min': 1}, | |||
}, | |||
}, | |||
}, | |||
@@ -70,14 +72,15 @@ 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 | |||
check_element(field, metadata[field], props) | |||
if field in metadata: | |||
check_element(field, metadata[field], props) | |||
# delete it to detect extras | |||
del metadata[field] | |||
# delete it to detect extras | |||
del metadata[field] | |||
# Done checking all specified fields, check if we have extras | |||
for field, value in metadata.items(): | |||
@@ -13,6 +13,8 @@ def test_metadata_sizes(): | |||
def check_metadata_sizes(implementation): | |||
metadata = implementation.scheme.metadata() | |||
impl_meta = next((impl for impl in metadata['implementations'] | |||
if impl['name'] == implementation.name), None) | |||
helpers.make('printparams', | |||
TYPE=implementation.scheme.type, | |||
SCHEME=implementation.scheme.name, | |||
@@ -30,7 +32,7 @@ def check_metadata_sizes(implementation): | |||
parsed = json.loads(out) | |||
assert parsed['CRYPTO_SECRETKEYBYTES'] == metadata['length-secret-key'] | |||
assert parsed['CRYPTO_SECRETKEYBYTES'] == impl_meta['length-secret-key'] | |||
assert parsed['CRYPTO_PUBLICKEYBYTES'] == metadata['length-public-key'] | |||
if implementation.scheme.type == 'kem': | |||
@@ -0,0 +1,37 @@ | |||
import os | |||
from glob import glob | |||
import pqclean | |||
from helpers import run_subprocess, ensure_available | |||
def test_preprocessor(): | |||
for scheme in pqclean.Scheme.all_schemes(): | |||
for implementation in scheme.implementations: | |||
yield check_preprocessor, implementation | |||
def check_preprocessor(implementation: pqclean.Implementation): | |||
cfiles = implementation.cfiles() | |||
hfiles = implementation.hfiles() | |||
errors = [] | |||
for file in hfiles + cfiles: | |||
with open(file) as f: | |||
for i, line in enumerate(f): | |||
line = line.strip() | |||
if file in hfiles and i == 0 and line.startswith('#ifndef'): | |||
continue | |||
if line.startswith('#if'): | |||
errors.append("\n at {}:{}".format(file, i+1)) | |||
if errors: | |||
raise AssertionError( | |||
"Prohibited use of preprocessor conditional" + "".join(errors) | |||
) | |||
if __name__ == "__main__": | |||
try: | |||
import nose2 | |||
nose2.main() | |||
except ImportError: | |||
import nose | |||
nose.runmodule() |