@@ -3,11 +3,9 @@ | |||||
<!-- Type some lines about your submission --> | <!-- 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. --> | <!-- 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 | * [ ] No stringification macros | ||||
* [ ] Output-parameter pointers in functions are on the left | * [ ] Output-parameter pointers in functions are on the left | ||||
* [ ] Negative return values on failure of API functions (within restrictions of FO transform). | * [ ] 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] Code is valid C99 | ||||
* [x] Passes functional tests | * [x] Passes functional tests | ||||
* [x] API functions do not write outside provided buffers | * [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] 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 across runs | ||||
* [x] Consistent test vectors on big-endian and little-endian machines | * [x] Consistent test vectors on big-endian and little-endian machines | ||||
* [x] Consistent test vectors on 32-bit and 64-bit 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 | ## Requirements on C implementations that are manually checked | ||||
* Minimalist Makefiles | * Minimalist Makefiles | ||||
* `#ifdef`s only for header encapsulation | |||||
* No stringification macros | * No stringification macros | ||||
* Output-parameter pointers in functions are on the left | * Output-parameter pointers in functions are on the left | ||||
* `const` arguments are labeled as `const` | * `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 | # 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) | 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 | type: kem | ||||
claimed-nist-level: 1 | claimed-nist-level: 1 | ||||
length-public-key: 9616 | length-public-key: 9616 | ||||
length-secret-key: 19888 | |||||
length-ciphertext: 9720 | length-ciphertext: 9720 | ||||
length-shared-secret: 16 | length-shared-secret: 16 | ||||
testvectors-sha256: 521ff891de20efe74e6584d09612dae989427ac76261a41630c4e4d6a4fc78a4 | |||||
testvectors-sha256: 8f922de02d41005fcc3c4164b2ab74c4c7b588ed69e34e22607d1ae4ab13d2c5 | |||||
principal-submitter: Douglas Stebila, University of Waterloo | principal-submitter: Douglas Stebila, University of Waterloo | ||||
auxiliary-submitters: | 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: | 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 | type: kem | ||||
claimed-nist-level: 3 | claimed-nist-level: 3 | ||||
length-public-key: 1088 | length-public-key: 1088 | ||||
length-secret-key: 2400 | |||||
length-ciphertext: 1152 | length-ciphertext: 1152 | ||||
length-shared-secret: 32 | length-shared-secret: 32 | ||||
testvectors-sha256: 0e002ee528febdab1709f100df79ceb00b31a809e03a4fb84e3a72c39235d372 | |||||
testvectors-sha256: 2f5cf9937959eb4a3bc910f71e830e9e0de029b28093c6192d2c3e915913016f | |||||
principal-submitter: Peter Schwabe | principal-submitter: Peter Schwabe | ||||
auxiliary-submitters: | auxiliary-submitters: | ||||
- Roberto Avanzi | - Roberto Avanzi | ||||
@@ -20,3 +19,4 @@ auxiliary-submitters: | |||||
implementations: | implementations: | ||||
- name: clean | - name: clean | ||||
version: https://github.com/pq-crystals/kyber/commit/ab996e7460e5356b0e23aa034e7c2fe6922e60e6 | version: https://github.com/pq-crystals/kyber/commit/ab996e7460e5356b0e23aa034e7c2fe6922e60e6 | ||||
length-secret-key: 2400 |
@@ -3,12 +3,10 @@ | |||||
#include <stdint.h> | #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" | #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 | * - const unsigned char *buf: pointer to input byte array | ||||
**************************************************/ | **************************************************/ | ||||
void PQCLEAN_KYBER768_CLEAN_cbd(poly *r, const unsigned char *buf) { | 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]; | uint32_t t, d, a[4], b[4]; | ||||
int i, j; | 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 + 2] = (uint16_t)(a[2] + KYBER_Q - b[2]); | ||||
r->coeffs[4 * i + 3] = (uint16_t)(a[3] + KYBER_Q - b[3]); | 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: | # This Makefile can be used with Microsoft Visual Studio's nmake using the command: | ||||
# nmake /f Makefile.Microsoft_nmake | # 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) | 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 | DEST_DIR=../bin | ||||
# This -Wall was supported by the European Commission through the ERC Starting Grant 805031 (EPOQUE) | # 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) \ | all: $(DEST_DIR)/functest_$(SCHEME)_$(IMPLEMENTATION) \ | ||||
$(DEST_DIR)/testvectors_$(SCHEME)_$(IMPLEMENTATION) \ | $(DEST_DIR)/testvectors_$(SCHEME)_$(IMPLEMENTATION) \ | ||||
@@ -41,13 +41,13 @@ $(DEST_DIR)\functest_$(SCHEME)_$(IMPLEMENTATION).exe: build-scheme $(COMMON_OBJE | |||||
-MKDIR $(DEST_DIR) | -MKDIR $(DEST_DIR) | ||||
-DEL functest.obj | -DEL functest.obj | ||||
$(CC) /c crypto_$(TYPE)\functest.c $(CFLAGS) /I $(SCHEME_DIR) /DPQCLEAN_NAMESPACE=PQCLEAN_$(SCHEME_UPPERCASE)_$(IMPLEMENTATION_UPPERCASE) | $(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 | $(DEST_DIR)\testvectors_$(SCHEME)_$(IMPLEMENTATION).exe: build-scheme $(COMMON_OBJECTS) $(COMMON_DIR)\notrandombytes.obj | ||||
-MKDIR $(DEST_DIR) | -MKDIR $(DEST_DIR) | ||||
-DEL testvectors.obj | -DEL testvectors.obj | ||||
$(CC) /c crypto_$(TYPE)\testvectors.c $(CFLAGS) /I $(SCHEME_DIR) /DPQCLEAN_NAMESPACE=PQCLEAN_$(SCHEME_UPPERCASE)_$(IMPLEMENTATION_UPPERCASE) | $(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 | $(DEST_DIR)\printparams_$(SCHEME)_$(IMPLEMENTATION).exe: crypto_$(TYPE)\printparams.c $(SCHEME_DIR)\api.h | ||||
-MKDIR $(DEST_DIR) | -MKDIR $(DEST_DIR) | ||||
@@ -5,7 +5,7 @@ | |||||
#include "api.h" | #include "api.h" | ||||
#include "randombytes.h" | #include "randombytes.h" | ||||
#define NTESTS 10 | |||||
#define NTESTS 5 | |||||
const uint8_t canary[8] = { | const uint8_t canary[8] = { | ||||
0x01, 0x23, 0x45, 0x67, 0x89, 0xAB, 0xCD, 0xEF | 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)) { | if (!memcmp(key_a, key_b, CRYPTO_BYTES)) { | ||||
printf("ERROR invalid sk_a\n"); | 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)) { | if (!memcmp(key_a, key_b, CRYPTO_BYTES)) { | ||||
printf("ERROR invalid ciphertext\n"); | printf("ERROR invalid ciphertext\n"); | ||||
return 1; | |||||
return -1; | |||||
} | } | ||||
} | } | ||||
@@ -6,7 +6,7 @@ | |||||
#include "api.h" | #include "api.h" | ||||
#include "randombytes.h" | #include "randombytes.h" | ||||
#define NTESTS 100 | |||||
#define NTESTS 5 | |||||
static void printbytes(const uint8_t *x, size_t xlen) { | static void printbytes(const uint8_t *x, size_t xlen) { | ||||
size_t i; | size_t i; | ||||
@@ -6,7 +6,7 @@ | |||||
#include "api.h" | #include "api.h" | ||||
#include "randombytes.h" | #include "randombytes.h" | ||||
#define NTESTS 15 | |||||
#define NTESTS 5 | |||||
#define MLEN 32 | #define MLEN 32 | ||||
const uint8_t canary[8] = { | 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_keypair NAMESPACE(crypto_sign_keypair) | ||||
#define crypto_sign NAMESPACE(crypto_sign) | #define crypto_sign NAMESPACE(crypto_sign) | ||||
#define crypto_sign_open NAMESPACE(crypto_sign_open) | #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) \ | #define RETURNS_ZERO(f) \ | ||||
if ((f) != 0) { \ | if ((f) != 0) { \ | ||||
@@ -133,6 +135,74 @@ static int test_sign(void) { | |||||
return 0; | 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) { | static int test_wrong_pk(void) { | ||||
uint8_t pk[CRYPTO_PUBLICKEYBYTES]; | uint8_t pk[CRYPTO_PUBLICKEYBYTES]; | ||||
uint8_t pk2[CRYPTO_PUBLICKEYBYTES]; | uint8_t pk2[CRYPTO_PUBLICKEYBYTES]; | ||||
@@ -175,6 +245,7 @@ int main(void) { | |||||
puts(CRYPTO_ALGNAME); | puts(CRYPTO_ALGNAME); | ||||
int result = 0; | int result = 0; | ||||
result += test_sign(); | result += test_sign(); | ||||
result += test_sign_detached(); | |||||
result += test_wrong_pk(); | result += test_wrong_pk(); | ||||
return result; | return result; | ||||
@@ -6,7 +6,6 @@ | |||||
#include "api.h" | #include "api.h" | ||||
#include "randombytes.h" | #include "randombytes.h" | ||||
#define NTESTS 100 | |||||
#define MAXMLEN 2048 | #define MAXMLEN 2048 | ||||
static void printbytes(const uint8_t *x, size_t xlen) { | 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_keypair NAMESPACE(crypto_sign_keypair) | ||||
#define crypto_sign NAMESPACE(crypto_sign) | #define crypto_sign NAMESPACE(crypto_sign) | ||||
#define crypto_sign_open NAMESPACE(crypto_sign_open) | #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) { | int main(void) { | ||||
uint8_t sk[CRYPTO_SECRETKEYBYTES]; | uint8_t sk[CRYPTO_SECRETKEYBYTES]; | ||||
@@ -36,14 +37,17 @@ int main(void) { | |||||
uint8_t mi[MAXMLEN]; | uint8_t mi[MAXMLEN]; | ||||
uint8_t sm[MAXMLEN + CRYPTO_BYTES]; | uint8_t sm[MAXMLEN + CRYPTO_BYTES]; | ||||
uint8_t sig[CRYPTO_BYTES]; | |||||
size_t smlen; | size_t smlen; | ||||
size_t siglen; | |||||
size_t mlen; | size_t mlen; | ||||
int r; | int r; | ||||
size_t i, k; | 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); | randombytes(mi, i); | ||||
crypto_sign_keypair(pk, sk); | crypto_sign_keypair(pk, sk); | ||||
@@ -52,12 +56,15 @@ int main(void) { | |||||
printbytes(sk, CRYPTO_SECRETKEYBYTES); | printbytes(sk, CRYPTO_SECRETKEYBYTES); | ||||
crypto_sign(sm, &smlen, mi, i, sk); | crypto_sign(sm, &smlen, mi, i, sk); | ||||
crypto_sign_signature(sig, &siglen, mi, i, sk); | |||||
printbytes(sm, smlen); | printbytes(sm, smlen); | ||||
printbytes(sig, siglen); | |||||
// By relying on m == sm we prevent having to allocate CRYPTO_BYTES | // By relying on m == sm we prevent having to allocate CRYPTO_BYTES | ||||
// twice | // twice | ||||
r = crypto_sign_open(sm, &mlen, sm, smlen, pk); | r = crypto_sign_open(sm, &mlen, sm, smlen, pk); | ||||
r |= crypto_sign_verify(sig, siglen, mi, i, pk); | |||||
if (r) { | if (r) { | ||||
printf("ERROR: signature verification failed\n"); | 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: | def all_schemes_of_type(type: str) -> list: | ||||
schemes = [] | schemes = [] | ||||
p = os.path.join('..', 'crypto_' + type) | 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 | return schemes | ||||
def metadata(self): | def metadata(self): | ||||
@@ -69,10 +70,18 @@ class Implementation: | |||||
def __init__(self, scheme, name): | def __init__(self, scheme, name): | ||||
self.scheme = scheme | self.scheme = scheme | ||||
self.name = name | 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: | def path(self, base='..') -> str: | ||||
return os.path.join(self.scheme.path(), self.name) | 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: | def libname(self) -> str: | ||||
if os.name == 'nt': | if os.name == 'nt': | ||||
return "lib{}_{}.lib".format(self.scheme.name, self.name) | 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(): | 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 scheme in pqclean.Scheme.all_schemes(): | ||||
for implementation in scheme.implementations: | for implementation in scheme.implementations: | ||||
yield check_char, implementation | 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( | implementation_names_in_yaml = set( | ||||
i['name'] for i in metadata['implementations']) | 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) | implementations_on_disk = set(i.name for i in scheme.implementations) | ||||
if implementation_names_in_yaml != implementations_on_disk: | if implementation_names_in_yaml != implementations_on_disk: | ||||
raise AssertionError("Implementations in YAML file {} and " | raise AssertionError("Implementations in YAML file {} and " | ||||
@@ -42,7 +44,6 @@ EXPECTED_FIELDS = { | |||||
'type': {'type': str}, | 'type': {'type': str}, | ||||
'claimed-nist-level': {'type': int, 'min': 1, 'max': 5}, | 'claimed-nist-level': {'type': int, 'min': 1, 'max': 5}, | ||||
'length-public-key': {'type': int, 'min': 1}, | 'length-public-key': {'type': int, 'min': 1}, | ||||
'length-secret-key': {'type': int, 'min': 1}, | |||||
'testvectors-sha256': {'type': str, 'length': 64}, | 'testvectors-sha256': {'type': str, 'length': 64}, | ||||
'principal-submitter': {'type': str}, | 'principal-submitter': {'type': str}, | ||||
'auxiliary-submitters': {'type': list, 'elements': {'type': str}}, | 'auxiliary-submitters': {'type': list, 'elements': {'type': str}}, | ||||
@@ -53,6 +54,7 @@ EXPECTED_FIELDS = { | |||||
'spec': { | 'spec': { | ||||
'name': {'type': str}, | 'name': {'type': str}, | ||||
'version': {'type': str}, | 'version': {'type': str}, | ||||
'length-secret-key': {'type': int, 'min': 1}, | |||||
}, | }, | ||||
}, | }, | ||||
}, | }, | ||||
@@ -70,14 +72,15 @@ SIGNATURE_FIELDS = { | |||||
def check_spec(metadata, spec): | def check_spec(metadata, spec): | ||||
for field, props in 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)) | raise AssertionError("Field '{}' not present.".format(field)) | ||||
# validate element | # 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 | # Done checking all specified fields, check if we have extras | ||||
for field, value in metadata.items(): | for field, value in metadata.items(): | ||||
@@ -13,6 +13,8 @@ def test_metadata_sizes(): | |||||
def check_metadata_sizes(implementation): | def check_metadata_sizes(implementation): | ||||
metadata = implementation.scheme.metadata() | metadata = implementation.scheme.metadata() | ||||
impl_meta = next((impl for impl in metadata['implementations'] | |||||
if impl['name'] == implementation.name), None) | |||||
helpers.make('printparams', | helpers.make('printparams', | ||||
TYPE=implementation.scheme.type, | TYPE=implementation.scheme.type, | ||||
SCHEME=implementation.scheme.name, | SCHEME=implementation.scheme.name, | ||||
@@ -30,7 +32,7 @@ def check_metadata_sizes(implementation): | |||||
parsed = json.loads(out) | 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'] | assert parsed['CRYPTO_PUBLICKEYBYTES'] == metadata['length-public-key'] | ||||
if implementation.scheme.type == 'kem': | 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() |