Add post-quantum wallet support and PQ transaction signing
This commit is contained in:
parent
45d9b111fb
commit
ba44de3cfd
67 changed files with 10102 additions and 269 deletions
2
.gitignore
vendored
2
.gitignore
vendored
|
|
@ -8,3 +8,5 @@ local.properties
|
||||||
*.keystore
|
*.keystore
|
||||||
keystore.properties
|
keystore.properties
|
||||||
app/build.gradle.kts.backup-*
|
app/build.gradle.kts.backup-*
|
||||||
|
|
||||||
|
app/.cxx/
|
||||||
|
|
|
||||||
|
|
@ -17,6 +17,7 @@ if (keystorePropertiesFile.exists()) {
|
||||||
android {
|
android {
|
||||||
namespace = "org.vextaproject.wallet"
|
namespace = "org.vextaproject.wallet"
|
||||||
compileSdk = 36
|
compileSdk = 36
|
||||||
|
ndkVersion = "27.3.13750724"
|
||||||
|
|
||||||
defaultConfig {
|
defaultConfig {
|
||||||
applicationId = "org.vextaproject.wallet"
|
applicationId = "org.vextaproject.wallet"
|
||||||
|
|
@ -42,7 +43,19 @@ android {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
externalNativeBuild {
|
||||||
|
cmake {
|
||||||
|
version = "3.22.1"
|
||||||
|
path = file("src/main/cpp/CMakeLists.txt")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
buildTypes {
|
buildTypes {
|
||||||
|
getByName("debug") {
|
||||||
|
applicationIdSuffix = ".debug"
|
||||||
|
versionNameSuffix = "-debug"
|
||||||
|
}
|
||||||
|
|
||||||
getByName("release") {
|
getByName("release") {
|
||||||
if (keystorePropertiesFile.exists()) {
|
if (keystorePropertiesFile.exists()) {
|
||||||
signingConfig = signingConfigs.getByName("release")
|
signingConfig = signingConfigs.getByName("release")
|
||||||
|
|
|
||||||
BIN
app/src/main/assets/pq-core-mldsa.sig
Normal file
BIN
app/src/main/assets/pq-core-mldsa.sig
Normal file
Binary file not shown.
BIN
app/src/main/assets/pq-core-sphincs.sig
Normal file
BIN
app/src/main/assets/pq-core-sphincs.sig
Normal file
Binary file not shown.
57
app/src/main/cpp/CMakeLists.txt
Normal file
57
app/src/main/cpp/CMakeLists.txt
Normal file
|
|
@ -0,0 +1,57 @@
|
||||||
|
cmake_minimum_required(VERSION 3.22.1)
|
||||||
|
|
||||||
|
project(vextapq LANGUAGES C CXX)
|
||||||
|
|
||||||
|
add_library(
|
||||||
|
vextapq
|
||||||
|
SHARED
|
||||||
|
|
||||||
|
vexta_pq_jni.cpp
|
||||||
|
vexta_pq_randombytes.cpp
|
||||||
|
pq/pq.cpp
|
||||||
|
|
||||||
|
pq/mldsa65/fips202.c
|
||||||
|
pq/mldsa65/ntt.c
|
||||||
|
pq/mldsa65/packing.c
|
||||||
|
pq/mldsa65/poly.c
|
||||||
|
pq/mldsa65/polyvec.c
|
||||||
|
pq/mldsa65/reduce.c
|
||||||
|
pq/mldsa65/rounding.c
|
||||||
|
pq/mldsa65/sign.c
|
||||||
|
pq/mldsa65/symmetric-shake.c
|
||||||
|
|
||||||
|
pq/sphincsplus/address.c
|
||||||
|
pq/sphincsplus/fips202.c
|
||||||
|
pq/sphincsplus/fors.c
|
||||||
|
pq/sphincsplus/hash_sha2.c
|
||||||
|
pq/sphincsplus/merkle.c
|
||||||
|
pq/sphincsplus/sha2.c
|
||||||
|
pq/sphincsplus/sign.c
|
||||||
|
pq/sphincsplus/thash_sha2_simple.c
|
||||||
|
pq/sphincsplus/utils.c
|
||||||
|
pq/sphincsplus/utilsx1.c
|
||||||
|
pq/sphincsplus/wots.c
|
||||||
|
pq/sphincsplus/wotsx1.c
|
||||||
|
)
|
||||||
|
|
||||||
|
target_include_directories(
|
||||||
|
vextapq
|
||||||
|
PRIVATE
|
||||||
|
${CMAKE_CURRENT_SOURCE_DIR}/pq
|
||||||
|
${CMAKE_CURRENT_SOURCE_DIR}/pq/mldsa65
|
||||||
|
${CMAKE_CURRENT_SOURCE_DIR}/pq/sphincsplus
|
||||||
|
)
|
||||||
|
|
||||||
|
target_compile_definitions(
|
||||||
|
vextapq
|
||||||
|
PRIVATE
|
||||||
|
DILITHIUM_MODE=3
|
||||||
|
PARAMS=sphincs-sha2-128s
|
||||||
|
)
|
||||||
|
|
||||||
|
find_library(log-lib log)
|
||||||
|
|
||||||
|
target_link_libraries(
|
||||||
|
vextapq
|
||||||
|
${log-lib}
|
||||||
|
)
|
||||||
7
app/src/main/cpp/pq/mldsa65/LICENSE
Normal file
7
app/src/main/cpp/pq/mldsa65/LICENSE
Normal file
|
|
@ -0,0 +1,7 @@
|
||||||
|
Public Domain (https://creativecommons.org/share-your-work/public-domain/cc0/);
|
||||||
|
or Apache 2.0 License (https://www.apache.org/licenses/LICENSE-2.0.html) or GPL 2.0 License (http://www.gnu.org/licenses/gpl-2.0.html).
|
||||||
|
|
||||||
|
For Keccak and the random number generator
|
||||||
|
we are using public-domain code from sources
|
||||||
|
and by authors listed in comments on top of
|
||||||
|
the respective files.
|
||||||
98
app/src/main/cpp/pq/mldsa65/api.h
Normal file
98
app/src/main/cpp/pq/mldsa65/api.h
Normal file
|
|
@ -0,0 +1,98 @@
|
||||||
|
#ifndef API_H
|
||||||
|
#define API_H
|
||||||
|
|
||||||
|
#include <stddef.h>
|
||||||
|
#include <stdint.h>
|
||||||
|
|
||||||
|
#define pqcrystals_dilithium2_PUBLICKEYBYTES 1312
|
||||||
|
#define pqcrystals_dilithium2_SECRETKEYBYTES 2560
|
||||||
|
#define pqcrystals_dilithium2_BYTES 2420
|
||||||
|
|
||||||
|
#define pqcrystals_dilithium2_ref_PUBLICKEYBYTES pqcrystals_dilithium2_PUBLICKEYBYTES
|
||||||
|
#define pqcrystals_dilithium2_ref_SECRETKEYBYTES pqcrystals_dilithium2_SECRETKEYBYTES
|
||||||
|
#define pqcrystals_dilithium2_ref_BYTES pqcrystals_dilithium2_BYTES
|
||||||
|
|
||||||
|
int pqcrystals_dilithium2_ref_keypair(uint8_t *pk, uint8_t *sk);
|
||||||
|
|
||||||
|
int pqcrystals_dilithium2_ref_signature(uint8_t *sig, size_t *siglen,
|
||||||
|
const uint8_t *m, size_t mlen,
|
||||||
|
const uint8_t *ctx, size_t ctxlen,
|
||||||
|
const uint8_t *sk);
|
||||||
|
|
||||||
|
int pqcrystals_dilithium2_ref(uint8_t *sm, size_t *smlen,
|
||||||
|
const uint8_t *m, size_t mlen,
|
||||||
|
const uint8_t *ctx, size_t ctxlen,
|
||||||
|
const uint8_t *sk);
|
||||||
|
|
||||||
|
int pqcrystals_dilithium2_ref_verify(const uint8_t *sig, size_t siglen,
|
||||||
|
const uint8_t *m, size_t mlen,
|
||||||
|
const uint8_t *ctx, size_t ctxlen,
|
||||||
|
const uint8_t *pk);
|
||||||
|
|
||||||
|
int pqcrystals_dilithium2_ref_open(uint8_t *m, size_t *mlen,
|
||||||
|
const uint8_t *sm, size_t smlen,
|
||||||
|
const uint8_t *ctx, size_t ctxlen,
|
||||||
|
const uint8_t *pk);
|
||||||
|
|
||||||
|
#define pqcrystals_dilithium3_PUBLICKEYBYTES 1952
|
||||||
|
#define pqcrystals_dilithium3_SECRETKEYBYTES 4032
|
||||||
|
#define pqcrystals_dilithium3_BYTES 3309
|
||||||
|
|
||||||
|
#define pqcrystals_dilithium3_ref_PUBLICKEYBYTES pqcrystals_dilithium3_PUBLICKEYBYTES
|
||||||
|
#define pqcrystals_dilithium3_ref_SECRETKEYBYTES pqcrystals_dilithium3_SECRETKEYBYTES
|
||||||
|
#define pqcrystals_dilithium3_ref_BYTES pqcrystals_dilithium3_BYTES
|
||||||
|
|
||||||
|
int pqcrystals_dilithium3_ref_keypair(uint8_t *pk, uint8_t *sk);
|
||||||
|
|
||||||
|
int pqcrystals_dilithium3_ref_signature(uint8_t *sig, size_t *siglen,
|
||||||
|
const uint8_t *m, size_t mlen,
|
||||||
|
const uint8_t *ctx, size_t ctxlen,
|
||||||
|
const uint8_t *sk);
|
||||||
|
|
||||||
|
int pqcrystals_dilithium3_ref(uint8_t *sm, size_t *smlen,
|
||||||
|
const uint8_t *m, size_t mlen,
|
||||||
|
const uint8_t *ctx, size_t ctxlen,
|
||||||
|
const uint8_t *sk);
|
||||||
|
|
||||||
|
int pqcrystals_dilithium3_ref_verify(const uint8_t *sig, size_t siglen,
|
||||||
|
const uint8_t *m, size_t mlen,
|
||||||
|
const uint8_t *ctx, size_t ctxlen,
|
||||||
|
const uint8_t *pk);
|
||||||
|
|
||||||
|
int pqcrystals_dilithium3_ref_open(uint8_t *m, size_t *mlen,
|
||||||
|
const uint8_t *sm, size_t smlen,
|
||||||
|
const uint8_t *ctx, size_t ctxlen,
|
||||||
|
const uint8_t *pk);
|
||||||
|
|
||||||
|
#define pqcrystals_dilithium5_PUBLICKEYBYTES 2592
|
||||||
|
#define pqcrystals_dilithium5_SECRETKEYBYTES 4896
|
||||||
|
#define pqcrystals_dilithium5_BYTES 4627
|
||||||
|
|
||||||
|
#define pqcrystals_dilithium5_ref_PUBLICKEYBYTES pqcrystals_dilithium5_PUBLICKEYBYTES
|
||||||
|
#define pqcrystals_dilithium5_ref_SECRETKEYBYTES pqcrystals_dilithium5_SECRETKEYBYTES
|
||||||
|
#define pqcrystals_dilithium5_ref_BYTES pqcrystals_dilithium5_BYTES
|
||||||
|
|
||||||
|
int pqcrystals_dilithium5_ref_keypair(uint8_t *pk, uint8_t *sk);
|
||||||
|
|
||||||
|
int pqcrystals_dilithium5_ref_signature(uint8_t *sig, size_t *siglen,
|
||||||
|
const uint8_t *m, size_t mlen,
|
||||||
|
const uint8_t *ctx, size_t ctxlen,
|
||||||
|
const uint8_t *sk);
|
||||||
|
|
||||||
|
int pqcrystals_dilithium5_ref(uint8_t *sm, size_t *smlen,
|
||||||
|
const uint8_t *m, size_t mlen,
|
||||||
|
const uint8_t *ctx, size_t ctxlen,
|
||||||
|
const uint8_t *sk);
|
||||||
|
|
||||||
|
int pqcrystals_dilithium5_ref_verify(const uint8_t *sig, size_t siglen,
|
||||||
|
const uint8_t *m, size_t mlen,
|
||||||
|
const uint8_t *ctx, size_t ctxlen,
|
||||||
|
const uint8_t *pk);
|
||||||
|
|
||||||
|
int pqcrystals_dilithium5_ref_open(uint8_t *m, size_t *mlen,
|
||||||
|
const uint8_t *sm, size_t smlen,
|
||||||
|
const uint8_t *ctx, size_t ctxlen,
|
||||||
|
const uint8_t *pk);
|
||||||
|
|
||||||
|
|
||||||
|
#endif
|
||||||
27
app/src/main/cpp/pq/mldsa65/config.h
Normal file
27
app/src/main/cpp/pq/mldsa65/config.h
Normal file
|
|
@ -0,0 +1,27 @@
|
||||||
|
#ifndef CONFIG_H
|
||||||
|
#define CONFIG_H
|
||||||
|
|
||||||
|
//#define DILITHIUM_MODE 2
|
||||||
|
#define DILITHIUM_RANDOMIZED_SIGNING
|
||||||
|
//#define USE_RDPMC
|
||||||
|
//#define DBENCH
|
||||||
|
|
||||||
|
#ifndef DILITHIUM_MODE
|
||||||
|
#define DILITHIUM_MODE 2
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#if DILITHIUM_MODE == 2
|
||||||
|
#define CRYPTO_ALGNAME "Dilithium2"
|
||||||
|
#define DILITHIUM_NAMESPACETOP pqcrystals_dilithium2_ref
|
||||||
|
#define DILITHIUM_NAMESPACE(s) pqcrystals_dilithium2_ref_##s
|
||||||
|
#elif DILITHIUM_MODE == 3
|
||||||
|
#define CRYPTO_ALGNAME "Dilithium3"
|
||||||
|
#define DILITHIUM_NAMESPACETOP pqcrystals_dilithium3_ref
|
||||||
|
#define DILITHIUM_NAMESPACE(s) pqcrystals_dilithium3_ref_##s
|
||||||
|
#elif DILITHIUM_MODE == 5
|
||||||
|
#define CRYPTO_ALGNAME "Dilithium5"
|
||||||
|
#define DILITHIUM_NAMESPACETOP pqcrystals_dilithium5_ref
|
||||||
|
#define DILITHIUM_NAMESPACE(s) pqcrystals_dilithium5_ref_##s
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#endif
|
||||||
774
app/src/main/cpp/pq/mldsa65/fips202.c
Normal file
774
app/src/main/cpp/pq/mldsa65/fips202.c
Normal file
|
|
@ -0,0 +1,774 @@
|
||||||
|
/* Based on the public domain implementation in crypto_hash/keccakc512/simple/ from
|
||||||
|
* http://bench.cr.yp.to/supercop.html by Ronny Van Keer and the public domain "TweetFips202"
|
||||||
|
* implementation from https://twitter.com/tweetfips202 by Gilles Van Assche, Daniel J. Bernstein,
|
||||||
|
* and Peter Schwabe */
|
||||||
|
|
||||||
|
#include <stddef.h>
|
||||||
|
#include <stdint.h>
|
||||||
|
#include "fips202.h"
|
||||||
|
|
||||||
|
#define NROUNDS 24
|
||||||
|
#define ROL(a, offset) ((a << offset) ^ (a >> (64-offset)))
|
||||||
|
|
||||||
|
/*************************************************
|
||||||
|
* Name: load64
|
||||||
|
*
|
||||||
|
* Description: Load 8 bytes into uint64_t in little-endian order
|
||||||
|
*
|
||||||
|
* Arguments: - const uint8_t *x: pointer to input byte array
|
||||||
|
*
|
||||||
|
* Returns the loaded 64-bit unsigned integer
|
||||||
|
**************************************************/
|
||||||
|
static uint64_t load64(const uint8_t x[8]) {
|
||||||
|
unsigned int i;
|
||||||
|
uint64_t r = 0;
|
||||||
|
|
||||||
|
for(i=0;i<8;i++)
|
||||||
|
r |= (uint64_t)x[i] << 8*i;
|
||||||
|
|
||||||
|
return r;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*************************************************
|
||||||
|
* Name: store64
|
||||||
|
*
|
||||||
|
* Description: Store a 64-bit integer to array of 8 bytes in little-endian order
|
||||||
|
*
|
||||||
|
* Arguments: - uint8_t *x: pointer to the output byte array (allocated)
|
||||||
|
* - uint64_t u: input 64-bit unsigned integer
|
||||||
|
**************************************************/
|
||||||
|
static void store64(uint8_t x[8], uint64_t u) {
|
||||||
|
unsigned int i;
|
||||||
|
|
||||||
|
for(i=0;i<8;i++)
|
||||||
|
x[i] = u >> 8*i;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Keccak round constants */
|
||||||
|
const uint64_t KeccakF_RoundConstants[NROUNDS] = {
|
||||||
|
(uint64_t)0x0000000000000001ULL,
|
||||||
|
(uint64_t)0x0000000000008082ULL,
|
||||||
|
(uint64_t)0x800000000000808aULL,
|
||||||
|
(uint64_t)0x8000000080008000ULL,
|
||||||
|
(uint64_t)0x000000000000808bULL,
|
||||||
|
(uint64_t)0x0000000080000001ULL,
|
||||||
|
(uint64_t)0x8000000080008081ULL,
|
||||||
|
(uint64_t)0x8000000000008009ULL,
|
||||||
|
(uint64_t)0x000000000000008aULL,
|
||||||
|
(uint64_t)0x0000000000000088ULL,
|
||||||
|
(uint64_t)0x0000000080008009ULL,
|
||||||
|
(uint64_t)0x000000008000000aULL,
|
||||||
|
(uint64_t)0x000000008000808bULL,
|
||||||
|
(uint64_t)0x800000000000008bULL,
|
||||||
|
(uint64_t)0x8000000000008089ULL,
|
||||||
|
(uint64_t)0x8000000000008003ULL,
|
||||||
|
(uint64_t)0x8000000000008002ULL,
|
||||||
|
(uint64_t)0x8000000000000080ULL,
|
||||||
|
(uint64_t)0x000000000000800aULL,
|
||||||
|
(uint64_t)0x800000008000000aULL,
|
||||||
|
(uint64_t)0x8000000080008081ULL,
|
||||||
|
(uint64_t)0x8000000000008080ULL,
|
||||||
|
(uint64_t)0x0000000080000001ULL,
|
||||||
|
(uint64_t)0x8000000080008008ULL
|
||||||
|
};
|
||||||
|
|
||||||
|
/*************************************************
|
||||||
|
* Name: KeccakF1600_StatePermute
|
||||||
|
*
|
||||||
|
* Description: The Keccak F1600 Permutation
|
||||||
|
*
|
||||||
|
* Arguments: - uint64_t *state: pointer to input/output Keccak state
|
||||||
|
**************************************************/
|
||||||
|
static void KeccakF1600_StatePermute(uint64_t state[25])
|
||||||
|
{
|
||||||
|
int round;
|
||||||
|
|
||||||
|
uint64_t Aba, Abe, Abi, Abo, Abu;
|
||||||
|
uint64_t Aga, Age, Agi, Ago, Agu;
|
||||||
|
uint64_t Aka, Ake, Aki, Ako, Aku;
|
||||||
|
uint64_t Ama, Ame, Ami, Amo, Amu;
|
||||||
|
uint64_t Asa, Ase, Asi, Aso, Asu;
|
||||||
|
uint64_t BCa, BCe, BCi, BCo, BCu;
|
||||||
|
uint64_t Da, De, Di, Do, Du;
|
||||||
|
uint64_t Eba, Ebe, Ebi, Ebo, Ebu;
|
||||||
|
uint64_t Ega, Ege, Egi, Ego, Egu;
|
||||||
|
uint64_t Eka, Eke, Eki, Eko, Eku;
|
||||||
|
uint64_t Ema, Eme, Emi, Emo, Emu;
|
||||||
|
uint64_t Esa, Ese, Esi, Eso, Esu;
|
||||||
|
|
||||||
|
//copyFromState(A, state)
|
||||||
|
Aba = state[ 0];
|
||||||
|
Abe = state[ 1];
|
||||||
|
Abi = state[ 2];
|
||||||
|
Abo = state[ 3];
|
||||||
|
Abu = state[ 4];
|
||||||
|
Aga = state[ 5];
|
||||||
|
Age = state[ 6];
|
||||||
|
Agi = state[ 7];
|
||||||
|
Ago = state[ 8];
|
||||||
|
Agu = state[ 9];
|
||||||
|
Aka = state[10];
|
||||||
|
Ake = state[11];
|
||||||
|
Aki = state[12];
|
||||||
|
Ako = state[13];
|
||||||
|
Aku = state[14];
|
||||||
|
Ama = state[15];
|
||||||
|
Ame = state[16];
|
||||||
|
Ami = state[17];
|
||||||
|
Amo = state[18];
|
||||||
|
Amu = state[19];
|
||||||
|
Asa = state[20];
|
||||||
|
Ase = state[21];
|
||||||
|
Asi = state[22];
|
||||||
|
Aso = state[23];
|
||||||
|
Asu = state[24];
|
||||||
|
|
||||||
|
for(round = 0; round < NROUNDS; round += 2) {
|
||||||
|
// prepareTheta
|
||||||
|
BCa = Aba^Aga^Aka^Ama^Asa;
|
||||||
|
BCe = Abe^Age^Ake^Ame^Ase;
|
||||||
|
BCi = Abi^Agi^Aki^Ami^Asi;
|
||||||
|
BCo = Abo^Ago^Ako^Amo^Aso;
|
||||||
|
BCu = Abu^Agu^Aku^Amu^Asu;
|
||||||
|
|
||||||
|
//thetaRhoPiChiIotaPrepareTheta(round, A, E)
|
||||||
|
Da = BCu^ROL(BCe, 1);
|
||||||
|
De = BCa^ROL(BCi, 1);
|
||||||
|
Di = BCe^ROL(BCo, 1);
|
||||||
|
Do = BCi^ROL(BCu, 1);
|
||||||
|
Du = BCo^ROL(BCa, 1);
|
||||||
|
|
||||||
|
Aba ^= Da;
|
||||||
|
BCa = Aba;
|
||||||
|
Age ^= De;
|
||||||
|
BCe = ROL(Age, 44);
|
||||||
|
Aki ^= Di;
|
||||||
|
BCi = ROL(Aki, 43);
|
||||||
|
Amo ^= Do;
|
||||||
|
BCo = ROL(Amo, 21);
|
||||||
|
Asu ^= Du;
|
||||||
|
BCu = ROL(Asu, 14);
|
||||||
|
Eba = BCa ^((~BCe)& BCi );
|
||||||
|
Eba ^= (uint64_t)KeccakF_RoundConstants[round];
|
||||||
|
Ebe = BCe ^((~BCi)& BCo );
|
||||||
|
Ebi = BCi ^((~BCo)& BCu );
|
||||||
|
Ebo = BCo ^((~BCu)& BCa );
|
||||||
|
Ebu = BCu ^((~BCa)& BCe );
|
||||||
|
|
||||||
|
Abo ^= Do;
|
||||||
|
BCa = ROL(Abo, 28);
|
||||||
|
Agu ^= Du;
|
||||||
|
BCe = ROL(Agu, 20);
|
||||||
|
Aka ^= Da;
|
||||||
|
BCi = ROL(Aka, 3);
|
||||||
|
Ame ^= De;
|
||||||
|
BCo = ROL(Ame, 45);
|
||||||
|
Asi ^= Di;
|
||||||
|
BCu = ROL(Asi, 61);
|
||||||
|
Ega = BCa ^((~BCe)& BCi );
|
||||||
|
Ege = BCe ^((~BCi)& BCo );
|
||||||
|
Egi = BCi ^((~BCo)& BCu );
|
||||||
|
Ego = BCo ^((~BCu)& BCa );
|
||||||
|
Egu = BCu ^((~BCa)& BCe );
|
||||||
|
|
||||||
|
Abe ^= De;
|
||||||
|
BCa = ROL(Abe, 1);
|
||||||
|
Agi ^= Di;
|
||||||
|
BCe = ROL(Agi, 6);
|
||||||
|
Ako ^= Do;
|
||||||
|
BCi = ROL(Ako, 25);
|
||||||
|
Amu ^= Du;
|
||||||
|
BCo = ROL(Amu, 8);
|
||||||
|
Asa ^= Da;
|
||||||
|
BCu = ROL(Asa, 18);
|
||||||
|
Eka = BCa ^((~BCe)& BCi );
|
||||||
|
Eke = BCe ^((~BCi)& BCo );
|
||||||
|
Eki = BCi ^((~BCo)& BCu );
|
||||||
|
Eko = BCo ^((~BCu)& BCa );
|
||||||
|
Eku = BCu ^((~BCa)& BCe );
|
||||||
|
|
||||||
|
Abu ^= Du;
|
||||||
|
BCa = ROL(Abu, 27);
|
||||||
|
Aga ^= Da;
|
||||||
|
BCe = ROL(Aga, 36);
|
||||||
|
Ake ^= De;
|
||||||
|
BCi = ROL(Ake, 10);
|
||||||
|
Ami ^= Di;
|
||||||
|
BCo = ROL(Ami, 15);
|
||||||
|
Aso ^= Do;
|
||||||
|
BCu = ROL(Aso, 56);
|
||||||
|
Ema = BCa ^((~BCe)& BCi );
|
||||||
|
Eme = BCe ^((~BCi)& BCo );
|
||||||
|
Emi = BCi ^((~BCo)& BCu );
|
||||||
|
Emo = BCo ^((~BCu)& BCa );
|
||||||
|
Emu = BCu ^((~BCa)& BCe );
|
||||||
|
|
||||||
|
Abi ^= Di;
|
||||||
|
BCa = ROL(Abi, 62);
|
||||||
|
Ago ^= Do;
|
||||||
|
BCe = ROL(Ago, 55);
|
||||||
|
Aku ^= Du;
|
||||||
|
BCi = ROL(Aku, 39);
|
||||||
|
Ama ^= Da;
|
||||||
|
BCo = ROL(Ama, 41);
|
||||||
|
Ase ^= De;
|
||||||
|
BCu = ROL(Ase, 2);
|
||||||
|
Esa = BCa ^((~BCe)& BCi );
|
||||||
|
Ese = BCe ^((~BCi)& BCo );
|
||||||
|
Esi = BCi ^((~BCo)& BCu );
|
||||||
|
Eso = BCo ^((~BCu)& BCa );
|
||||||
|
Esu = BCu ^((~BCa)& BCe );
|
||||||
|
|
||||||
|
// prepareTheta
|
||||||
|
BCa = Eba^Ega^Eka^Ema^Esa;
|
||||||
|
BCe = Ebe^Ege^Eke^Eme^Ese;
|
||||||
|
BCi = Ebi^Egi^Eki^Emi^Esi;
|
||||||
|
BCo = Ebo^Ego^Eko^Emo^Eso;
|
||||||
|
BCu = Ebu^Egu^Eku^Emu^Esu;
|
||||||
|
|
||||||
|
//thetaRhoPiChiIotaPrepareTheta(round+1, E, A)
|
||||||
|
Da = BCu^ROL(BCe, 1);
|
||||||
|
De = BCa^ROL(BCi, 1);
|
||||||
|
Di = BCe^ROL(BCo, 1);
|
||||||
|
Do = BCi^ROL(BCu, 1);
|
||||||
|
Du = BCo^ROL(BCa, 1);
|
||||||
|
|
||||||
|
Eba ^= Da;
|
||||||
|
BCa = Eba;
|
||||||
|
Ege ^= De;
|
||||||
|
BCe = ROL(Ege, 44);
|
||||||
|
Eki ^= Di;
|
||||||
|
BCi = ROL(Eki, 43);
|
||||||
|
Emo ^= Do;
|
||||||
|
BCo = ROL(Emo, 21);
|
||||||
|
Esu ^= Du;
|
||||||
|
BCu = ROL(Esu, 14);
|
||||||
|
Aba = BCa ^((~BCe)& BCi );
|
||||||
|
Aba ^= (uint64_t)KeccakF_RoundConstants[round+1];
|
||||||
|
Abe = BCe ^((~BCi)& BCo );
|
||||||
|
Abi = BCi ^((~BCo)& BCu );
|
||||||
|
Abo = BCo ^((~BCu)& BCa );
|
||||||
|
Abu = BCu ^((~BCa)& BCe );
|
||||||
|
|
||||||
|
Ebo ^= Do;
|
||||||
|
BCa = ROL(Ebo, 28);
|
||||||
|
Egu ^= Du;
|
||||||
|
BCe = ROL(Egu, 20);
|
||||||
|
Eka ^= Da;
|
||||||
|
BCi = ROL(Eka, 3);
|
||||||
|
Eme ^= De;
|
||||||
|
BCo = ROL(Eme, 45);
|
||||||
|
Esi ^= Di;
|
||||||
|
BCu = ROL(Esi, 61);
|
||||||
|
Aga = BCa ^((~BCe)& BCi );
|
||||||
|
Age = BCe ^((~BCi)& BCo );
|
||||||
|
Agi = BCi ^((~BCo)& BCu );
|
||||||
|
Ago = BCo ^((~BCu)& BCa );
|
||||||
|
Agu = BCu ^((~BCa)& BCe );
|
||||||
|
|
||||||
|
Ebe ^= De;
|
||||||
|
BCa = ROL(Ebe, 1);
|
||||||
|
Egi ^= Di;
|
||||||
|
BCe = ROL(Egi, 6);
|
||||||
|
Eko ^= Do;
|
||||||
|
BCi = ROL(Eko, 25);
|
||||||
|
Emu ^= Du;
|
||||||
|
BCo = ROL(Emu, 8);
|
||||||
|
Esa ^= Da;
|
||||||
|
BCu = ROL(Esa, 18);
|
||||||
|
Aka = BCa ^((~BCe)& BCi );
|
||||||
|
Ake = BCe ^((~BCi)& BCo );
|
||||||
|
Aki = BCi ^((~BCo)& BCu );
|
||||||
|
Ako = BCo ^((~BCu)& BCa );
|
||||||
|
Aku = BCu ^((~BCa)& BCe );
|
||||||
|
|
||||||
|
Ebu ^= Du;
|
||||||
|
BCa = ROL(Ebu, 27);
|
||||||
|
Ega ^= Da;
|
||||||
|
BCe = ROL(Ega, 36);
|
||||||
|
Eke ^= De;
|
||||||
|
BCi = ROL(Eke, 10);
|
||||||
|
Emi ^= Di;
|
||||||
|
BCo = ROL(Emi, 15);
|
||||||
|
Eso ^= Do;
|
||||||
|
BCu = ROL(Eso, 56);
|
||||||
|
Ama = BCa ^((~BCe)& BCi );
|
||||||
|
Ame = BCe ^((~BCi)& BCo );
|
||||||
|
Ami = BCi ^((~BCo)& BCu );
|
||||||
|
Amo = BCo ^((~BCu)& BCa );
|
||||||
|
Amu = BCu ^((~BCa)& BCe );
|
||||||
|
|
||||||
|
Ebi ^= Di;
|
||||||
|
BCa = ROL(Ebi, 62);
|
||||||
|
Ego ^= Do;
|
||||||
|
BCe = ROL(Ego, 55);
|
||||||
|
Eku ^= Du;
|
||||||
|
BCi = ROL(Eku, 39);
|
||||||
|
Ema ^= Da;
|
||||||
|
BCo = ROL(Ema, 41);
|
||||||
|
Ese ^= De;
|
||||||
|
BCu = ROL(Ese, 2);
|
||||||
|
Asa = BCa ^((~BCe)& BCi );
|
||||||
|
Ase = BCe ^((~BCi)& BCo );
|
||||||
|
Asi = BCi ^((~BCo)& BCu );
|
||||||
|
Aso = BCo ^((~BCu)& BCa );
|
||||||
|
Asu = BCu ^((~BCa)& BCe );
|
||||||
|
}
|
||||||
|
|
||||||
|
//copyToState(state, A)
|
||||||
|
state[ 0] = Aba;
|
||||||
|
state[ 1] = Abe;
|
||||||
|
state[ 2] = Abi;
|
||||||
|
state[ 3] = Abo;
|
||||||
|
state[ 4] = Abu;
|
||||||
|
state[ 5] = Aga;
|
||||||
|
state[ 6] = Age;
|
||||||
|
state[ 7] = Agi;
|
||||||
|
state[ 8] = Ago;
|
||||||
|
state[ 9] = Agu;
|
||||||
|
state[10] = Aka;
|
||||||
|
state[11] = Ake;
|
||||||
|
state[12] = Aki;
|
||||||
|
state[13] = Ako;
|
||||||
|
state[14] = Aku;
|
||||||
|
state[15] = Ama;
|
||||||
|
state[16] = Ame;
|
||||||
|
state[17] = Ami;
|
||||||
|
state[18] = Amo;
|
||||||
|
state[19] = Amu;
|
||||||
|
state[20] = Asa;
|
||||||
|
state[21] = Ase;
|
||||||
|
state[22] = Asi;
|
||||||
|
state[23] = Aso;
|
||||||
|
state[24] = Asu;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*************************************************
|
||||||
|
* Name: keccak_init
|
||||||
|
*
|
||||||
|
* Description: Initializes the Keccak state.
|
||||||
|
*
|
||||||
|
* Arguments: - uint64_t *s: pointer to Keccak state
|
||||||
|
**************************************************/
|
||||||
|
static void keccak_init(uint64_t s[25])
|
||||||
|
{
|
||||||
|
unsigned int i;
|
||||||
|
for(i=0;i<25;i++)
|
||||||
|
s[i] = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*************************************************
|
||||||
|
* Name: keccak_absorb
|
||||||
|
*
|
||||||
|
* Description: Absorb step of Keccak; incremental.
|
||||||
|
*
|
||||||
|
* Arguments: - uint64_t *s: pointer to Keccak state
|
||||||
|
* - unsigned int pos: position in current block to be absorbed
|
||||||
|
* - unsigned int r: rate in bytes (e.g., 168 for SHAKE128)
|
||||||
|
* - const uint8_t *in: pointer to input to be absorbed into s
|
||||||
|
* - size_t inlen: length of input in bytes
|
||||||
|
*
|
||||||
|
* Returns new position pos in current block
|
||||||
|
**************************************************/
|
||||||
|
static unsigned int keccak_absorb(uint64_t s[25],
|
||||||
|
unsigned int pos,
|
||||||
|
unsigned int r,
|
||||||
|
const uint8_t *in,
|
||||||
|
size_t inlen)
|
||||||
|
{
|
||||||
|
unsigned int i;
|
||||||
|
|
||||||
|
while(pos+inlen >= r) {
|
||||||
|
for(i=pos;i<r;i++)
|
||||||
|
s[i/8] ^= (uint64_t)*in++ << 8*(i%8);
|
||||||
|
inlen -= r-pos;
|
||||||
|
KeccakF1600_StatePermute(s);
|
||||||
|
pos = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
for(i=pos;i<pos+inlen;i++)
|
||||||
|
s[i/8] ^= (uint64_t)*in++ << 8*(i%8);
|
||||||
|
|
||||||
|
return i;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*************************************************
|
||||||
|
* Name: keccak_finalize
|
||||||
|
*
|
||||||
|
* Description: Finalize absorb step.
|
||||||
|
*
|
||||||
|
* Arguments: - uint64_t *s: pointer to Keccak state
|
||||||
|
* - unsigned int pos: position in current block to be absorbed
|
||||||
|
* - unsigned int r: rate in bytes (e.g., 168 for SHAKE128)
|
||||||
|
* - uint8_t p: domain separation byte
|
||||||
|
**************************************************/
|
||||||
|
static void keccak_finalize(uint64_t s[25], unsigned int pos, unsigned int r, uint8_t p)
|
||||||
|
{
|
||||||
|
s[pos/8] ^= (uint64_t)p << 8*(pos%8);
|
||||||
|
s[r/8-1] ^= 1ULL << 63;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*************************************************
|
||||||
|
* Name: keccak_squeeze
|
||||||
|
*
|
||||||
|
* Description: Squeeze step of Keccak. Squeezes arbitratrily many bytes.
|
||||||
|
* Modifies the state. Can be called multiple times to keep
|
||||||
|
* squeezing, i.e., is incremental.
|
||||||
|
*
|
||||||
|
* Arguments: - uint8_t *out: pointer to output
|
||||||
|
* - size_t outlen: number of bytes to be squeezed (written to out)
|
||||||
|
* - uint64_t *s: pointer to input/output Keccak state
|
||||||
|
* - unsigned int pos: number of bytes in current block already squeezed
|
||||||
|
* - unsigned int r: rate in bytes (e.g., 168 for SHAKE128)
|
||||||
|
*
|
||||||
|
* Returns new position pos in current block
|
||||||
|
**************************************************/
|
||||||
|
static unsigned int keccak_squeeze(uint8_t *out,
|
||||||
|
size_t outlen,
|
||||||
|
uint64_t s[25],
|
||||||
|
unsigned int pos,
|
||||||
|
unsigned int r)
|
||||||
|
{
|
||||||
|
unsigned int i;
|
||||||
|
|
||||||
|
while(outlen) {
|
||||||
|
if(pos == r) {
|
||||||
|
KeccakF1600_StatePermute(s);
|
||||||
|
pos = 0;
|
||||||
|
}
|
||||||
|
for(i=pos;i < r && i < pos+outlen; i++)
|
||||||
|
*out++ = s[i/8] >> 8*(i%8);
|
||||||
|
outlen -= i-pos;
|
||||||
|
pos = i;
|
||||||
|
}
|
||||||
|
|
||||||
|
return pos;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/*************************************************
|
||||||
|
* Name: keccak_absorb_once
|
||||||
|
*
|
||||||
|
* Description: Absorb step of Keccak;
|
||||||
|
* non-incremental, starts by zeroeing the state.
|
||||||
|
*
|
||||||
|
* Arguments: - uint64_t *s: pointer to (uninitialized) output Keccak state
|
||||||
|
* - unsigned int r: rate in bytes (e.g., 168 for SHAKE128)
|
||||||
|
* - const uint8_t *in: pointer to input to be absorbed into s
|
||||||
|
* - size_t inlen: length of input in bytes
|
||||||
|
* - uint8_t p: domain-separation byte for different Keccak-derived functions
|
||||||
|
**************************************************/
|
||||||
|
static void keccak_absorb_once(uint64_t s[25],
|
||||||
|
unsigned int r,
|
||||||
|
const uint8_t *in,
|
||||||
|
size_t inlen,
|
||||||
|
uint8_t p)
|
||||||
|
{
|
||||||
|
unsigned int i;
|
||||||
|
|
||||||
|
for(i=0;i<25;i++)
|
||||||
|
s[i] = 0;
|
||||||
|
|
||||||
|
while(inlen >= r) {
|
||||||
|
for(i=0;i<r/8;i++)
|
||||||
|
s[i] ^= load64(in+8*i);
|
||||||
|
in += r;
|
||||||
|
inlen -= r;
|
||||||
|
KeccakF1600_StatePermute(s);
|
||||||
|
}
|
||||||
|
|
||||||
|
for(i=0;i<inlen;i++)
|
||||||
|
s[i/8] ^= (uint64_t)in[i] << 8*(i%8);
|
||||||
|
|
||||||
|
s[i/8] ^= (uint64_t)p << 8*(i%8);
|
||||||
|
s[(r-1)/8] ^= 1ULL << 63;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*************************************************
|
||||||
|
* Name: keccak_squeezeblocks
|
||||||
|
*
|
||||||
|
* Description: Squeeze step of Keccak. Squeezes full blocks of r bytes each.
|
||||||
|
* Modifies the state. Can be called multiple times to keep
|
||||||
|
* squeezing, i.e., is incremental. Assumes zero bytes of current
|
||||||
|
* block have already been squeezed.
|
||||||
|
*
|
||||||
|
* Arguments: - uint8_t *out: pointer to output blocks
|
||||||
|
* - size_t nblocks: number of blocks to be squeezed (written to out)
|
||||||
|
* - uint64_t *s: pointer to input/output Keccak state
|
||||||
|
* - unsigned int r: rate in bytes (e.g., 168 for SHAKE128)
|
||||||
|
**************************************************/
|
||||||
|
static void keccak_squeezeblocks(uint8_t *out,
|
||||||
|
size_t nblocks,
|
||||||
|
uint64_t s[25],
|
||||||
|
unsigned int r)
|
||||||
|
{
|
||||||
|
unsigned int i;
|
||||||
|
|
||||||
|
while(nblocks) {
|
||||||
|
KeccakF1600_StatePermute(s);
|
||||||
|
for(i=0;i<r/8;i++)
|
||||||
|
store64(out+8*i, s[i]);
|
||||||
|
out += r;
|
||||||
|
nblocks -= 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/*************************************************
|
||||||
|
* Name: shake128_init
|
||||||
|
*
|
||||||
|
* Description: Initilizes Keccak state for use as SHAKE128 XOF
|
||||||
|
*
|
||||||
|
* Arguments: - keccak_state *state: pointer to (uninitialized) Keccak state
|
||||||
|
**************************************************/
|
||||||
|
void shake128_init(keccak_state *state)
|
||||||
|
{
|
||||||
|
keccak_init(state->s);
|
||||||
|
state->pos = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*************************************************
|
||||||
|
* Name: shake128_absorb
|
||||||
|
*
|
||||||
|
* Description: Absorb step of the SHAKE128 XOF; incremental.
|
||||||
|
*
|
||||||
|
* Arguments: - keccak_state *state: pointer to (initialized) output Keccak state
|
||||||
|
* - const uint8_t *in: pointer to input to be absorbed into s
|
||||||
|
* - size_t inlen: length of input in bytes
|
||||||
|
**************************************************/
|
||||||
|
void shake128_absorb(keccak_state *state, const uint8_t *in, size_t inlen)
|
||||||
|
{
|
||||||
|
state->pos = keccak_absorb(state->s, state->pos, SHAKE128_RATE, in, inlen);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*************************************************
|
||||||
|
* Name: shake128_finalize
|
||||||
|
*
|
||||||
|
* Description: Finalize absorb step of the SHAKE128 XOF.
|
||||||
|
*
|
||||||
|
* Arguments: - keccak_state *state: pointer to Keccak state
|
||||||
|
**************************************************/
|
||||||
|
void shake128_finalize(keccak_state *state)
|
||||||
|
{
|
||||||
|
keccak_finalize(state->s, state->pos, SHAKE128_RATE, 0x1F);
|
||||||
|
state->pos = SHAKE128_RATE;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*************************************************
|
||||||
|
* Name: shake128_squeeze
|
||||||
|
*
|
||||||
|
* Description: Squeeze step of SHAKE128 XOF. Squeezes arbitraily many
|
||||||
|
* bytes. Can be called multiple times to keep squeezing.
|
||||||
|
*
|
||||||
|
* Arguments: - uint8_t *out: pointer to output blocks
|
||||||
|
* - size_t outlen : number of bytes to be squeezed (written to output)
|
||||||
|
* - keccak_state *s: pointer to input/output Keccak state
|
||||||
|
**************************************************/
|
||||||
|
void shake128_squeeze(uint8_t *out, size_t outlen, keccak_state *state)
|
||||||
|
{
|
||||||
|
state->pos = keccak_squeeze(out, outlen, state->s, state->pos, SHAKE128_RATE);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*************************************************
|
||||||
|
* Name: shake128_absorb_once
|
||||||
|
*
|
||||||
|
* Description: Initialize, absorb into and finalize SHAKE128 XOF; non-incremental.
|
||||||
|
*
|
||||||
|
* Arguments: - keccak_state *state: pointer to (uninitialized) output Keccak state
|
||||||
|
* - const uint8_t *in: pointer to input to be absorbed into s
|
||||||
|
* - size_t inlen: length of input in bytes
|
||||||
|
**************************************************/
|
||||||
|
void shake128_absorb_once(keccak_state *state, const uint8_t *in, size_t inlen)
|
||||||
|
{
|
||||||
|
keccak_absorb_once(state->s, SHAKE128_RATE, in, inlen, 0x1F);
|
||||||
|
state->pos = SHAKE128_RATE;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*************************************************
|
||||||
|
* Name: shake128_squeezeblocks
|
||||||
|
*
|
||||||
|
* Description: Squeeze step of SHAKE128 XOF. Squeezes full blocks of
|
||||||
|
* SHAKE128_RATE bytes each. Can be called multiple times
|
||||||
|
* to keep squeezing. Assumes new block has not yet been
|
||||||
|
* started (state->pos = SHAKE128_RATE).
|
||||||
|
*
|
||||||
|
* Arguments: - uint8_t *out: pointer to output blocks
|
||||||
|
* - size_t nblocks: number of blocks to be squeezed (written to output)
|
||||||
|
* - keccak_state *s: pointer to input/output Keccak state
|
||||||
|
**************************************************/
|
||||||
|
void shake128_squeezeblocks(uint8_t *out, size_t nblocks, keccak_state *state)
|
||||||
|
{
|
||||||
|
keccak_squeezeblocks(out, nblocks, state->s, SHAKE128_RATE);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*************************************************
|
||||||
|
* Name: shake256_init
|
||||||
|
*
|
||||||
|
* Description: Initilizes Keccak state for use as SHAKE256 XOF
|
||||||
|
*
|
||||||
|
* Arguments: - keccak_state *state: pointer to (uninitialized) Keccak state
|
||||||
|
**************************************************/
|
||||||
|
void shake256_init(keccak_state *state)
|
||||||
|
{
|
||||||
|
keccak_init(state->s);
|
||||||
|
state->pos = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*************************************************
|
||||||
|
* Name: shake256_absorb
|
||||||
|
*
|
||||||
|
* Description: Absorb step of the SHAKE256 XOF; incremental.
|
||||||
|
*
|
||||||
|
* Arguments: - keccak_state *state: pointer to (initialized) output Keccak state
|
||||||
|
* - const uint8_t *in: pointer to input to be absorbed into s
|
||||||
|
* - size_t inlen: length of input in bytes
|
||||||
|
**************************************************/
|
||||||
|
void shake256_absorb(keccak_state *state, const uint8_t *in, size_t inlen)
|
||||||
|
{
|
||||||
|
state->pos = keccak_absorb(state->s, state->pos, SHAKE256_RATE, in, inlen);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*************************************************
|
||||||
|
* Name: shake256_finalize
|
||||||
|
*
|
||||||
|
* Description: Finalize absorb step of the SHAKE256 XOF.
|
||||||
|
*
|
||||||
|
* Arguments: - keccak_state *state: pointer to Keccak state
|
||||||
|
**************************************************/
|
||||||
|
void shake256_finalize(keccak_state *state)
|
||||||
|
{
|
||||||
|
keccak_finalize(state->s, state->pos, SHAKE256_RATE, 0x1F);
|
||||||
|
state->pos = SHAKE256_RATE;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*************************************************
|
||||||
|
* Name: shake256_squeeze
|
||||||
|
*
|
||||||
|
* Description: Squeeze step of SHAKE256 XOF. Squeezes arbitraily many
|
||||||
|
* bytes. Can be called multiple times to keep squeezing.
|
||||||
|
*
|
||||||
|
* Arguments: - uint8_t *out: pointer to output blocks
|
||||||
|
* - size_t outlen : number of bytes to be squeezed (written to output)
|
||||||
|
* - keccak_state *s: pointer to input/output Keccak state
|
||||||
|
**************************************************/
|
||||||
|
void shake256_squeeze(uint8_t *out, size_t outlen, keccak_state *state)
|
||||||
|
{
|
||||||
|
state->pos = keccak_squeeze(out, outlen, state->s, state->pos, SHAKE256_RATE);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*************************************************
|
||||||
|
* Name: shake256_absorb_once
|
||||||
|
*
|
||||||
|
* Description: Initialize, absorb into and finalize SHAKE256 XOF; non-incremental.
|
||||||
|
*
|
||||||
|
* Arguments: - keccak_state *state: pointer to (uninitialized) output Keccak state
|
||||||
|
* - const uint8_t *in: pointer to input to be absorbed into s
|
||||||
|
* - size_t inlen: length of input in bytes
|
||||||
|
**************************************************/
|
||||||
|
void shake256_absorb_once(keccak_state *state, const uint8_t *in, size_t inlen)
|
||||||
|
{
|
||||||
|
keccak_absorb_once(state->s, SHAKE256_RATE, in, inlen, 0x1F);
|
||||||
|
state->pos = SHAKE256_RATE;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*************************************************
|
||||||
|
* Name: shake256_squeezeblocks
|
||||||
|
*
|
||||||
|
* Description: Squeeze step of SHAKE256 XOF. Squeezes full blocks of
|
||||||
|
* SHAKE256_RATE bytes each. Can be called multiple times
|
||||||
|
* to keep squeezing. Assumes next block has not yet been
|
||||||
|
* started (state->pos = SHAKE256_RATE).
|
||||||
|
*
|
||||||
|
* Arguments: - uint8_t *out: pointer to output blocks
|
||||||
|
* - size_t nblocks: number of blocks to be squeezed (written to output)
|
||||||
|
* - keccak_state *s: pointer to input/output Keccak state
|
||||||
|
**************************************************/
|
||||||
|
void shake256_squeezeblocks(uint8_t *out, size_t nblocks, keccak_state *state)
|
||||||
|
{
|
||||||
|
keccak_squeezeblocks(out, nblocks, state->s, SHAKE256_RATE);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*************************************************
|
||||||
|
* Name: shake128
|
||||||
|
*
|
||||||
|
* Description: SHAKE128 XOF with non-incremental API
|
||||||
|
*
|
||||||
|
* Arguments: - uint8_t *out: pointer to output
|
||||||
|
* - size_t outlen: requested output length in bytes
|
||||||
|
* - const uint8_t *in: pointer to input
|
||||||
|
* - size_t inlen: length of input in bytes
|
||||||
|
**************************************************/
|
||||||
|
void shake128(uint8_t *out, size_t outlen, const uint8_t *in, size_t inlen)
|
||||||
|
{
|
||||||
|
size_t nblocks;
|
||||||
|
keccak_state state;
|
||||||
|
|
||||||
|
shake128_absorb_once(&state, in, inlen);
|
||||||
|
nblocks = outlen/SHAKE128_RATE;
|
||||||
|
shake128_squeezeblocks(out, nblocks, &state);
|
||||||
|
outlen -= nblocks*SHAKE128_RATE;
|
||||||
|
out += nblocks*SHAKE128_RATE;
|
||||||
|
shake128_squeeze(out, outlen, &state);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*************************************************
|
||||||
|
* Name: shake256
|
||||||
|
*
|
||||||
|
* Description: SHAKE256 XOF with non-incremental API
|
||||||
|
*
|
||||||
|
* Arguments: - uint8_t *out: pointer to output
|
||||||
|
* - size_t outlen: requested output length in bytes
|
||||||
|
* - const uint8_t *in: pointer to input
|
||||||
|
* - size_t inlen: length of input in bytes
|
||||||
|
**************************************************/
|
||||||
|
void shake256(uint8_t *out, size_t outlen, const uint8_t *in, size_t inlen)
|
||||||
|
{
|
||||||
|
size_t nblocks;
|
||||||
|
keccak_state state;
|
||||||
|
|
||||||
|
shake256_absorb_once(&state, in, inlen);
|
||||||
|
nblocks = outlen/SHAKE256_RATE;
|
||||||
|
shake256_squeezeblocks(out, nblocks, &state);
|
||||||
|
outlen -= nblocks*SHAKE256_RATE;
|
||||||
|
out += nblocks*SHAKE256_RATE;
|
||||||
|
shake256_squeeze(out, outlen, &state);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*************************************************
|
||||||
|
* Name: sha3_256
|
||||||
|
*
|
||||||
|
* Description: SHA3-256 with non-incremental API
|
||||||
|
*
|
||||||
|
* Arguments: - uint8_t *h: pointer to output (32 bytes)
|
||||||
|
* - const uint8_t *in: pointer to input
|
||||||
|
* - size_t inlen: length of input in bytes
|
||||||
|
**************************************************/
|
||||||
|
void sha3_256(uint8_t h[32], const uint8_t *in, size_t inlen)
|
||||||
|
{
|
||||||
|
unsigned int i;
|
||||||
|
uint64_t s[25];
|
||||||
|
|
||||||
|
keccak_absorb_once(s, SHA3_256_RATE, in, inlen, 0x06);
|
||||||
|
KeccakF1600_StatePermute(s);
|
||||||
|
for(i=0;i<4;i++)
|
||||||
|
store64(h+8*i,s[i]);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*************************************************
|
||||||
|
* Name: sha3_512
|
||||||
|
*
|
||||||
|
* Description: SHA3-512 with non-incremental API
|
||||||
|
*
|
||||||
|
* Arguments: - uint8_t *h: pointer to output (64 bytes)
|
||||||
|
* - const uint8_t *in: pointer to input
|
||||||
|
* - size_t inlen: length of input in bytes
|
||||||
|
**************************************************/
|
||||||
|
void sha3_512(uint8_t h[64], const uint8_t *in, size_t inlen)
|
||||||
|
{
|
||||||
|
unsigned int i;
|
||||||
|
uint64_t s[25];
|
||||||
|
|
||||||
|
keccak_absorb_once(s, SHA3_512_RATE, in, inlen, 0x06);
|
||||||
|
KeccakF1600_StatePermute(s);
|
||||||
|
for(i=0;i<8;i++)
|
||||||
|
store64(h+8*i,s[i]);
|
||||||
|
}
|
||||||
57
app/src/main/cpp/pq/mldsa65/fips202.h
Normal file
57
app/src/main/cpp/pq/mldsa65/fips202.h
Normal file
|
|
@ -0,0 +1,57 @@
|
||||||
|
#ifndef FIPS202_H
|
||||||
|
#define FIPS202_H
|
||||||
|
|
||||||
|
#include <stddef.h>
|
||||||
|
#include <stdint.h>
|
||||||
|
|
||||||
|
#define SHAKE128_RATE 168
|
||||||
|
#define SHAKE256_RATE 136
|
||||||
|
#define SHA3_256_RATE 136
|
||||||
|
#define SHA3_512_RATE 72
|
||||||
|
|
||||||
|
#define FIPS202_NAMESPACE(s) pqcrystals_dilithium_fips202_ref_##s
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
uint64_t s[25];
|
||||||
|
unsigned int pos;
|
||||||
|
} keccak_state;
|
||||||
|
|
||||||
|
#define KeccakF_RoundConstants FIPS202_NAMESPACE(KeccakF_RoundConstants)
|
||||||
|
extern const uint64_t KeccakF_RoundConstants[];
|
||||||
|
|
||||||
|
#define shake128_init FIPS202_NAMESPACE(shake128_init)
|
||||||
|
void shake128_init(keccak_state *state);
|
||||||
|
#define shake128_absorb FIPS202_NAMESPACE(shake128_absorb)
|
||||||
|
void shake128_absorb(keccak_state *state, const uint8_t *in, size_t inlen);
|
||||||
|
#define shake128_finalize FIPS202_NAMESPACE(shake128_finalize)
|
||||||
|
void shake128_finalize(keccak_state *state);
|
||||||
|
#define shake128_squeeze FIPS202_NAMESPACE(shake128_squeeze)
|
||||||
|
void shake128_squeeze(uint8_t *out, size_t outlen, keccak_state *state);
|
||||||
|
#define shake128_absorb_once FIPS202_NAMESPACE(shake128_absorb_once)
|
||||||
|
void shake128_absorb_once(keccak_state *state, const uint8_t *in, size_t inlen);
|
||||||
|
#define shake128_squeezeblocks FIPS202_NAMESPACE(shake128_squeezeblocks)
|
||||||
|
void shake128_squeezeblocks(uint8_t *out, size_t nblocks, keccak_state *state);
|
||||||
|
|
||||||
|
#define shake256_init FIPS202_NAMESPACE(shake256_init)
|
||||||
|
void shake256_init(keccak_state *state);
|
||||||
|
#define shake256_absorb FIPS202_NAMESPACE(shake256_absorb)
|
||||||
|
void shake256_absorb(keccak_state *state, const uint8_t *in, size_t inlen);
|
||||||
|
#define shake256_finalize FIPS202_NAMESPACE(shake256_finalize)
|
||||||
|
void shake256_finalize(keccak_state *state);
|
||||||
|
#define shake256_squeeze FIPS202_NAMESPACE(shake256_squeeze)
|
||||||
|
void shake256_squeeze(uint8_t *out, size_t outlen, keccak_state *state);
|
||||||
|
#define shake256_absorb_once FIPS202_NAMESPACE(shake256_absorb_once)
|
||||||
|
void shake256_absorb_once(keccak_state *state, const uint8_t *in, size_t inlen);
|
||||||
|
#define shake256_squeezeblocks FIPS202_NAMESPACE(shake256_squeezeblocks)
|
||||||
|
void shake256_squeezeblocks(uint8_t *out, size_t nblocks, keccak_state *state);
|
||||||
|
|
||||||
|
#define shake128 FIPS202_NAMESPACE(shake128)
|
||||||
|
void shake128(uint8_t *out, size_t outlen, const uint8_t *in, size_t inlen);
|
||||||
|
#define shake256 FIPS202_NAMESPACE(shake256)
|
||||||
|
void shake256(uint8_t *out, size_t outlen, const uint8_t *in, size_t inlen);
|
||||||
|
#define sha3_256 FIPS202_NAMESPACE(sha3_256)
|
||||||
|
void sha3_256(uint8_t h[32], const uint8_t *in, size_t inlen);
|
||||||
|
#define sha3_512 FIPS202_NAMESPACE(sha3_512)
|
||||||
|
void sha3_512(uint8_t h[64], const uint8_t *in, size_t inlen);
|
||||||
|
|
||||||
|
#endif
|
||||||
98
app/src/main/cpp/pq/mldsa65/ntt.c
Normal file
98
app/src/main/cpp/pq/mldsa65/ntt.c
Normal file
|
|
@ -0,0 +1,98 @@
|
||||||
|
#include <stdint.h>
|
||||||
|
#include "params.h"
|
||||||
|
#include "ntt.h"
|
||||||
|
#include "reduce.h"
|
||||||
|
|
||||||
|
static const int32_t zetas[N] = {
|
||||||
|
0, 25847, -2608894, -518909, 237124, -777960, -876248, 466468,
|
||||||
|
1826347, 2353451, -359251, -2091905, 3119733, -2884855, 3111497, 2680103,
|
||||||
|
2725464, 1024112, -1079900, 3585928, -549488, -1119584, 2619752, -2108549,
|
||||||
|
-2118186, -3859737, -1399561, -3277672, 1757237, -19422, 4010497, 280005,
|
||||||
|
2706023, 95776, 3077325, 3530437, -1661693, -3592148, -2537516, 3915439,
|
||||||
|
-3861115, -3043716, 3574422, -2867647, 3539968, -300467, 2348700, -539299,
|
||||||
|
-1699267, -1643818, 3505694, -3821735, 3507263, -2140649, -1600420, 3699596,
|
||||||
|
811944, 531354, 954230, 3881043, 3900724, -2556880, 2071892, -2797779,
|
||||||
|
-3930395, -1528703, -3677745, -3041255, -1452451, 3475950, 2176455, -1585221,
|
||||||
|
-1257611, 1939314, -4083598, -1000202, -3190144, -3157330, -3632928, 126922,
|
||||||
|
3412210, -983419, 2147896, 2715295, -2967645, -3693493, -411027, -2477047,
|
||||||
|
-671102, -1228525, -22981, -1308169, -381987, 1349076, 1852771, -1430430,
|
||||||
|
-3343383, 264944, 508951, 3097992, 44288, -1100098, 904516, 3958618,
|
||||||
|
-3724342, -8578, 1653064, -3249728, 2389356, -210977, 759969, -1316856,
|
||||||
|
189548, -3553272, 3159746, -1851402, -2409325, -177440, 1315589, 1341330,
|
||||||
|
1285669, -1584928, -812732, -1439742, -3019102, -3881060, -3628969, 3839961,
|
||||||
|
2091667, 3407706, 2316500, 3817976, -3342478, 2244091, -2446433, -3562462,
|
||||||
|
266997, 2434439, -1235728, 3513181, -3520352, -3759364, -1197226, -3193378,
|
||||||
|
900702, 1859098, 909542, 819034, 495491, -1613174, -43260, -522500,
|
||||||
|
-655327, -3122442, 2031748, 3207046, -3556995, -525098, -768622, -3595838,
|
||||||
|
342297, 286988, -2437823, 4108315, 3437287, -3342277, 1735879, 203044,
|
||||||
|
2842341, 2691481, -2590150, 1265009, 4055324, 1247620, 2486353, 1595974,
|
||||||
|
-3767016, 1250494, 2635921, -3548272, -2994039, 1869119, 1903435, -1050970,
|
||||||
|
-1333058, 1237275, -3318210, -1430225, -451100, 1312455, 3306115, -1962642,
|
||||||
|
-1279661, 1917081, -2546312, -1374803, 1500165, 777191, 2235880, 3406031,
|
||||||
|
-542412, -2831860, -1671176, -1846953, -2584293, -3724270, 594136, -3776993,
|
||||||
|
-2013608, 2432395, 2454455, -164721, 1957272, 3369112, 185531, -1207385,
|
||||||
|
-3183426, 162844, 1616392, 3014001, 810149, 1652634, -3694233, -1799107,
|
||||||
|
-3038916, 3523897, 3866901, 269760, 2213111, -975884, 1717735, 472078,
|
||||||
|
-426683, 1723600, -1803090, 1910376, -1667432, -1104333, -260646, -3833893,
|
||||||
|
-2939036, -2235985, -420899, -2286327, 183443, -976891, 1612842, -3545687,
|
||||||
|
-554416, 3919660, -48306, -1362209, 3937738, 1400424, -846154, 1976782
|
||||||
|
};
|
||||||
|
|
||||||
|
/*************************************************
|
||||||
|
* Name: ntt
|
||||||
|
*
|
||||||
|
* Description: Forward NTT, in-place. No modular reduction is performed after
|
||||||
|
* additions or subtractions. Output vector is in bitreversed order.
|
||||||
|
*
|
||||||
|
* Arguments: - uint32_t p[N]: input/output coefficient array
|
||||||
|
**************************************************/
|
||||||
|
void ntt(int32_t a[N]) {
|
||||||
|
unsigned int len, start, j, k;
|
||||||
|
int32_t zeta, t;
|
||||||
|
|
||||||
|
k = 0;
|
||||||
|
for(len = 128; len > 0; len >>= 1) {
|
||||||
|
for(start = 0; start < N; start = j + len) {
|
||||||
|
zeta = zetas[++k];
|
||||||
|
for(j = start; j < start + len; ++j) {
|
||||||
|
t = montgomery_reduce((int64_t)zeta * a[j + len]);
|
||||||
|
a[j + len] = a[j] - t;
|
||||||
|
a[j] = a[j] + t;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/*************************************************
|
||||||
|
* Name: invntt_tomont
|
||||||
|
*
|
||||||
|
* Description: Inverse NTT and multiplication by Montgomery factor 2^32.
|
||||||
|
* In-place. No modular reductions after additions or
|
||||||
|
* subtractions; input coefficients need to be smaller than
|
||||||
|
* Q in absolute value. Output coefficient are smaller than Q in
|
||||||
|
* absolute value.
|
||||||
|
*
|
||||||
|
* Arguments: - uint32_t p[N]: input/output coefficient array
|
||||||
|
**************************************************/
|
||||||
|
void invntt_tomont(int32_t a[N]) {
|
||||||
|
unsigned int start, len, j, k;
|
||||||
|
int32_t t, zeta;
|
||||||
|
const int32_t f = 41978; // mont^2/256
|
||||||
|
|
||||||
|
k = 256;
|
||||||
|
for(len = 1; len < N; len <<= 1) {
|
||||||
|
for(start = 0; start < N; start = j + len) {
|
||||||
|
zeta = -zetas[--k];
|
||||||
|
for(j = start; j < start + len; ++j) {
|
||||||
|
t = a[j];
|
||||||
|
a[j] = t + a[j + len];
|
||||||
|
a[j + len] = t - a[j + len];
|
||||||
|
a[j + len] = montgomery_reduce((int64_t)zeta * a[j + len]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for(j = 0; j < N; ++j) {
|
||||||
|
a[j] = montgomery_reduce((int64_t)f * a[j]);
|
||||||
|
}
|
||||||
|
}
|
||||||
13
app/src/main/cpp/pq/mldsa65/ntt.h
Normal file
13
app/src/main/cpp/pq/mldsa65/ntt.h
Normal file
|
|
@ -0,0 +1,13 @@
|
||||||
|
#ifndef NTT_H
|
||||||
|
#define NTT_H
|
||||||
|
|
||||||
|
#include <stdint.h>
|
||||||
|
#include "params.h"
|
||||||
|
|
||||||
|
#define ntt DILITHIUM_NAMESPACE(ntt)
|
||||||
|
void ntt(int32_t a[N]);
|
||||||
|
|
||||||
|
#define invntt_tomont DILITHIUM_NAMESPACE(invntt_tomont)
|
||||||
|
void invntt_tomont(int32_t a[N]);
|
||||||
|
|
||||||
|
#endif
|
||||||
237
app/src/main/cpp/pq/mldsa65/packing.c
Normal file
237
app/src/main/cpp/pq/mldsa65/packing.c
Normal file
|
|
@ -0,0 +1,237 @@
|
||||||
|
#include "params.h"
|
||||||
|
#include "packing.h"
|
||||||
|
#include "polyvec.h"
|
||||||
|
#include "poly.h"
|
||||||
|
|
||||||
|
/*************************************************
|
||||||
|
* Name: pack_pk
|
||||||
|
*
|
||||||
|
* Description: Bit-pack public key pk = (rho, t1).
|
||||||
|
*
|
||||||
|
* Arguments: - uint8_t pk[]: output byte array
|
||||||
|
* - const uint8_t rho[]: byte array containing rho
|
||||||
|
* - const polyveck *t1: pointer to vector t1
|
||||||
|
**************************************************/
|
||||||
|
void pack_pk(uint8_t pk[CRYPTO_PUBLICKEYBYTES],
|
||||||
|
const uint8_t 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)
|
||||||
|
polyt1_pack(pk + i*POLYT1_PACKEDBYTES, &t1->vec[i]);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*************************************************
|
||||||
|
* Name: unpack_pk
|
||||||
|
*
|
||||||
|
* Description: Unpack public key pk = (rho, t1).
|
||||||
|
*
|
||||||
|
* Arguments: - const uint8_t rho[]: output byte array for rho
|
||||||
|
* - const polyveck *t1: pointer to output vector t1
|
||||||
|
* - uint8_t pk[]: byte array containing bit-packed pk
|
||||||
|
**************************************************/
|
||||||
|
void unpack_pk(uint8_t rho[SEEDBYTES],
|
||||||
|
polyveck *t1,
|
||||||
|
const uint8_t pk[CRYPTO_PUBLICKEYBYTES])
|
||||||
|
{
|
||||||
|
unsigned int i;
|
||||||
|
|
||||||
|
for(i = 0; i < SEEDBYTES; ++i)
|
||||||
|
rho[i] = pk[i];
|
||||||
|
pk += SEEDBYTES;
|
||||||
|
|
||||||
|
for(i = 0; i < K; ++i)
|
||||||
|
polyt1_unpack(&t1->vec[i], pk + i*POLYT1_PACKEDBYTES);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*************************************************
|
||||||
|
* Name: pack_sk
|
||||||
|
*
|
||||||
|
* Description: Bit-pack secret key sk = (rho, tr, key, t0, s1, s2).
|
||||||
|
*
|
||||||
|
* Arguments: - uint8_t sk[]: output byte array
|
||||||
|
* - const uint8_t rho[]: byte array containing rho
|
||||||
|
* - const uint8_t tr[]: byte array containing tr
|
||||||
|
* - const uint8_t key[]: byte array containing key
|
||||||
|
* - const polyveck *t0: pointer to vector t0
|
||||||
|
* - const polyvecl *s1: pointer to vector s1
|
||||||
|
* - const polyveck *s2: pointer to vector s2
|
||||||
|
**************************************************/
|
||||||
|
void pack_sk(uint8_t sk[CRYPTO_SECRETKEYBYTES],
|
||||||
|
const uint8_t rho[SEEDBYTES],
|
||||||
|
const uint8_t tr[TRBYTES],
|
||||||
|
const uint8_t key[SEEDBYTES],
|
||||||
|
const polyveck *t0,
|
||||||
|
const polyvecl *s1,
|
||||||
|
const polyveck *s2)
|
||||||
|
{
|
||||||
|
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 < TRBYTES; ++i)
|
||||||
|
sk[i] = tr[i];
|
||||||
|
sk += TRBYTES;
|
||||||
|
|
||||||
|
for(i = 0; i < L; ++i)
|
||||||
|
polyeta_pack(sk + i*POLYETA_PACKEDBYTES, &s1->vec[i]);
|
||||||
|
sk += L*POLYETA_PACKEDBYTES;
|
||||||
|
|
||||||
|
for(i = 0; i < K; ++i)
|
||||||
|
polyeta_pack(sk + i*POLYETA_PACKEDBYTES, &s2->vec[i]);
|
||||||
|
sk += K*POLYETA_PACKEDBYTES;
|
||||||
|
|
||||||
|
for(i = 0; i < K; ++i)
|
||||||
|
polyt0_pack(sk + i*POLYT0_PACKEDBYTES, &t0->vec[i]);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*************************************************
|
||||||
|
* Name: unpack_sk
|
||||||
|
*
|
||||||
|
* Description: Unpack secret key sk = (rho, tr, key, t0, s1, s2).
|
||||||
|
*
|
||||||
|
* Arguments: - const uint8_t rho[]: output byte array for rho
|
||||||
|
* - const uint8_t tr[]: output byte array for tr
|
||||||
|
* - const uint8_t key[]: output byte array for key
|
||||||
|
* - const polyveck *t0: pointer to output vector t0
|
||||||
|
* - const polyvecl *s1: pointer to output vector s1
|
||||||
|
* - const polyveck *s2: pointer to output vector s2
|
||||||
|
* - uint8_t sk[]: byte array containing bit-packed sk
|
||||||
|
**************************************************/
|
||||||
|
void unpack_sk(uint8_t rho[SEEDBYTES],
|
||||||
|
uint8_t tr[TRBYTES],
|
||||||
|
uint8_t key[SEEDBYTES],
|
||||||
|
polyveck *t0,
|
||||||
|
polyvecl *s1,
|
||||||
|
polyveck *s2,
|
||||||
|
const uint8_t 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 < TRBYTES; ++i)
|
||||||
|
tr[i] = sk[i];
|
||||||
|
sk += TRBYTES;
|
||||||
|
|
||||||
|
for(i=0; i < L; ++i)
|
||||||
|
polyeta_unpack(&s1->vec[i], sk + i*POLYETA_PACKEDBYTES);
|
||||||
|
sk += L*POLYETA_PACKEDBYTES;
|
||||||
|
|
||||||
|
for(i=0; i < K; ++i)
|
||||||
|
polyeta_unpack(&s2->vec[i], sk + i*POLYETA_PACKEDBYTES);
|
||||||
|
sk += K*POLYETA_PACKEDBYTES;
|
||||||
|
|
||||||
|
for(i=0; i < K; ++i)
|
||||||
|
polyt0_unpack(&t0->vec[i], sk + i*POLYT0_PACKEDBYTES);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*************************************************
|
||||||
|
* Name: pack_sig
|
||||||
|
*
|
||||||
|
* Description: Bit-pack signature sig = (c, z, h).
|
||||||
|
*
|
||||||
|
* Arguments: - uint8_t sig[]: output byte array
|
||||||
|
* - const uint8_t *c: pointer to challenge hash length SEEDBYTES
|
||||||
|
* - const polyvecl *z: pointer to vector z
|
||||||
|
* - const polyveck *h: pointer to hint vector h
|
||||||
|
**************************************************/
|
||||||
|
void pack_sig(uint8_t sig[CRYPTO_BYTES],
|
||||||
|
const uint8_t c[CTILDEBYTES],
|
||||||
|
const polyvecl *z,
|
||||||
|
const polyveck *h)
|
||||||
|
{
|
||||||
|
unsigned int i, j, k;
|
||||||
|
|
||||||
|
for(i=0; i < CTILDEBYTES; ++i)
|
||||||
|
sig[i] = c[i];
|
||||||
|
sig += CTILDEBYTES;
|
||||||
|
|
||||||
|
for(i = 0; i < L; ++i)
|
||||||
|
polyz_pack(sig + i*POLYZ_PACKEDBYTES, &z->vec[i]);
|
||||||
|
sig += L*POLYZ_PACKEDBYTES;
|
||||||
|
|
||||||
|
/* Encode h */
|
||||||
|
for(i = 0; i < OMEGA + K; ++i)
|
||||||
|
sig[i] = 0;
|
||||||
|
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/*************************************************
|
||||||
|
* Name: unpack_sig
|
||||||
|
*
|
||||||
|
* Description: Unpack signature sig = (c, z, h).
|
||||||
|
*
|
||||||
|
* Arguments: - uint8_t *c: pointer to output challenge hash
|
||||||
|
* - polyvecl *z: pointer to output vector z
|
||||||
|
* - polyveck *h: pointer to output hint vector h
|
||||||
|
* - const uint8_t sig[]: byte array containing
|
||||||
|
* bit-packed signature
|
||||||
|
*
|
||||||
|
* Returns 1 in case of malformed signature; otherwise 0.
|
||||||
|
**************************************************/
|
||||||
|
int unpack_sig(uint8_t c[CTILDEBYTES],
|
||||||
|
polyvecl *z,
|
||||||
|
polyveck *h,
|
||||||
|
const uint8_t sig[CRYPTO_BYTES])
|
||||||
|
{
|
||||||
|
unsigned int i, j, k;
|
||||||
|
|
||||||
|
for(i = 0; i < CTILDEBYTES; ++i)
|
||||||
|
c[i] = sig[i];
|
||||||
|
sig += CTILDEBYTES;
|
||||||
|
|
||||||
|
for(i = 0; i < L; ++i)
|
||||||
|
polyz_unpack(&z->vec[i], sig + i*POLYZ_PACKEDBYTES);
|
||||||
|
sig += L*POLYZ_PACKEDBYTES;
|
||||||
|
|
||||||
|
/* 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;
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
38
app/src/main/cpp/pq/mldsa65/packing.h
Normal file
38
app/src/main/cpp/pq/mldsa65/packing.h
Normal file
|
|
@ -0,0 +1,38 @@
|
||||||
|
#ifndef PACKING_H
|
||||||
|
#define PACKING_H
|
||||||
|
|
||||||
|
#include <stdint.h>
|
||||||
|
#include "params.h"
|
||||||
|
#include "polyvec.h"
|
||||||
|
|
||||||
|
#define pack_pk DILITHIUM_NAMESPACE(pack_pk)
|
||||||
|
void pack_pk(uint8_t pk[CRYPTO_PUBLICKEYBYTES], const uint8_t rho[SEEDBYTES], const polyveck *t1);
|
||||||
|
|
||||||
|
#define pack_sk DILITHIUM_NAMESPACE(pack_sk)
|
||||||
|
void pack_sk(uint8_t sk[CRYPTO_SECRETKEYBYTES],
|
||||||
|
const uint8_t rho[SEEDBYTES],
|
||||||
|
const uint8_t tr[TRBYTES],
|
||||||
|
const uint8_t key[SEEDBYTES],
|
||||||
|
const polyveck *t0,
|
||||||
|
const polyvecl *s1,
|
||||||
|
const polyveck *s2);
|
||||||
|
|
||||||
|
#define pack_sig DILITHIUM_NAMESPACE(pack_sig)
|
||||||
|
void pack_sig(uint8_t sig[CRYPTO_BYTES], const uint8_t c[CTILDEBYTES], const polyvecl *z, const polyveck *h);
|
||||||
|
|
||||||
|
#define unpack_pk DILITHIUM_NAMESPACE(unpack_pk)
|
||||||
|
void unpack_pk(uint8_t rho[SEEDBYTES], polyveck *t1, const uint8_t pk[CRYPTO_PUBLICKEYBYTES]);
|
||||||
|
|
||||||
|
#define unpack_sk DILITHIUM_NAMESPACE(unpack_sk)
|
||||||
|
void unpack_sk(uint8_t rho[SEEDBYTES],
|
||||||
|
uint8_t tr[TRBYTES],
|
||||||
|
uint8_t key[SEEDBYTES],
|
||||||
|
polyveck *t0,
|
||||||
|
polyvecl *s1,
|
||||||
|
polyveck *s2,
|
||||||
|
const uint8_t sk[CRYPTO_SECRETKEYBYTES]);
|
||||||
|
|
||||||
|
#define unpack_sig DILITHIUM_NAMESPACE(unpack_sig)
|
||||||
|
int unpack_sig(uint8_t c[CTILDEBYTES], polyvecl *z, polyveck *h, const uint8_t sig[CRYPTO_BYTES]);
|
||||||
|
|
||||||
|
#endif
|
||||||
80
app/src/main/cpp/pq/mldsa65/params.h
Normal file
80
app/src/main/cpp/pq/mldsa65/params.h
Normal file
|
|
@ -0,0 +1,80 @@
|
||||||
|
#ifndef PARAMS_H
|
||||||
|
#define PARAMS_H
|
||||||
|
|
||||||
|
#include "config.h"
|
||||||
|
|
||||||
|
#define SEEDBYTES 32
|
||||||
|
#define CRHBYTES 64
|
||||||
|
#define TRBYTES 64
|
||||||
|
#define RNDBYTES 32
|
||||||
|
#define N 256
|
||||||
|
#define Q 8380417
|
||||||
|
#define D 13
|
||||||
|
#define ROOT_OF_UNITY 1753
|
||||||
|
|
||||||
|
#if DILITHIUM_MODE == 2
|
||||||
|
#define K 4
|
||||||
|
#define L 4
|
||||||
|
#define ETA 2
|
||||||
|
#define TAU 39
|
||||||
|
#define BETA 78
|
||||||
|
#define GAMMA1 (1 << 17)
|
||||||
|
#define GAMMA2 ((Q-1)/88)
|
||||||
|
#define OMEGA 80
|
||||||
|
#define CTILDEBYTES 32
|
||||||
|
|
||||||
|
#elif DILITHIUM_MODE == 3
|
||||||
|
#define K 6
|
||||||
|
#define L 5
|
||||||
|
#define ETA 4
|
||||||
|
#define TAU 49
|
||||||
|
#define BETA 196
|
||||||
|
#define GAMMA1 (1 << 19)
|
||||||
|
#define GAMMA2 ((Q-1)/32)
|
||||||
|
#define OMEGA 55
|
||||||
|
#define CTILDEBYTES 48
|
||||||
|
|
||||||
|
#elif DILITHIUM_MODE == 5
|
||||||
|
#define K 8
|
||||||
|
#define L 7
|
||||||
|
#define ETA 2
|
||||||
|
#define TAU 60
|
||||||
|
#define BETA 120
|
||||||
|
#define GAMMA1 (1 << 19)
|
||||||
|
#define GAMMA2 ((Q-1)/32)
|
||||||
|
#define OMEGA 75
|
||||||
|
#define CTILDEBYTES 64
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#define POLYT1_PACKEDBYTES 320
|
||||||
|
#define POLYT0_PACKEDBYTES 416
|
||||||
|
#define POLYVECH_PACKEDBYTES (OMEGA + K)
|
||||||
|
|
||||||
|
#if GAMMA1 == (1 << 17)
|
||||||
|
#define POLYZ_PACKEDBYTES 576
|
||||||
|
#elif GAMMA1 == (1 << 19)
|
||||||
|
#define POLYZ_PACKEDBYTES 640
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#if GAMMA2 == (Q-1)/88
|
||||||
|
#define POLYW1_PACKEDBYTES 192
|
||||||
|
#elif GAMMA2 == (Q-1)/32
|
||||||
|
#define POLYW1_PACKEDBYTES 128
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#if ETA == 2
|
||||||
|
#define POLYETA_PACKEDBYTES 96
|
||||||
|
#elif ETA == 4
|
||||||
|
#define POLYETA_PACKEDBYTES 128
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#define CRYPTO_PUBLICKEYBYTES (SEEDBYTES + K*POLYT1_PACKEDBYTES)
|
||||||
|
#define CRYPTO_SECRETKEYBYTES (2*SEEDBYTES \
|
||||||
|
+ TRBYTES \
|
||||||
|
+ L*POLYETA_PACKEDBYTES \
|
||||||
|
+ K*POLYETA_PACKEDBYTES \
|
||||||
|
+ K*POLYT0_PACKEDBYTES)
|
||||||
|
#define CRYPTO_BYTES (CTILDEBYTES + L*POLYZ_PACKEDBYTES + POLYVECH_PACKEDBYTES)
|
||||||
|
|
||||||
|
#endif
|
||||||
907
app/src/main/cpp/pq/mldsa65/poly.c
Normal file
907
app/src/main/cpp/pq/mldsa65/poly.c
Normal file
|
|
@ -0,0 +1,907 @@
|
||||||
|
#include <stdint.h>
|
||||||
|
#include "params.h"
|
||||||
|
#include "poly.h"
|
||||||
|
#include "ntt.h"
|
||||||
|
#include "reduce.h"
|
||||||
|
#include "rounding.h"
|
||||||
|
#include "symmetric.h"
|
||||||
|
|
||||||
|
#ifdef DBENCH
|
||||||
|
#include "test/cpucycles.h"
|
||||||
|
extern const uint64_t timing_overhead;
|
||||||
|
extern uint64_t *tred, *tadd, *tmul, *tround, *tsample, *tpack;
|
||||||
|
#define DBENCH_START() uint64_t time = cpucycles()
|
||||||
|
#define DBENCH_STOP(t) t += cpucycles() - time - timing_overhead
|
||||||
|
#else
|
||||||
|
#define DBENCH_START()
|
||||||
|
#define DBENCH_STOP(t)
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/*************************************************
|
||||||
|
* Name: poly_reduce
|
||||||
|
*
|
||||||
|
* Description: Inplace reduction of all coefficients of polynomial to
|
||||||
|
* representative in [-6283008,6283008].
|
||||||
|
*
|
||||||
|
* Arguments: - poly *a: pointer to input/output polynomial
|
||||||
|
**************************************************/
|
||||||
|
void poly_reduce(poly *a) {
|
||||||
|
unsigned int i;
|
||||||
|
DBENCH_START();
|
||||||
|
|
||||||
|
for(i = 0; i < N; ++i)
|
||||||
|
a->coeffs[i] = reduce32(a->coeffs[i]);
|
||||||
|
|
||||||
|
DBENCH_STOP(*tred);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*************************************************
|
||||||
|
* Name: poly_caddq
|
||||||
|
*
|
||||||
|
* Description: For all coefficients of in/out polynomial add Q if
|
||||||
|
* coefficient is negative.
|
||||||
|
*
|
||||||
|
* Arguments: - poly *a: pointer to input/output polynomial
|
||||||
|
**************************************************/
|
||||||
|
void poly_caddq(poly *a) {
|
||||||
|
unsigned int i;
|
||||||
|
DBENCH_START();
|
||||||
|
|
||||||
|
for(i = 0; i < N; ++i)
|
||||||
|
a->coeffs[i] = caddq(a->coeffs[i]);
|
||||||
|
|
||||||
|
DBENCH_STOP(*tred);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*************************************************
|
||||||
|
* 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 poly_add(poly *c, const poly *a, const poly *b) {
|
||||||
|
unsigned int i;
|
||||||
|
DBENCH_START();
|
||||||
|
|
||||||
|
for(i = 0; i < N; ++i)
|
||||||
|
c->coeffs[i] = a->coeffs[i] + b->coeffs[i];
|
||||||
|
|
||||||
|
DBENCH_STOP(*tadd);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*************************************************
|
||||||
|
* Name: poly_sub
|
||||||
|
*
|
||||||
|
* Description: Subtract polynomials. 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 poly_sub(poly *c, const poly *a, const poly *b) {
|
||||||
|
unsigned int i;
|
||||||
|
DBENCH_START();
|
||||||
|
|
||||||
|
for(i = 0; i < N; ++i)
|
||||||
|
c->coeffs[i] = a->coeffs[i] - b->coeffs[i];
|
||||||
|
|
||||||
|
DBENCH_STOP(*tadd);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*************************************************
|
||||||
|
* Name: poly_shiftl
|
||||||
|
*
|
||||||
|
* Description: Multiply polynomial by 2^D without modular reduction. Assumes
|
||||||
|
* input coefficients to be less than 2^{31-D} in absolute value.
|
||||||
|
*
|
||||||
|
* Arguments: - poly *a: pointer to input/output polynomial
|
||||||
|
**************************************************/
|
||||||
|
void poly_shiftl(poly *a) {
|
||||||
|
unsigned int i;
|
||||||
|
DBENCH_START();
|
||||||
|
|
||||||
|
for(i = 0; i < N; ++i)
|
||||||
|
a->coeffs[i] <<= D;
|
||||||
|
|
||||||
|
DBENCH_STOP(*tmul);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*************************************************
|
||||||
|
* Name: poly_ntt
|
||||||
|
*
|
||||||
|
* Description: Inplace forward NTT. Coefficients can grow by
|
||||||
|
* 8*Q in absolute value.
|
||||||
|
*
|
||||||
|
* Arguments: - poly *a: pointer to input/output polynomial
|
||||||
|
**************************************************/
|
||||||
|
void poly_ntt(poly *a) {
|
||||||
|
DBENCH_START();
|
||||||
|
|
||||||
|
ntt(a->coeffs);
|
||||||
|
|
||||||
|
DBENCH_STOP(*tmul);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*************************************************
|
||||||
|
* Name: poly_invntt_tomont
|
||||||
|
*
|
||||||
|
* Description: Inplace inverse NTT and multiplication by 2^{32}.
|
||||||
|
* Input coefficients need to be less than Q in absolute
|
||||||
|
* value and output coefficients are again bounded by Q.
|
||||||
|
*
|
||||||
|
* Arguments: - poly *a: pointer to input/output polynomial
|
||||||
|
**************************************************/
|
||||||
|
void poly_invntt_tomont(poly *a) {
|
||||||
|
DBENCH_START();
|
||||||
|
|
||||||
|
invntt_tomont(a->coeffs);
|
||||||
|
|
||||||
|
DBENCH_STOP(*tmul);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*************************************************
|
||||||
|
* Name: poly_pointwise_montgomery
|
||||||
|
*
|
||||||
|
* Description: Pointwise multiplication of polynomials in NTT domain
|
||||||
|
* representation and multiplication of resulting polynomial
|
||||||
|
* by 2^{-32}.
|
||||||
|
*
|
||||||
|
* Arguments: - poly *c: pointer to output polynomial
|
||||||
|
* - const poly *a: pointer to first input polynomial
|
||||||
|
* - const poly *b: pointer to second input polynomial
|
||||||
|
**************************************************/
|
||||||
|
void poly_pointwise_montgomery(poly *c, const poly *a, const poly *b) {
|
||||||
|
unsigned int i;
|
||||||
|
DBENCH_START();
|
||||||
|
|
||||||
|
for(i = 0; i < N; ++i)
|
||||||
|
c->coeffs[i] = montgomery_reduce((int64_t)a->coeffs[i] * b->coeffs[i]);
|
||||||
|
|
||||||
|
DBENCH_STOP(*tmul);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*************************************************
|
||||||
|
* 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 c0
|
||||||
|
* - const poly *a: pointer to input polynomial
|
||||||
|
**************************************************/
|
||||||
|
void poly_power2round(poly *a1, poly *a0, const poly *a) {
|
||||||
|
unsigned int i;
|
||||||
|
DBENCH_START();
|
||||||
|
|
||||||
|
for(i = 0; i < N; ++i)
|
||||||
|
a1->coeffs[i] = power2round(&a0->coeffs[i], a->coeffs[i]);
|
||||||
|
|
||||||
|
DBENCH_STOP(*tround);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*************************************************
|
||||||
|
* 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 c0
|
||||||
|
* - const poly *a: pointer to input polynomial
|
||||||
|
**************************************************/
|
||||||
|
void poly_decompose(poly *a1, poly *a0, const poly *a) {
|
||||||
|
unsigned int i;
|
||||||
|
DBENCH_START();
|
||||||
|
|
||||||
|
for(i = 0; i < N; ++i)
|
||||||
|
a1->coeffs[i] = decompose(&a0->coeffs[i], a->coeffs[i]);
|
||||||
|
|
||||||
|
DBENCH_STOP(*tround);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*************************************************
|
||||||
|
* Name: poly_make_hint
|
||||||
|
*
|
||||||
|
* Description: Compute hint polynomial. The coefficients of which indicate
|
||||||
|
* whether the low bits of the corresponding coefficient of
|
||||||
|
* the input polynomial overflow into the high bits.
|
||||||
|
*
|
||||||
|
* Arguments: - poly *h: pointer to output hint polynomial
|
||||||
|
* - const poly *a0: pointer to low part of input polynomial
|
||||||
|
* - const poly *a1: pointer to high part of input polynomial
|
||||||
|
*
|
||||||
|
* Returns number of 1 bits.
|
||||||
|
**************************************************/
|
||||||
|
unsigned int poly_make_hint(poly *h, const poly *a0, const poly *a1) {
|
||||||
|
unsigned int i, s = 0;
|
||||||
|
DBENCH_START();
|
||||||
|
|
||||||
|
for(i = 0; i < N; ++i) {
|
||||||
|
h->coeffs[i] = make_hint(a0->coeffs[i], a1->coeffs[i]);
|
||||||
|
s += h->coeffs[i];
|
||||||
|
}
|
||||||
|
|
||||||
|
DBENCH_STOP(*tround);
|
||||||
|
return s;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*************************************************
|
||||||
|
* Name: poly_use_hint
|
||||||
|
*
|
||||||
|
* Description: Use hint polynomial to correct the high bits of a polynomial.
|
||||||
|
*
|
||||||
|
* Arguments: - poly *b: pointer to output polynomial with corrected high bits
|
||||||
|
* - const poly *a: pointer to input polynomial
|
||||||
|
* - const poly *h: pointer to input hint polynomial
|
||||||
|
**************************************************/
|
||||||
|
void poly_use_hint(poly *b, const poly *a, const poly *h) {
|
||||||
|
unsigned int i;
|
||||||
|
DBENCH_START();
|
||||||
|
|
||||||
|
for(i = 0; i < N; ++i)
|
||||||
|
b->coeffs[i] = use_hint(a->coeffs[i], h->coeffs[i]);
|
||||||
|
|
||||||
|
DBENCH_STOP(*tround);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*************************************************
|
||||||
|
* Name: poly_chknorm
|
||||||
|
*
|
||||||
|
* Description: Check infinity norm of polynomial against given bound.
|
||||||
|
* Assumes input coefficients were reduced by reduce32().
|
||||||
|
*
|
||||||
|
* Arguments: - const poly *a: pointer to polynomial
|
||||||
|
* - int32_t B: norm bound
|
||||||
|
*
|
||||||
|
* Returns 0 if norm is strictly smaller than B <= (Q-1)/8 and 1 otherwise.
|
||||||
|
**************************************************/
|
||||||
|
int poly_chknorm(const poly *a, int32_t B) {
|
||||||
|
unsigned int i;
|
||||||
|
int32_t t;
|
||||||
|
DBENCH_START();
|
||||||
|
|
||||||
|
if(B > (Q-1)/8)
|
||||||
|
return 1;
|
||||||
|
|
||||||
|
/* 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 */
|
||||||
|
t = a->coeffs[i] >> 31;
|
||||||
|
t = a->coeffs[i] - (t & 2*a->coeffs[i]);
|
||||||
|
|
||||||
|
if(t >= B) {
|
||||||
|
DBENCH_STOP(*tsample);
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
DBENCH_STOP(*tsample);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*************************************************
|
||||||
|
* Name: rej_uniform
|
||||||
|
*
|
||||||
|
* Description: Sample uniformly random coefficients in [0, Q-1] by
|
||||||
|
* performing rejection sampling on array of random bytes.
|
||||||
|
*
|
||||||
|
* Arguments: - int32_t *a: pointer to output array (allocated)
|
||||||
|
* - unsigned int len: number of coefficients to be sampled
|
||||||
|
* - const uint8_t *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_uniform(int32_t *a,
|
||||||
|
unsigned int len,
|
||||||
|
const uint8_t *buf,
|
||||||
|
unsigned int buflen)
|
||||||
|
{
|
||||||
|
unsigned int ctr, pos;
|
||||||
|
uint32_t t;
|
||||||
|
DBENCH_START();
|
||||||
|
|
||||||
|
ctr = pos = 0;
|
||||||
|
while(ctr < len && pos + 3 <= buflen) {
|
||||||
|
t = buf[pos++];
|
||||||
|
t |= (uint32_t)buf[pos++] << 8;
|
||||||
|
t |= (uint32_t)buf[pos++] << 16;
|
||||||
|
t &= 0x7FFFFF;
|
||||||
|
|
||||||
|
if(t < Q)
|
||||||
|
a[ctr++] = t;
|
||||||
|
}
|
||||||
|
|
||||||
|
DBENCH_STOP(*tsample);
|
||||||
|
return ctr;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*************************************************
|
||||||
|
* Name: poly_uniform
|
||||||
|
*
|
||||||
|
* Description: Sample polynomial with uniformly random coefficients
|
||||||
|
* in [0,Q-1] by performing rejection sampling on the
|
||||||
|
* output stream of SHAKE128(seed|nonce)
|
||||||
|
*
|
||||||
|
* Arguments: - poly *a: pointer to output polynomial
|
||||||
|
* - const uint8_t seed[]: byte array with seed of length SEEDBYTES
|
||||||
|
* - uint16_t nonce: 2-byte nonce
|
||||||
|
**************************************************/
|
||||||
|
#define POLY_UNIFORM_NBLOCKS ((768 + STREAM128_BLOCKBYTES - 1)/STREAM128_BLOCKBYTES)
|
||||||
|
void poly_uniform(poly *a,
|
||||||
|
const uint8_t seed[SEEDBYTES],
|
||||||
|
uint16_t nonce)
|
||||||
|
{
|
||||||
|
unsigned int i, ctr, off;
|
||||||
|
unsigned int buflen = POLY_UNIFORM_NBLOCKS*STREAM128_BLOCKBYTES;
|
||||||
|
uint8_t buf[POLY_UNIFORM_NBLOCKS*STREAM128_BLOCKBYTES + 2];
|
||||||
|
stream128_state state;
|
||||||
|
|
||||||
|
stream128_init(&state, seed, nonce);
|
||||||
|
stream128_squeezeblocks(buf, POLY_UNIFORM_NBLOCKS, &state);
|
||||||
|
|
||||||
|
ctr = rej_uniform(a->coeffs, N, buf, buflen);
|
||||||
|
|
||||||
|
while(ctr < N) {
|
||||||
|
off = buflen % 3;
|
||||||
|
for(i = 0; i < off; ++i)
|
||||||
|
buf[i] = buf[buflen - off + i];
|
||||||
|
|
||||||
|
stream128_squeezeblocks(buf + off, 1, &state);
|
||||||
|
buflen = STREAM128_BLOCKBYTES + off;
|
||||||
|
ctr += rej_uniform(a->coeffs + ctr, N - ctr, buf, buflen);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/*************************************************
|
||||||
|
* Name: rej_eta
|
||||||
|
*
|
||||||
|
* Description: Sample uniformly random coefficients in [-ETA, ETA] by
|
||||||
|
* performing rejection sampling on array of random bytes.
|
||||||
|
*
|
||||||
|
* Arguments: - int32_t *a: pointer to output array (allocated)
|
||||||
|
* - unsigned int len: number of coefficients to be sampled
|
||||||
|
* - const uint8_t *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(int32_t *a,
|
||||||
|
unsigned int len,
|
||||||
|
const uint8_t *buf,
|
||||||
|
unsigned int buflen)
|
||||||
|
{
|
||||||
|
unsigned int ctr, pos;
|
||||||
|
uint32_t t0, t1;
|
||||||
|
DBENCH_START();
|
||||||
|
|
||||||
|
ctr = pos = 0;
|
||||||
|
while(ctr < len && pos < buflen) {
|
||||||
|
t0 = buf[pos] & 0x0F;
|
||||||
|
t1 = buf[pos++] >> 4;
|
||||||
|
|
||||||
|
#if ETA == 2
|
||||||
|
if(t0 < 15) {
|
||||||
|
t0 = t0 - (205*t0 >> 10)*5;
|
||||||
|
a[ctr++] = 2 - t0;
|
||||||
|
}
|
||||||
|
if(t1 < 15 && ctr < len) {
|
||||||
|
t1 = t1 - (205*t1 >> 10)*5;
|
||||||
|
a[ctr++] = 2 - t1;
|
||||||
|
}
|
||||||
|
#elif ETA == 4
|
||||||
|
if(t0 < 9)
|
||||||
|
a[ctr++] = 4 - t0;
|
||||||
|
if(t1 < 9 && ctr < len)
|
||||||
|
a[ctr++] = 4 - t1;
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
DBENCH_STOP(*tsample);
|
||||||
|
return ctr;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*************************************************
|
||||||
|
* Name: poly_uniform_eta
|
||||||
|
*
|
||||||
|
* Description: Sample polynomial with uniformly random coefficients
|
||||||
|
* in [-ETA,ETA] by performing rejection sampling on the
|
||||||
|
* output stream from SHAKE256(seed|nonce)
|
||||||
|
*
|
||||||
|
* Arguments: - poly *a: pointer to output polynomial
|
||||||
|
* - const uint8_t seed[]: byte array with seed of length CRHBYTES
|
||||||
|
* - uint16_t nonce: 2-byte nonce
|
||||||
|
**************************************************/
|
||||||
|
#if ETA == 2
|
||||||
|
#define POLY_UNIFORM_ETA_NBLOCKS ((136 + STREAM256_BLOCKBYTES - 1)/STREAM256_BLOCKBYTES)
|
||||||
|
#elif ETA == 4
|
||||||
|
#define POLY_UNIFORM_ETA_NBLOCKS ((227 + STREAM256_BLOCKBYTES - 1)/STREAM256_BLOCKBYTES)
|
||||||
|
#endif
|
||||||
|
void poly_uniform_eta(poly *a,
|
||||||
|
const uint8_t seed[CRHBYTES],
|
||||||
|
uint16_t nonce)
|
||||||
|
{
|
||||||
|
unsigned int ctr;
|
||||||
|
unsigned int buflen = POLY_UNIFORM_ETA_NBLOCKS*STREAM256_BLOCKBYTES;
|
||||||
|
uint8_t buf[POLY_UNIFORM_ETA_NBLOCKS*STREAM256_BLOCKBYTES];
|
||||||
|
stream256_state state;
|
||||||
|
|
||||||
|
stream256_init(&state, seed, nonce);
|
||||||
|
stream256_squeezeblocks(buf, POLY_UNIFORM_ETA_NBLOCKS, &state);
|
||||||
|
|
||||||
|
ctr = rej_eta(a->coeffs, N, buf, buflen);
|
||||||
|
|
||||||
|
while(ctr < N) {
|
||||||
|
stream256_squeezeblocks(buf, 1, &state);
|
||||||
|
ctr += rej_eta(a->coeffs + ctr, N - ctr, buf, STREAM256_BLOCKBYTES);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/*************************************************
|
||||||
|
* Name: poly_uniform_gamma1m1
|
||||||
|
*
|
||||||
|
* Description: Sample polynomial with uniformly random coefficients
|
||||||
|
* in [-(GAMMA1 - 1), GAMMA1] by unpacking output stream
|
||||||
|
* of SHAKE256(seed|nonce)
|
||||||
|
*
|
||||||
|
* Arguments: - poly *a: pointer to output polynomial
|
||||||
|
* - const uint8_t seed[]: byte array with seed of length CRHBYTES
|
||||||
|
* - uint16_t nonce: 16-bit nonce
|
||||||
|
**************************************************/
|
||||||
|
#define POLY_UNIFORM_GAMMA1_NBLOCKS ((POLYZ_PACKEDBYTES + STREAM256_BLOCKBYTES - 1)/STREAM256_BLOCKBYTES)
|
||||||
|
void poly_uniform_gamma1(poly *a,
|
||||||
|
const uint8_t seed[CRHBYTES],
|
||||||
|
uint16_t nonce)
|
||||||
|
{
|
||||||
|
uint8_t buf[POLY_UNIFORM_GAMMA1_NBLOCKS*STREAM256_BLOCKBYTES];
|
||||||
|
stream256_state state;
|
||||||
|
|
||||||
|
stream256_init(&state, seed, nonce);
|
||||||
|
stream256_squeezeblocks(buf, POLY_UNIFORM_GAMMA1_NBLOCKS, &state);
|
||||||
|
polyz_unpack(a, buf);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*************************************************
|
||||||
|
* Name: challenge
|
||||||
|
*
|
||||||
|
* Description: Implementation of H. Samples polynomial with TAU nonzero
|
||||||
|
* coefficients in {-1,1} using the output stream of
|
||||||
|
* SHAKE256(seed).
|
||||||
|
*
|
||||||
|
* Arguments: - poly *c: pointer to output polynomial
|
||||||
|
* - const uint8_t mu[]: byte array containing seed of length CTILDEBYTES
|
||||||
|
**************************************************/
|
||||||
|
void poly_challenge(poly *c, const uint8_t seed[CTILDEBYTES]) {
|
||||||
|
unsigned int i, b, pos;
|
||||||
|
uint64_t signs;
|
||||||
|
uint8_t buf[SHAKE256_RATE];
|
||||||
|
keccak_state state;
|
||||||
|
|
||||||
|
shake256_init(&state);
|
||||||
|
shake256_absorb(&state, seed, CTILDEBYTES);
|
||||||
|
shake256_finalize(&state);
|
||||||
|
shake256_squeezeblocks(buf, 1, &state);
|
||||||
|
|
||||||
|
signs = 0;
|
||||||
|
for(i = 0; i < 8; ++i)
|
||||||
|
signs |= (uint64_t)buf[i] << 8*i;
|
||||||
|
pos = 8;
|
||||||
|
|
||||||
|
for(i = 0; i < N; ++i)
|
||||||
|
c->coeffs[i] = 0;
|
||||||
|
for(i = N-TAU; i < N; ++i) {
|
||||||
|
do {
|
||||||
|
if(pos >= SHAKE256_RATE) {
|
||||||
|
shake256_squeezeblocks(buf, 1, &state);
|
||||||
|
pos = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
b = buf[pos++];
|
||||||
|
} while(b > i);
|
||||||
|
|
||||||
|
c->coeffs[i] = c->coeffs[b];
|
||||||
|
c->coeffs[b] = 1 - 2*(signs & 1);
|
||||||
|
signs >>= 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/*************************************************
|
||||||
|
* Name: polyeta_pack
|
||||||
|
*
|
||||||
|
* Description: Bit-pack polynomial with coefficients in [-ETA,ETA].
|
||||||
|
*
|
||||||
|
* Arguments: - uint8_t *r: pointer to output byte array with at least
|
||||||
|
* POLYETA_PACKEDBYTES bytes
|
||||||
|
* - const poly *a: pointer to input polynomial
|
||||||
|
**************************************************/
|
||||||
|
void polyeta_pack(uint8_t *r, const poly *a) {
|
||||||
|
unsigned int i;
|
||||||
|
uint8_t t[8];
|
||||||
|
DBENCH_START();
|
||||||
|
|
||||||
|
#if ETA == 2
|
||||||
|
for(i = 0; i < N/8; ++i) {
|
||||||
|
t[0] = ETA - a->coeffs[8*i+0];
|
||||||
|
t[1] = ETA - a->coeffs[8*i+1];
|
||||||
|
t[2] = ETA - a->coeffs[8*i+2];
|
||||||
|
t[3] = ETA - a->coeffs[8*i+3];
|
||||||
|
t[4] = ETA - a->coeffs[8*i+4];
|
||||||
|
t[5] = ETA - a->coeffs[8*i+5];
|
||||||
|
t[6] = ETA - a->coeffs[8*i+6];
|
||||||
|
t[7] = ETA - a->coeffs[8*i+7];
|
||||||
|
|
||||||
|
r[3*i+0] = (t[0] >> 0) | (t[1] << 3) | (t[2] << 6);
|
||||||
|
r[3*i+1] = (t[2] >> 2) | (t[3] << 1) | (t[4] << 4) | (t[5] << 7);
|
||||||
|
r[3*i+2] = (t[5] >> 1) | (t[6] << 2) | (t[7] << 5);
|
||||||
|
}
|
||||||
|
#elif ETA == 4
|
||||||
|
for(i = 0; i < N/2; ++i) {
|
||||||
|
t[0] = ETA - a->coeffs[2*i+0];
|
||||||
|
t[1] = ETA - a->coeffs[2*i+1];
|
||||||
|
r[i] = t[0] | (t[1] << 4);
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
DBENCH_STOP(*tpack);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*************************************************
|
||||||
|
* Name: polyeta_unpack
|
||||||
|
*
|
||||||
|
* Description: Unpack polynomial with coefficients in [-ETA,ETA].
|
||||||
|
*
|
||||||
|
* Arguments: - poly *r: pointer to output polynomial
|
||||||
|
* - const uint8_t *a: byte array with bit-packed polynomial
|
||||||
|
**************************************************/
|
||||||
|
void polyeta_unpack(poly *r, const uint8_t *a) {
|
||||||
|
unsigned int i;
|
||||||
|
DBENCH_START();
|
||||||
|
|
||||||
|
#if ETA == 2
|
||||||
|
for(i = 0; i < N/8; ++i) {
|
||||||
|
r->coeffs[8*i+0] = (a[3*i+0] >> 0) & 7;
|
||||||
|
r->coeffs[8*i+1] = (a[3*i+0] >> 3) & 7;
|
||||||
|
r->coeffs[8*i+2] = ((a[3*i+0] >> 6) | (a[3*i+1] << 2)) & 7;
|
||||||
|
r->coeffs[8*i+3] = (a[3*i+1] >> 1) & 7;
|
||||||
|
r->coeffs[8*i+4] = (a[3*i+1] >> 4) & 7;
|
||||||
|
r->coeffs[8*i+5] = ((a[3*i+1] >> 7) | (a[3*i+2] << 1)) & 7;
|
||||||
|
r->coeffs[8*i+6] = (a[3*i+2] >> 2) & 7;
|
||||||
|
r->coeffs[8*i+7] = (a[3*i+2] >> 5) & 7;
|
||||||
|
|
||||||
|
r->coeffs[8*i+0] = ETA - r->coeffs[8*i+0];
|
||||||
|
r->coeffs[8*i+1] = ETA - r->coeffs[8*i+1];
|
||||||
|
r->coeffs[8*i+2] = ETA - r->coeffs[8*i+2];
|
||||||
|
r->coeffs[8*i+3] = ETA - r->coeffs[8*i+3];
|
||||||
|
r->coeffs[8*i+4] = ETA - r->coeffs[8*i+4];
|
||||||
|
r->coeffs[8*i+5] = ETA - r->coeffs[8*i+5];
|
||||||
|
r->coeffs[8*i+6] = ETA - r->coeffs[8*i+6];
|
||||||
|
r->coeffs[8*i+7] = ETA - r->coeffs[8*i+7];
|
||||||
|
}
|
||||||
|
#elif ETA == 4
|
||||||
|
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] = ETA - r->coeffs[2*i+0];
|
||||||
|
r->coeffs[2*i+1] = ETA - r->coeffs[2*i+1];
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
DBENCH_STOP(*tpack);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*************************************************
|
||||||
|
* Name: polyt1_pack
|
||||||
|
*
|
||||||
|
* Description: Bit-pack polynomial t1 with coefficients fitting in 10 bits.
|
||||||
|
* Input coefficients are assumed to be standard representatives.
|
||||||
|
*
|
||||||
|
* Arguments: - uint8_t *r: pointer to output byte array with at least
|
||||||
|
* POLYT1_PACKEDBYTES bytes
|
||||||
|
* - const poly *a: pointer to input polynomial
|
||||||
|
**************************************************/
|
||||||
|
void polyt1_pack(uint8_t *r, const poly *a) {
|
||||||
|
unsigned int i;
|
||||||
|
DBENCH_START();
|
||||||
|
|
||||||
|
for(i = 0; i < N/4; ++i) {
|
||||||
|
r[5*i+0] = (a->coeffs[4*i+0] >> 0);
|
||||||
|
r[5*i+1] = (a->coeffs[4*i+0] >> 8) | (a->coeffs[4*i+1] << 2);
|
||||||
|
r[5*i+2] = (a->coeffs[4*i+1] >> 6) | (a->coeffs[4*i+2] << 4);
|
||||||
|
r[5*i+3] = (a->coeffs[4*i+2] >> 4) | (a->coeffs[4*i+3] << 6);
|
||||||
|
r[5*i+4] = (a->coeffs[4*i+3] >> 2);
|
||||||
|
}
|
||||||
|
|
||||||
|
DBENCH_STOP(*tpack);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*************************************************
|
||||||
|
* Name: polyt1_unpack
|
||||||
|
*
|
||||||
|
* Description: Unpack polynomial t1 with 10-bit coefficients.
|
||||||
|
* Output coefficients are standard representatives.
|
||||||
|
*
|
||||||
|
* Arguments: - poly *r: pointer to output polynomial
|
||||||
|
* - const uint8_t *a: byte array with bit-packed polynomial
|
||||||
|
**************************************************/
|
||||||
|
void polyt1_unpack(poly *r, const uint8_t *a) {
|
||||||
|
unsigned int i;
|
||||||
|
DBENCH_START();
|
||||||
|
|
||||||
|
for(i = 0; i < N/4; ++i) {
|
||||||
|
r->coeffs[4*i+0] = ((a[5*i+0] >> 0) | ((uint32_t)a[5*i+1] << 8)) & 0x3FF;
|
||||||
|
r->coeffs[4*i+1] = ((a[5*i+1] >> 2) | ((uint32_t)a[5*i+2] << 6)) & 0x3FF;
|
||||||
|
r->coeffs[4*i+2] = ((a[5*i+2] >> 4) | ((uint32_t)a[5*i+3] << 4)) & 0x3FF;
|
||||||
|
r->coeffs[4*i+3] = ((a[5*i+3] >> 6) | ((uint32_t)a[5*i+4] << 2)) & 0x3FF;
|
||||||
|
}
|
||||||
|
|
||||||
|
DBENCH_STOP(*tpack);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*************************************************
|
||||||
|
* Name: polyt0_pack
|
||||||
|
*
|
||||||
|
* Description: Bit-pack polynomial t0 with coefficients in ]-2^{D-1}, 2^{D-1}].
|
||||||
|
*
|
||||||
|
* Arguments: - uint8_t *r: pointer to output byte array with at least
|
||||||
|
* POLYT0_PACKEDBYTES bytes
|
||||||
|
* - const poly *a: pointer to input polynomial
|
||||||
|
**************************************************/
|
||||||
|
void polyt0_pack(uint8_t *r, const poly *a) {
|
||||||
|
unsigned int i;
|
||||||
|
uint32_t t[8];
|
||||||
|
DBENCH_START();
|
||||||
|
|
||||||
|
for(i = 0; i < N/8; ++i) {
|
||||||
|
t[0] = (1 << (D-1)) - a->coeffs[8*i+0];
|
||||||
|
t[1] = (1 << (D-1)) - a->coeffs[8*i+1];
|
||||||
|
t[2] = (1 << (D-1)) - a->coeffs[8*i+2];
|
||||||
|
t[3] = (1 << (D-1)) - a->coeffs[8*i+3];
|
||||||
|
t[4] = (1 << (D-1)) - a->coeffs[8*i+4];
|
||||||
|
t[5] = (1 << (D-1)) - a->coeffs[8*i+5];
|
||||||
|
t[6] = (1 << (D-1)) - a->coeffs[8*i+6];
|
||||||
|
t[7] = (1 << (D-1)) - a->coeffs[8*i+7];
|
||||||
|
|
||||||
|
r[13*i+ 0] = t[0];
|
||||||
|
r[13*i+ 1] = t[0] >> 8;
|
||||||
|
r[13*i+ 1] |= t[1] << 5;
|
||||||
|
r[13*i+ 2] = t[1] >> 3;
|
||||||
|
r[13*i+ 3] = t[1] >> 11;
|
||||||
|
r[13*i+ 3] |= t[2] << 2;
|
||||||
|
r[13*i+ 4] = t[2] >> 6;
|
||||||
|
r[13*i+ 4] |= t[3] << 7;
|
||||||
|
r[13*i+ 5] = t[3] >> 1;
|
||||||
|
r[13*i+ 6] = t[3] >> 9;
|
||||||
|
r[13*i+ 6] |= t[4] << 4;
|
||||||
|
r[13*i+ 7] = t[4] >> 4;
|
||||||
|
r[13*i+ 8] = t[4] >> 12;
|
||||||
|
r[13*i+ 8] |= t[5] << 1;
|
||||||
|
r[13*i+ 9] = t[5] >> 7;
|
||||||
|
r[13*i+ 9] |= t[6] << 6;
|
||||||
|
r[13*i+10] = t[6] >> 2;
|
||||||
|
r[13*i+11] = t[6] >> 10;
|
||||||
|
r[13*i+11] |= t[7] << 3;
|
||||||
|
r[13*i+12] = t[7] >> 5;
|
||||||
|
}
|
||||||
|
|
||||||
|
DBENCH_STOP(*tpack);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*************************************************
|
||||||
|
* Name: polyt0_unpack
|
||||||
|
*
|
||||||
|
* Description: Unpack polynomial t0 with coefficients in ]-2^{D-1}, 2^{D-1}].
|
||||||
|
*
|
||||||
|
* Arguments: - poly *r: pointer to output polynomial
|
||||||
|
* - const uint8_t *a: byte array with bit-packed polynomial
|
||||||
|
**************************************************/
|
||||||
|
void polyt0_unpack(poly *r, const uint8_t *a) {
|
||||||
|
unsigned int i;
|
||||||
|
DBENCH_START();
|
||||||
|
|
||||||
|
for(i = 0; i < N/8; ++i) {
|
||||||
|
r->coeffs[8*i+0] = a[13*i+0];
|
||||||
|
r->coeffs[8*i+0] |= (uint32_t)a[13*i+1] << 8;
|
||||||
|
r->coeffs[8*i+0] &= 0x1FFF;
|
||||||
|
|
||||||
|
r->coeffs[8*i+1] = a[13*i+1] >> 5;
|
||||||
|
r->coeffs[8*i+1] |= (uint32_t)a[13*i+2] << 3;
|
||||||
|
r->coeffs[8*i+1] |= (uint32_t)a[13*i+3] << 11;
|
||||||
|
r->coeffs[8*i+1] &= 0x1FFF;
|
||||||
|
|
||||||
|
r->coeffs[8*i+2] = a[13*i+3] >> 2;
|
||||||
|
r->coeffs[8*i+2] |= (uint32_t)a[13*i+4] << 6;
|
||||||
|
r->coeffs[8*i+2] &= 0x1FFF;
|
||||||
|
|
||||||
|
r->coeffs[8*i+3] = a[13*i+4] >> 7;
|
||||||
|
r->coeffs[8*i+3] |= (uint32_t)a[13*i+5] << 1;
|
||||||
|
r->coeffs[8*i+3] |= (uint32_t)a[13*i+6] << 9;
|
||||||
|
r->coeffs[8*i+3] &= 0x1FFF;
|
||||||
|
|
||||||
|
r->coeffs[8*i+4] = a[13*i+6] >> 4;
|
||||||
|
r->coeffs[8*i+4] |= (uint32_t)a[13*i+7] << 4;
|
||||||
|
r->coeffs[8*i+4] |= (uint32_t)a[13*i+8] << 12;
|
||||||
|
r->coeffs[8*i+4] &= 0x1FFF;
|
||||||
|
|
||||||
|
r->coeffs[8*i+5] = a[13*i+8] >> 1;
|
||||||
|
r->coeffs[8*i+5] |= (uint32_t)a[13*i+9] << 7;
|
||||||
|
r->coeffs[8*i+5] &= 0x1FFF;
|
||||||
|
|
||||||
|
r->coeffs[8*i+6] = a[13*i+9] >> 6;
|
||||||
|
r->coeffs[8*i+6] |= (uint32_t)a[13*i+10] << 2;
|
||||||
|
r->coeffs[8*i+6] |= (uint32_t)a[13*i+11] << 10;
|
||||||
|
r->coeffs[8*i+6] &= 0x1FFF;
|
||||||
|
|
||||||
|
r->coeffs[8*i+7] = a[13*i+11] >> 3;
|
||||||
|
r->coeffs[8*i+7] |= (uint32_t)a[13*i+12] << 5;
|
||||||
|
r->coeffs[8*i+7] &= 0x1FFF;
|
||||||
|
|
||||||
|
r->coeffs[8*i+0] = (1 << (D-1)) - r->coeffs[8*i+0];
|
||||||
|
r->coeffs[8*i+1] = (1 << (D-1)) - r->coeffs[8*i+1];
|
||||||
|
r->coeffs[8*i+2] = (1 << (D-1)) - r->coeffs[8*i+2];
|
||||||
|
r->coeffs[8*i+3] = (1 << (D-1)) - r->coeffs[8*i+3];
|
||||||
|
r->coeffs[8*i+4] = (1 << (D-1)) - r->coeffs[8*i+4];
|
||||||
|
r->coeffs[8*i+5] = (1 << (D-1)) - r->coeffs[8*i+5];
|
||||||
|
r->coeffs[8*i+6] = (1 << (D-1)) - r->coeffs[8*i+6];
|
||||||
|
r->coeffs[8*i+7] = (1 << (D-1)) - r->coeffs[8*i+7];
|
||||||
|
}
|
||||||
|
|
||||||
|
DBENCH_STOP(*tpack);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*************************************************
|
||||||
|
* Name: polyz_pack
|
||||||
|
*
|
||||||
|
* Description: Bit-pack polynomial with coefficients
|
||||||
|
* in [-(GAMMA1 - 1), GAMMA1].
|
||||||
|
*
|
||||||
|
* Arguments: - uint8_t *r: pointer to output byte array with at least
|
||||||
|
* POLYZ_PACKEDBYTES bytes
|
||||||
|
* - const poly *a: pointer to input polynomial
|
||||||
|
**************************************************/
|
||||||
|
void polyz_pack(uint8_t *r, const poly *a) {
|
||||||
|
unsigned int i;
|
||||||
|
uint32_t t[4];
|
||||||
|
DBENCH_START();
|
||||||
|
|
||||||
|
#if GAMMA1 == (1 << 17)
|
||||||
|
for(i = 0; i < N/4; ++i) {
|
||||||
|
t[0] = GAMMA1 - a->coeffs[4*i+0];
|
||||||
|
t[1] = GAMMA1 - a->coeffs[4*i+1];
|
||||||
|
t[2] = GAMMA1 - a->coeffs[4*i+2];
|
||||||
|
t[3] = GAMMA1 - a->coeffs[4*i+3];
|
||||||
|
|
||||||
|
r[9*i+0] = t[0];
|
||||||
|
r[9*i+1] = t[0] >> 8;
|
||||||
|
r[9*i+2] = t[0] >> 16;
|
||||||
|
r[9*i+2] |= t[1] << 2;
|
||||||
|
r[9*i+3] = t[1] >> 6;
|
||||||
|
r[9*i+4] = t[1] >> 14;
|
||||||
|
r[9*i+4] |= t[2] << 4;
|
||||||
|
r[9*i+5] = t[2] >> 4;
|
||||||
|
r[9*i+6] = t[2] >> 12;
|
||||||
|
r[9*i+6] |= t[3] << 6;
|
||||||
|
r[9*i+7] = t[3] >> 2;
|
||||||
|
r[9*i+8] = t[3] >> 10;
|
||||||
|
}
|
||||||
|
#elif GAMMA1 == (1 << 19)
|
||||||
|
for(i = 0; i < N/2; ++i) {
|
||||||
|
t[0] = GAMMA1 - a->coeffs[2*i+0];
|
||||||
|
t[1] = GAMMA1 - a->coeffs[2*i+1];
|
||||||
|
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
DBENCH_STOP(*tpack);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*************************************************
|
||||||
|
* Name: polyz_unpack
|
||||||
|
*
|
||||||
|
* Description: Unpack polynomial z with coefficients
|
||||||
|
* in [-(GAMMA1 - 1), GAMMA1].
|
||||||
|
*
|
||||||
|
* Arguments: - poly *r: pointer to output polynomial
|
||||||
|
* - const uint8_t *a: byte array with bit-packed polynomial
|
||||||
|
**************************************************/
|
||||||
|
void polyz_unpack(poly *r, const uint8_t *a) {
|
||||||
|
unsigned int i;
|
||||||
|
DBENCH_START();
|
||||||
|
|
||||||
|
#if GAMMA1 == (1 << 17)
|
||||||
|
for(i = 0; i < N/4; ++i) {
|
||||||
|
r->coeffs[4*i+0] = a[9*i+0];
|
||||||
|
r->coeffs[4*i+0] |= (uint32_t)a[9*i+1] << 8;
|
||||||
|
r->coeffs[4*i+0] |= (uint32_t)a[9*i+2] << 16;
|
||||||
|
r->coeffs[4*i+0] &= 0x3FFFF;
|
||||||
|
|
||||||
|
r->coeffs[4*i+1] = a[9*i+2] >> 2;
|
||||||
|
r->coeffs[4*i+1] |= (uint32_t)a[9*i+3] << 6;
|
||||||
|
r->coeffs[4*i+1] |= (uint32_t)a[9*i+4] << 14;
|
||||||
|
r->coeffs[4*i+1] &= 0x3FFFF;
|
||||||
|
|
||||||
|
r->coeffs[4*i+2] = a[9*i+4] >> 4;
|
||||||
|
r->coeffs[4*i+2] |= (uint32_t)a[9*i+5] << 4;
|
||||||
|
r->coeffs[4*i+2] |= (uint32_t)a[9*i+6] << 12;
|
||||||
|
r->coeffs[4*i+2] &= 0x3FFFF;
|
||||||
|
|
||||||
|
r->coeffs[4*i+3] = a[9*i+6] >> 6;
|
||||||
|
r->coeffs[4*i+3] |= (uint32_t)a[9*i+7] << 2;
|
||||||
|
r->coeffs[4*i+3] |= (uint32_t)a[9*i+8] << 10;
|
||||||
|
r->coeffs[4*i+3] &= 0x3FFFF;
|
||||||
|
|
||||||
|
r->coeffs[4*i+0] = GAMMA1 - r->coeffs[4*i+0];
|
||||||
|
r->coeffs[4*i+1] = GAMMA1 - r->coeffs[4*i+1];
|
||||||
|
r->coeffs[4*i+2] = GAMMA1 - r->coeffs[4*i+2];
|
||||||
|
r->coeffs[4*i+3] = GAMMA1 - r->coeffs[4*i+3];
|
||||||
|
}
|
||||||
|
#elif GAMMA1 == (1 << 19)
|
||||||
|
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] << 16;
|
||||||
|
r->coeffs[2*i+0] &= 0xFFFFF;
|
||||||
|
|
||||||
|
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+1] &= 0xFFFFF; */ /* No effect, since we're anyway at 20 bits */
|
||||||
|
|
||||||
|
r->coeffs[2*i+0] = GAMMA1 - r->coeffs[2*i+0];
|
||||||
|
r->coeffs[2*i+1] = GAMMA1 - r->coeffs[2*i+1];
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
DBENCH_STOP(*tpack);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*************************************************
|
||||||
|
* Name: polyw1_pack
|
||||||
|
*
|
||||||
|
* Description: Bit-pack polynomial w1 with coefficients in [0,15] or [0,43].
|
||||||
|
* Input coefficients are assumed to be standard representatives.
|
||||||
|
*
|
||||||
|
* Arguments: - uint8_t *r: pointer to output byte array with at least
|
||||||
|
* POLYW1_PACKEDBYTES bytes
|
||||||
|
* - const poly *a: pointer to input polynomial
|
||||||
|
**************************************************/
|
||||||
|
void polyw1_pack(uint8_t *r, const poly *a) {
|
||||||
|
unsigned int i;
|
||||||
|
DBENCH_START();
|
||||||
|
|
||||||
|
#if GAMMA2 == (Q-1)/88
|
||||||
|
for(i = 0; i < N/4; ++i) {
|
||||||
|
r[3*i+0] = a->coeffs[4*i+0];
|
||||||
|
r[3*i+0] |= a->coeffs[4*i+1] << 6;
|
||||||
|
r[3*i+1] = a->coeffs[4*i+1] >> 2;
|
||||||
|
r[3*i+1] |= a->coeffs[4*i+2] << 4;
|
||||||
|
r[3*i+2] = a->coeffs[4*i+2] >> 4;
|
||||||
|
r[3*i+2] |= a->coeffs[4*i+3] << 2;
|
||||||
|
}
|
||||||
|
#elif GAMMA2 == (Q-1)/32
|
||||||
|
for(i = 0; i < N/2; ++i)
|
||||||
|
r[i] = a->coeffs[2*i+0] | (a->coeffs[2*i+1] << 4);
|
||||||
|
#endif
|
||||||
|
|
||||||
|
DBENCH_STOP(*tpack);
|
||||||
|
}
|
||||||
79
app/src/main/cpp/pq/mldsa65/poly.h
Normal file
79
app/src/main/cpp/pq/mldsa65/poly.h
Normal file
|
|
@ -0,0 +1,79 @@
|
||||||
|
#ifndef POLY_H
|
||||||
|
#define POLY_H
|
||||||
|
|
||||||
|
#include <stdint.h>
|
||||||
|
#include "params.h"
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
int32_t coeffs[N];
|
||||||
|
} poly;
|
||||||
|
|
||||||
|
#define poly_reduce DILITHIUM_NAMESPACE(poly_reduce)
|
||||||
|
void poly_reduce(poly *a);
|
||||||
|
#define poly_caddq DILITHIUM_NAMESPACE(poly_caddq)
|
||||||
|
void poly_caddq(poly *a);
|
||||||
|
|
||||||
|
#define poly_add DILITHIUM_NAMESPACE(poly_add)
|
||||||
|
void poly_add(poly *c, const poly *a, const poly *b);
|
||||||
|
#define poly_sub DILITHIUM_NAMESPACE(poly_sub)
|
||||||
|
void poly_sub(poly *c, const poly *a, const poly *b);
|
||||||
|
#define poly_shiftl DILITHIUM_NAMESPACE(poly_shiftl)
|
||||||
|
void poly_shiftl(poly *a);
|
||||||
|
|
||||||
|
#define poly_ntt DILITHIUM_NAMESPACE(poly_ntt)
|
||||||
|
void poly_ntt(poly *a);
|
||||||
|
#define poly_invntt_tomont DILITHIUM_NAMESPACE(poly_invntt_tomont)
|
||||||
|
void poly_invntt_tomont(poly *a);
|
||||||
|
#define poly_pointwise_montgomery DILITHIUM_NAMESPACE(poly_pointwise_montgomery)
|
||||||
|
void poly_pointwise_montgomery(poly *c, const poly *a, const poly *b);
|
||||||
|
|
||||||
|
#define poly_power2round DILITHIUM_NAMESPACE(poly_power2round)
|
||||||
|
void poly_power2round(poly *a1, poly *a0, const poly *a);
|
||||||
|
#define poly_decompose DILITHIUM_NAMESPACE(poly_decompose)
|
||||||
|
void poly_decompose(poly *a1, poly *a0, const poly *a);
|
||||||
|
#define poly_make_hint DILITHIUM_NAMESPACE(poly_make_hint)
|
||||||
|
unsigned int poly_make_hint(poly *h, const poly *a0, const poly *a1);
|
||||||
|
#define poly_use_hint DILITHIUM_NAMESPACE(poly_use_hint)
|
||||||
|
void poly_use_hint(poly *b, const poly *a, const poly *h);
|
||||||
|
|
||||||
|
#define poly_chknorm DILITHIUM_NAMESPACE(poly_chknorm)
|
||||||
|
int poly_chknorm(const poly *a, int32_t B);
|
||||||
|
#define poly_uniform DILITHIUM_NAMESPACE(poly_uniform)
|
||||||
|
void poly_uniform(poly *a,
|
||||||
|
const uint8_t seed[SEEDBYTES],
|
||||||
|
uint16_t nonce);
|
||||||
|
#define poly_uniform_eta DILITHIUM_NAMESPACE(poly_uniform_eta)
|
||||||
|
void poly_uniform_eta(poly *a,
|
||||||
|
const uint8_t seed[CRHBYTES],
|
||||||
|
uint16_t nonce);
|
||||||
|
#define poly_uniform_gamma1 DILITHIUM_NAMESPACE(poly_uniform_gamma1)
|
||||||
|
void poly_uniform_gamma1(poly *a,
|
||||||
|
const uint8_t seed[CRHBYTES],
|
||||||
|
uint16_t nonce);
|
||||||
|
#define poly_challenge DILITHIUM_NAMESPACE(poly_challenge)
|
||||||
|
void poly_challenge(poly *c, const uint8_t seed[CTILDEBYTES]);
|
||||||
|
|
||||||
|
#define polyeta_pack DILITHIUM_NAMESPACE(polyeta_pack)
|
||||||
|
void polyeta_pack(uint8_t *r, const poly *a);
|
||||||
|
#define polyeta_unpack DILITHIUM_NAMESPACE(polyeta_unpack)
|
||||||
|
void polyeta_unpack(poly *r, const uint8_t *a);
|
||||||
|
|
||||||
|
#define polyt1_pack DILITHIUM_NAMESPACE(polyt1_pack)
|
||||||
|
void polyt1_pack(uint8_t *r, const poly *a);
|
||||||
|
#define polyt1_unpack DILITHIUM_NAMESPACE(polyt1_unpack)
|
||||||
|
void polyt1_unpack(poly *r, const uint8_t *a);
|
||||||
|
|
||||||
|
#define polyt0_pack DILITHIUM_NAMESPACE(polyt0_pack)
|
||||||
|
void polyt0_pack(uint8_t *r, const poly *a);
|
||||||
|
#define polyt0_unpack DILITHIUM_NAMESPACE(polyt0_unpack)
|
||||||
|
void polyt0_unpack(poly *r, const uint8_t *a);
|
||||||
|
|
||||||
|
#define polyz_pack DILITHIUM_NAMESPACE(polyz_pack)
|
||||||
|
void polyz_pack(uint8_t *r, const poly *a);
|
||||||
|
#define polyz_unpack DILITHIUM_NAMESPACE(polyz_unpack)
|
||||||
|
void polyz_unpack(poly *r, const uint8_t *a);
|
||||||
|
|
||||||
|
#define polyw1_pack DILITHIUM_NAMESPACE(polyw1_pack)
|
||||||
|
void polyw1_pack(uint8_t *r, const poly *a);
|
||||||
|
|
||||||
|
#endif
|
||||||
389
app/src/main/cpp/pq/mldsa65/polyvec.c
Normal file
389
app/src/main/cpp/pq/mldsa65/polyvec.c
Normal file
|
|
@ -0,0 +1,389 @@
|
||||||
|
#include <stdint.h>
|
||||||
|
#include "params.h"
|
||||||
|
#include "polyvec.h"
|
||||||
|
#include "poly.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|j|i)
|
||||||
|
*
|
||||||
|
* Arguments: - polyvecl mat[K]: output matrix
|
||||||
|
* - const uint8_t rho[]: byte array containing seed rho
|
||||||
|
**************************************************/
|
||||||
|
void polyvec_matrix_expand(polyvecl mat[K], const uint8_t rho[SEEDBYTES]) {
|
||||||
|
unsigned int i, j;
|
||||||
|
|
||||||
|
for(i = 0; i < K; ++i)
|
||||||
|
for(j = 0; j < L; ++j)
|
||||||
|
poly_uniform(&mat[i].vec[j], rho, (i << 8) + j);
|
||||||
|
}
|
||||||
|
|
||||||
|
void polyvec_matrix_pointwise_montgomery(polyveck *t, const polyvecl mat[K], const polyvecl *v) {
|
||||||
|
unsigned int i;
|
||||||
|
|
||||||
|
for(i = 0; i < K; ++i)
|
||||||
|
polyvecl_pointwise_acc_montgomery(&t->vec[i], &mat[i], v);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**************************************************************/
|
||||||
|
/************ Vectors of polynomials of length L **************/
|
||||||
|
/**************************************************************/
|
||||||
|
|
||||||
|
void polyvecl_uniform_eta(polyvecl *v, const uint8_t seed[CRHBYTES], uint16_t nonce) {
|
||||||
|
unsigned int i;
|
||||||
|
|
||||||
|
for(i = 0; i < L; ++i)
|
||||||
|
poly_uniform_eta(&v->vec[i], seed, nonce++);
|
||||||
|
}
|
||||||
|
|
||||||
|
void polyvecl_uniform_gamma1(polyvecl *v, const uint8_t seed[CRHBYTES], uint16_t nonce) {
|
||||||
|
unsigned int i;
|
||||||
|
|
||||||
|
for(i = 0; i < L; ++i)
|
||||||
|
poly_uniform_gamma1(&v->vec[i], seed, L*nonce + i);
|
||||||
|
}
|
||||||
|
|
||||||
|
void polyvecl_reduce(polyvecl *v) {
|
||||||
|
unsigned int i;
|
||||||
|
|
||||||
|
for(i = 0; i < L; ++i)
|
||||||
|
poly_reduce(&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 polyvecl_add(polyvecl *w, const polyvecl *u, const polyvecl *v) {
|
||||||
|
unsigned int i;
|
||||||
|
|
||||||
|
for(i = 0; i < L; ++i)
|
||||||
|
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 polyvecl_ntt(polyvecl *v) {
|
||||||
|
unsigned int i;
|
||||||
|
|
||||||
|
for(i = 0; i < L; ++i)
|
||||||
|
poly_ntt(&v->vec[i]);
|
||||||
|
}
|
||||||
|
|
||||||
|
void polyvecl_invntt_tomont(polyvecl *v) {
|
||||||
|
unsigned int i;
|
||||||
|
|
||||||
|
for(i = 0; i < L; ++i)
|
||||||
|
poly_invntt_tomont(&v->vec[i]);
|
||||||
|
}
|
||||||
|
|
||||||
|
void polyvecl_pointwise_poly_montgomery(polyvecl *r, const poly *a, const polyvecl *v) {
|
||||||
|
unsigned int i;
|
||||||
|
|
||||||
|
for(i = 0; i < L; ++i)
|
||||||
|
poly_pointwise_montgomery(&r->vec[i], a, &v->vec[i]);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*************************************************
|
||||||
|
* Name: polyvecl_pointwise_acc_montgomery
|
||||||
|
*
|
||||||
|
* 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.
|
||||||
|
*
|
||||||
|
* Arguments: - poly *w: output polynomial
|
||||||
|
* - const polyvecl *u: pointer to first input vector
|
||||||
|
* - const polyvecl *v: pointer to second input vector
|
||||||
|
**************************************************/
|
||||||
|
void polyvecl_pointwise_acc_montgomery(poly *w,
|
||||||
|
const polyvecl *u,
|
||||||
|
const polyvecl *v)
|
||||||
|
{
|
||||||
|
unsigned int i;
|
||||||
|
poly t;
|
||||||
|
|
||||||
|
poly_pointwise_montgomery(w, &u->vec[0], &v->vec[0]);
|
||||||
|
for(i = 1; i < L; ++i) {
|
||||||
|
poly_pointwise_montgomery(&t, &u->vec[i], &v->vec[i]);
|
||||||
|
poly_add(w, w, &t);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/*************************************************
|
||||||
|
* Name: polyvecl_chknorm
|
||||||
|
*
|
||||||
|
* Description: Check infinity norm of polynomials in vector of length L.
|
||||||
|
* Assumes input polyvecl to be reduced by polyvecl_reduce().
|
||||||
|
*
|
||||||
|
* Arguments: - const polyvecl *v: pointer to vector
|
||||||
|
* - int32_t B: norm bound
|
||||||
|
*
|
||||||
|
* Returns 0 if norm of all polynomials is strictly smaller than B <= (Q-1)/8
|
||||||
|
* and 1 otherwise.
|
||||||
|
**************************************************/
|
||||||
|
int polyvecl_chknorm(const polyvecl *v, int32_t bound) {
|
||||||
|
unsigned int i;
|
||||||
|
|
||||||
|
for(i = 0; i < L; ++i)
|
||||||
|
if(poly_chknorm(&v->vec[i], bound))
|
||||||
|
return 1;
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**************************************************************/
|
||||||
|
/************ Vectors of polynomials of length K **************/
|
||||||
|
/**************************************************************/
|
||||||
|
|
||||||
|
void polyveck_uniform_eta(polyveck *v, const uint8_t seed[CRHBYTES], uint16_t nonce) {
|
||||||
|
unsigned int i;
|
||||||
|
|
||||||
|
for(i = 0; i < K; ++i)
|
||||||
|
poly_uniform_eta(&v->vec[i], seed, nonce++);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*************************************************
|
||||||
|
* Name: polyveck_reduce
|
||||||
|
*
|
||||||
|
* Description: Reduce coefficients of polynomials in vector of length K
|
||||||
|
* to representatives in [-6283008,6283008].
|
||||||
|
*
|
||||||
|
* Arguments: - polyveck *v: pointer to input/output vector
|
||||||
|
**************************************************/
|
||||||
|
void polyveck_reduce(polyveck *v) {
|
||||||
|
unsigned int i;
|
||||||
|
|
||||||
|
for(i = 0; i < K; ++i)
|
||||||
|
poly_reduce(&v->vec[i]);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*************************************************
|
||||||
|
* Name: polyveck_caddq
|
||||||
|
*
|
||||||
|
* Description: For all coefficients of polynomials in vector of length K
|
||||||
|
* add Q if coefficient is negative.
|
||||||
|
*
|
||||||
|
* Arguments: - polyveck *v: pointer to input/output vector
|
||||||
|
**************************************************/
|
||||||
|
void polyveck_caddq(polyveck *v) {
|
||||||
|
unsigned int i;
|
||||||
|
|
||||||
|
for(i = 0; i < K; ++i)
|
||||||
|
poly_caddq(&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 polyveck_add(polyveck *w, const polyveck *u, const polyveck *v) {
|
||||||
|
unsigned int i;
|
||||||
|
|
||||||
|
for(i = 0; i < K; ++i)
|
||||||
|
poly_add(&w->vec[i], &u->vec[i], &v->vec[i]);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*************************************************
|
||||||
|
* Name: polyveck_sub
|
||||||
|
*
|
||||||
|
* Description: Subtract vectors of polynomials of length K.
|
||||||
|
* 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 polyveck_sub(polyveck *w, const polyveck *u, const polyveck *v) {
|
||||||
|
unsigned int i;
|
||||||
|
|
||||||
|
for(i = 0; i < K; ++i)
|
||||||
|
poly_sub(&w->vec[i], &u->vec[i], &v->vec[i]);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*************************************************
|
||||||
|
* Name: polyveck_shiftl
|
||||||
|
*
|
||||||
|
* Description: Multiply vector of polynomials of Length K by 2^D without modular
|
||||||
|
* reduction. Assumes input coefficients to be less than 2^{31-D}.
|
||||||
|
*
|
||||||
|
* Arguments: - polyveck *v: pointer to input/output vector
|
||||||
|
**************************************************/
|
||||||
|
void polyveck_shiftl(polyveck *v) {
|
||||||
|
unsigned int i;
|
||||||
|
|
||||||
|
for(i = 0; i < K; ++i)
|
||||||
|
poly_shiftl(&v->vec[i]);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*************************************************
|
||||||
|
* 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 polyveck_ntt(polyveck *v) {
|
||||||
|
unsigned int i;
|
||||||
|
|
||||||
|
for(i = 0; i < K; ++i)
|
||||||
|
poly_ntt(&v->vec[i]);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*************************************************
|
||||||
|
* Name: polyveck_invntt_tomont
|
||||||
|
*
|
||||||
|
* 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 polyveck_invntt_tomont(polyveck *v) {
|
||||||
|
unsigned int i;
|
||||||
|
|
||||||
|
for(i = 0; i < K; ++i)
|
||||||
|
poly_invntt_tomont(&v->vec[i]);
|
||||||
|
}
|
||||||
|
|
||||||
|
void polyveck_pointwise_poly_montgomery(polyveck *r, const poly *a, const polyveck *v) {
|
||||||
|
unsigned int i;
|
||||||
|
|
||||||
|
for(i = 0; i < K; ++i)
|
||||||
|
poly_pointwise_montgomery(&r->vec[i], a, &v->vec[i]);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/*************************************************
|
||||||
|
* Name: polyveck_chknorm
|
||||||
|
*
|
||||||
|
* Description: Check infinity norm of polynomials in vector of length K.
|
||||||
|
* Assumes input polyveck to be reduced by polyveck_reduce().
|
||||||
|
*
|
||||||
|
* Arguments: - const polyveck *v: pointer to vector
|
||||||
|
* - int32_t B: norm bound
|
||||||
|
*
|
||||||
|
* Returns 0 if norm of all polynomials are strictly smaller than B <= (Q-1)/8
|
||||||
|
* and 1 otherwise.
|
||||||
|
**************************************************/
|
||||||
|
int polyveck_chknorm(const polyveck *v, int32_t bound) {
|
||||||
|
unsigned int i;
|
||||||
|
|
||||||
|
for(i = 0; i < K; ++i)
|
||||||
|
if(poly_chknorm(&v->vec[i], bound))
|
||||||
|
return 1;
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*************************************************
|
||||||
|
* 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 a0
|
||||||
|
* - const polyveck *v: pointer to input vector
|
||||||
|
**************************************************/
|
||||||
|
void polyveck_power2round(polyveck *v1, polyveck *v0, const polyveck *v) {
|
||||||
|
unsigned int i;
|
||||||
|
|
||||||
|
for(i = 0; i < K; ++i)
|
||||||
|
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 a0
|
||||||
|
* - const polyveck *v: pointer to input vector
|
||||||
|
**************************************************/
|
||||||
|
void polyveck_decompose(polyveck *v1, polyveck *v0, const polyveck *v) {
|
||||||
|
unsigned int i;
|
||||||
|
|
||||||
|
for(i = 0; i < K; ++i)
|
||||||
|
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 *v0: pointer to low part of input vector
|
||||||
|
* - const polyveck *v1: pointer to high part of input vector
|
||||||
|
*
|
||||||
|
* Returns number of 1 bits.
|
||||||
|
**************************************************/
|
||||||
|
unsigned int polyveck_make_hint(polyveck *h,
|
||||||
|
const polyveck *v0,
|
||||||
|
const polyveck *v1)
|
||||||
|
{
|
||||||
|
unsigned int i, s = 0;
|
||||||
|
|
||||||
|
for(i = 0; i < K; ++i)
|
||||||
|
s += poly_make_hint(&h->vec[i], &v0->vec[i], &v1->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 polyveck_use_hint(polyveck *w, const polyveck *u, const polyveck *h) {
|
||||||
|
unsigned int i;
|
||||||
|
|
||||||
|
for(i = 0; i < K; ++i)
|
||||||
|
poly_use_hint(&w->vec[i], &u->vec[i], &h->vec[i]);
|
||||||
|
}
|
||||||
|
|
||||||
|
void polyveck_pack_w1(uint8_t r[K*POLYW1_PACKEDBYTES], const polyveck *w1) {
|
||||||
|
unsigned int i;
|
||||||
|
|
||||||
|
for(i = 0; i < K; ++i)
|
||||||
|
polyw1_pack(&r[i*POLYW1_PACKEDBYTES], &w1->vec[i]);
|
||||||
|
}
|
||||||
93
app/src/main/cpp/pq/mldsa65/polyvec.h
Normal file
93
app/src/main/cpp/pq/mldsa65/polyvec.h
Normal file
|
|
@ -0,0 +1,93 @@
|
||||||
|
#ifndef POLYVEC_H
|
||||||
|
#define POLYVEC_H
|
||||||
|
|
||||||
|
#include <stdint.h>
|
||||||
|
#include "params.h"
|
||||||
|
#include "poly.h"
|
||||||
|
|
||||||
|
/* Vectors of polynomials of length L */
|
||||||
|
typedef struct {
|
||||||
|
poly vec[L];
|
||||||
|
} polyvecl;
|
||||||
|
|
||||||
|
#define polyvecl_uniform_eta DILITHIUM_NAMESPACE(polyvecl_uniform_eta)
|
||||||
|
void polyvecl_uniform_eta(polyvecl *v, const uint8_t seed[CRHBYTES], uint16_t nonce);
|
||||||
|
|
||||||
|
#define polyvecl_uniform_gamma1 DILITHIUM_NAMESPACE(polyvecl_uniform_gamma1)
|
||||||
|
void polyvecl_uniform_gamma1(polyvecl *v, const uint8_t seed[CRHBYTES], uint16_t nonce);
|
||||||
|
|
||||||
|
#define polyvecl_reduce DILITHIUM_NAMESPACE(polyvecl_reduce)
|
||||||
|
void polyvecl_reduce(polyvecl *v);
|
||||||
|
|
||||||
|
#define polyvecl_add DILITHIUM_NAMESPACE(polyvecl_add)
|
||||||
|
void polyvecl_add(polyvecl *w, const polyvecl *u, const polyvecl *v);
|
||||||
|
|
||||||
|
#define polyvecl_ntt DILITHIUM_NAMESPACE(polyvecl_ntt)
|
||||||
|
void polyvecl_ntt(polyvecl *v);
|
||||||
|
#define polyvecl_invntt_tomont DILITHIUM_NAMESPACE(polyvecl_invntt_tomont)
|
||||||
|
void polyvecl_invntt_tomont(polyvecl *v);
|
||||||
|
#define polyvecl_pointwise_poly_montgomery DILITHIUM_NAMESPACE(polyvecl_pointwise_poly_montgomery)
|
||||||
|
void polyvecl_pointwise_poly_montgomery(polyvecl *r, const poly *a, const polyvecl *v);
|
||||||
|
#define polyvecl_pointwise_acc_montgomery \
|
||||||
|
DILITHIUM_NAMESPACE(polyvecl_pointwise_acc_montgomery)
|
||||||
|
void polyvecl_pointwise_acc_montgomery(poly *w,
|
||||||
|
const polyvecl *u,
|
||||||
|
const polyvecl *v);
|
||||||
|
|
||||||
|
|
||||||
|
#define polyvecl_chknorm DILITHIUM_NAMESPACE(polyvecl_chknorm)
|
||||||
|
int polyvecl_chknorm(const polyvecl *v, int32_t B);
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/* Vectors of polynomials of length K */
|
||||||
|
typedef struct {
|
||||||
|
poly vec[K];
|
||||||
|
} polyveck;
|
||||||
|
|
||||||
|
#define polyveck_uniform_eta DILITHIUM_NAMESPACE(polyveck_uniform_eta)
|
||||||
|
void polyveck_uniform_eta(polyveck *v, const uint8_t seed[CRHBYTES], uint16_t nonce);
|
||||||
|
|
||||||
|
#define polyveck_reduce DILITHIUM_NAMESPACE(polyveck_reduce)
|
||||||
|
void polyveck_reduce(polyveck *v);
|
||||||
|
#define polyveck_caddq DILITHIUM_NAMESPACE(polyveck_caddq)
|
||||||
|
void polyveck_caddq(polyveck *v);
|
||||||
|
|
||||||
|
#define polyveck_add DILITHIUM_NAMESPACE(polyveck_add)
|
||||||
|
void polyveck_add(polyveck *w, const polyveck *u, const polyveck *v);
|
||||||
|
#define polyveck_sub DILITHIUM_NAMESPACE(polyveck_sub)
|
||||||
|
void polyveck_sub(polyveck *w, const polyveck *u, const polyveck *v);
|
||||||
|
#define polyveck_shiftl DILITHIUM_NAMESPACE(polyveck_shiftl)
|
||||||
|
void polyveck_shiftl(polyveck *v);
|
||||||
|
|
||||||
|
#define polyveck_ntt DILITHIUM_NAMESPACE(polyveck_ntt)
|
||||||
|
void polyveck_ntt(polyveck *v);
|
||||||
|
#define polyveck_invntt_tomont DILITHIUM_NAMESPACE(polyveck_invntt_tomont)
|
||||||
|
void polyveck_invntt_tomont(polyveck *v);
|
||||||
|
#define polyveck_pointwise_poly_montgomery DILITHIUM_NAMESPACE(polyveck_pointwise_poly_montgomery)
|
||||||
|
void polyveck_pointwise_poly_montgomery(polyveck *r, const poly *a, const polyveck *v);
|
||||||
|
|
||||||
|
#define polyveck_chknorm DILITHIUM_NAMESPACE(polyveck_chknorm)
|
||||||
|
int polyveck_chknorm(const polyveck *v, int32_t B);
|
||||||
|
|
||||||
|
#define polyveck_power2round DILITHIUM_NAMESPACE(polyveck_power2round)
|
||||||
|
void polyveck_power2round(polyveck *v1, polyveck *v0, const polyveck *v);
|
||||||
|
#define polyveck_decompose DILITHIUM_NAMESPACE(polyveck_decompose)
|
||||||
|
void polyveck_decompose(polyveck *v1, polyveck *v0, const polyveck *v);
|
||||||
|
#define polyveck_make_hint DILITHIUM_NAMESPACE(polyveck_make_hint)
|
||||||
|
unsigned int polyveck_make_hint(polyveck *h,
|
||||||
|
const polyveck *v0,
|
||||||
|
const polyveck *v1);
|
||||||
|
#define polyveck_use_hint DILITHIUM_NAMESPACE(polyveck_use_hint)
|
||||||
|
void polyveck_use_hint(polyveck *w, const polyveck *v, const polyveck *h);
|
||||||
|
|
||||||
|
#define polyveck_pack_w1 DILITHIUM_NAMESPACE(polyveck_pack_w1)
|
||||||
|
void polyveck_pack_w1(uint8_t r[K*POLYW1_PACKEDBYTES], const polyveck *w1);
|
||||||
|
|
||||||
|
#define polyvec_matrix_expand DILITHIUM_NAMESPACE(polyvec_matrix_expand)
|
||||||
|
void polyvec_matrix_expand(polyvecl mat[K], const uint8_t rho[SEEDBYTES]);
|
||||||
|
|
||||||
|
#define polyvec_matrix_pointwise_montgomery DILITHIUM_NAMESPACE(polyvec_matrix_pointwise_montgomery)
|
||||||
|
void polyvec_matrix_pointwise_montgomery(polyveck *t, const polyvecl mat[K], const polyvecl *v);
|
||||||
|
|
||||||
|
#endif
|
||||||
15
app/src/main/cpp/pq/mldsa65/randombytes.h
Normal file
15
app/src/main/cpp/pq/mldsa65/randombytes.h
Normal file
|
|
@ -0,0 +1,15 @@
|
||||||
|
#ifndef VEXTA_PQ_RANDOMBYTES_H
|
||||||
|
#define VEXTA_PQ_RANDOMBYTES_H
|
||||||
|
#include <stddef.h>
|
||||||
|
#include <stdint.h>
|
||||||
|
#ifdef __cplusplus
|
||||||
|
extern "C" {
|
||||||
|
#endif
|
||||||
|
/* VEXTA: unified prototype. Definition lives in pq_randombytes.cpp and is
|
||||||
|
backed by Bitcoin's CSPRNG (GetStrongRandBytes). The upstream test-only
|
||||||
|
randombytes.c files were deleted — they must never ship in a wallet. */
|
||||||
|
void randombytes(uint8_t *out, size_t outlen);
|
||||||
|
#ifdef __cplusplus
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
#endif
|
||||||
69
app/src/main/cpp/pq/mldsa65/reduce.c
Normal file
69
app/src/main/cpp/pq/mldsa65/reduce.c
Normal file
|
|
@ -0,0 +1,69 @@
|
||||||
|
#include <stdint.h>
|
||||||
|
#include "params.h"
|
||||||
|
#include "reduce.h"
|
||||||
|
|
||||||
|
/*************************************************
|
||||||
|
* Name: montgomery_reduce
|
||||||
|
*
|
||||||
|
* Description: For finite field element a with -2^{31}Q <= a <= Q*2^31,
|
||||||
|
* compute r \equiv a*2^{-32} (mod Q) such that -Q < r < Q.
|
||||||
|
*
|
||||||
|
* Arguments: - int64_t: finite field element a
|
||||||
|
*
|
||||||
|
* Returns r.
|
||||||
|
**************************************************/
|
||||||
|
int32_t montgomery_reduce(int64_t a) {
|
||||||
|
int32_t t;
|
||||||
|
|
||||||
|
t = (int64_t)(int32_t)a*QINV;
|
||||||
|
t = (a - (int64_t)t*Q) >> 32;
|
||||||
|
return t;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*************************************************
|
||||||
|
* Name: reduce32
|
||||||
|
*
|
||||||
|
* Description: For finite field element a with a <= 2^{31} - 2^{22} - 1,
|
||||||
|
* compute r \equiv a (mod Q) such that -6283008 <= r <= 6283008.
|
||||||
|
*
|
||||||
|
* Arguments: - int32_t: finite field element a
|
||||||
|
*
|
||||||
|
* Returns r.
|
||||||
|
**************************************************/
|
||||||
|
int32_t reduce32(int32_t a) {
|
||||||
|
int32_t t;
|
||||||
|
|
||||||
|
t = (a + (1 << 22)) >> 23;
|
||||||
|
t = a - t*Q;
|
||||||
|
return t;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*************************************************
|
||||||
|
* Name: caddq
|
||||||
|
*
|
||||||
|
* Description: Add Q if input coefficient is negative.
|
||||||
|
*
|
||||||
|
* Arguments: - int32_t: finite field element a
|
||||||
|
*
|
||||||
|
* Returns r.
|
||||||
|
**************************************************/
|
||||||
|
int32_t caddq(int32_t a) {
|
||||||
|
a += (a >> 31) & Q;
|
||||||
|
return a;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*************************************************
|
||||||
|
* Name: freeze
|
||||||
|
*
|
||||||
|
* Description: For finite field element a, compute standard
|
||||||
|
* representative r = a mod^+ Q.
|
||||||
|
*
|
||||||
|
* Arguments: - int32_t: finite field element a
|
||||||
|
*
|
||||||
|
* Returns r.
|
||||||
|
**************************************************/
|
||||||
|
int32_t freeze(int32_t a) {
|
||||||
|
a = reduce32(a);
|
||||||
|
a = caddq(a);
|
||||||
|
return a;
|
||||||
|
}
|
||||||
22
app/src/main/cpp/pq/mldsa65/reduce.h
Normal file
22
app/src/main/cpp/pq/mldsa65/reduce.h
Normal file
|
|
@ -0,0 +1,22 @@
|
||||||
|
#ifndef REDUCE_H
|
||||||
|
#define REDUCE_H
|
||||||
|
|
||||||
|
#include <stdint.h>
|
||||||
|
#include "params.h"
|
||||||
|
|
||||||
|
#define MONT -4186625 // 2^32 % Q
|
||||||
|
#define QINV 58728449 // q^(-1) mod 2^32
|
||||||
|
|
||||||
|
#define montgomery_reduce DILITHIUM_NAMESPACE(montgomery_reduce)
|
||||||
|
int32_t montgomery_reduce(int64_t a);
|
||||||
|
|
||||||
|
#define reduce32 DILITHIUM_NAMESPACE(reduce32)
|
||||||
|
int32_t reduce32(int32_t a);
|
||||||
|
|
||||||
|
#define caddq DILITHIUM_NAMESPACE(caddq)
|
||||||
|
int32_t caddq(int32_t a);
|
||||||
|
|
||||||
|
#define freeze DILITHIUM_NAMESPACE(freeze)
|
||||||
|
int32_t freeze(int32_t a);
|
||||||
|
|
||||||
|
#endif
|
||||||
102
app/src/main/cpp/pq/mldsa65/rounding.c
Normal file
102
app/src/main/cpp/pq/mldsa65/rounding.c
Normal file
|
|
@ -0,0 +1,102 @@
|
||||||
|
#include <stdint.h>
|
||||||
|
#include "params.h"
|
||||||
|
#include "rounding.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: - int32_t a: input element
|
||||||
|
* - int32_t *a0: pointer to output element a0
|
||||||
|
*
|
||||||
|
* Returns a1.
|
||||||
|
**************************************************/
|
||||||
|
int32_t power2round(int32_t *a0, int32_t a) {
|
||||||
|
int32_t a1;
|
||||||
|
|
||||||
|
a1 = (a + (1 << (D-1)) - 1) >> D;
|
||||||
|
*a0 = a - (a1 << D);
|
||||||
|
return a1;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*************************************************
|
||||||
|
* 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: - int32_t a: input element
|
||||||
|
* - int32_t *a0: pointer to output element a0
|
||||||
|
*
|
||||||
|
* Returns a1.
|
||||||
|
**************************************************/
|
||||||
|
int32_t decompose(int32_t *a0, int32_t a) {
|
||||||
|
int32_t a1;
|
||||||
|
|
||||||
|
a1 = (a + 127) >> 7;
|
||||||
|
#if GAMMA2 == (Q-1)/32
|
||||||
|
a1 = (a1*1025 + (1 << 21)) >> 22;
|
||||||
|
a1 &= 15;
|
||||||
|
#elif GAMMA2 == (Q-1)/88
|
||||||
|
a1 = (a1*11275 + (1 << 23)) >> 24;
|
||||||
|
a1 ^= ((43 - a1) >> 31) & a1;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
*a0 = a - a1*2*GAMMA2;
|
||||||
|
*a0 -= (((Q-1)/2 - *a0) >> 31) & Q;
|
||||||
|
return a1;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*************************************************
|
||||||
|
* Name: make_hint
|
||||||
|
*
|
||||||
|
* Description: Compute hint bit indicating whether the low bits of the
|
||||||
|
* input element overflow into the high bits.
|
||||||
|
*
|
||||||
|
* Arguments: - int32_t a0: low bits of input element
|
||||||
|
* - int32_t a1: high bits of input element
|
||||||
|
*
|
||||||
|
* Returns 1 if overflow.
|
||||||
|
**************************************************/
|
||||||
|
unsigned int make_hint(int32_t a0, int32_t a1) {
|
||||||
|
if(a0 > GAMMA2 || a0 < -GAMMA2 || (a0 == -GAMMA2 && a1 != 0))
|
||||||
|
return 1;
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*************************************************
|
||||||
|
* Name: use_hint
|
||||||
|
*
|
||||||
|
* Description: Correct high bits according to hint.
|
||||||
|
*
|
||||||
|
* Arguments: - int32_t a: input element
|
||||||
|
* - unsigned int hint: hint bit
|
||||||
|
*
|
||||||
|
* Returns corrected high bits.
|
||||||
|
**************************************************/
|
||||||
|
int32_t use_hint(int32_t a, unsigned int hint) {
|
||||||
|
int32_t a0, a1;
|
||||||
|
|
||||||
|
a1 = decompose(&a0, a);
|
||||||
|
if(hint == 0)
|
||||||
|
return a1;
|
||||||
|
|
||||||
|
#if GAMMA2 == (Q-1)/32
|
||||||
|
if(a0 > 0)
|
||||||
|
return (a1 + 1) & 15;
|
||||||
|
else
|
||||||
|
return (a1 - 1) & 15;
|
||||||
|
#elif GAMMA2 == (Q-1)/88
|
||||||
|
if(a0 > 0)
|
||||||
|
return (a1 == 43) ? 0 : a1 + 1;
|
||||||
|
else
|
||||||
|
return (a1 == 0) ? 43 : a1 - 1;
|
||||||
|
#endif
|
||||||
|
}
|
||||||
19
app/src/main/cpp/pq/mldsa65/rounding.h
Normal file
19
app/src/main/cpp/pq/mldsa65/rounding.h
Normal file
|
|
@ -0,0 +1,19 @@
|
||||||
|
#ifndef ROUNDING_H
|
||||||
|
#define ROUNDING_H
|
||||||
|
|
||||||
|
#include <stdint.h>
|
||||||
|
#include "params.h"
|
||||||
|
|
||||||
|
#define power2round DILITHIUM_NAMESPACE(power2round)
|
||||||
|
int32_t power2round(int32_t *a0, int32_t a);
|
||||||
|
|
||||||
|
#define decompose DILITHIUM_NAMESPACE(decompose)
|
||||||
|
int32_t decompose(int32_t *a0, int32_t a);
|
||||||
|
|
||||||
|
#define make_hint DILITHIUM_NAMESPACE(make_hint)
|
||||||
|
unsigned int make_hint(int32_t a0, int32_t a1);
|
||||||
|
|
||||||
|
#define use_hint DILITHIUM_NAMESPACE(use_hint)
|
||||||
|
int32_t use_hint(int32_t a, unsigned int hint);
|
||||||
|
|
||||||
|
#endif
|
||||||
477
app/src/main/cpp/pq/mldsa65/sign.c
Normal file
477
app/src/main/cpp/pq/mldsa65/sign.c
Normal file
|
|
@ -0,0 +1,477 @@
|
||||||
|
#include <stdint.h>
|
||||||
|
#include "params.h"
|
||||||
|
#include "sign.h"
|
||||||
|
#include "packing.h"
|
||||||
|
#include "polyvec.h"
|
||||||
|
#include "poly.h"
|
||||||
|
#include "randombytes.h"
|
||||||
|
#include "symmetric.h"
|
||||||
|
#include "fips202.h"
|
||||||
|
|
||||||
|
#ifndef VEXTA_PQ_VERIFY_ONLY
|
||||||
|
/*************************************************
|
||||||
|
* 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 crypto_sign_keypair_from_seed(uint8_t *pk, uint8_t *sk, const uint8_t *seed) {
|
||||||
|
uint8_t seedbuf[2*SEEDBYTES + CRHBYTES];
|
||||||
|
uint8_t tr[TRBYTES];
|
||||||
|
const uint8_t *rho, *rhoprime, *key;
|
||||||
|
polyvecl mat[K];
|
||||||
|
polyvecl s1, s1hat;
|
||||||
|
polyveck s2, t1, t0;
|
||||||
|
for (int _i=0;_i<SEEDBYTES;_i++) seedbuf[_i]=seed[_i];
|
||||||
|
seedbuf[SEEDBYTES+0] = K;
|
||||||
|
seedbuf[SEEDBYTES+1] = L;
|
||||||
|
shake256(seedbuf, 2*SEEDBYTES + CRHBYTES, seedbuf, SEEDBYTES+2);
|
||||||
|
rho = seedbuf;
|
||||||
|
rhoprime = rho + SEEDBYTES;
|
||||||
|
key = rhoprime + CRHBYTES;
|
||||||
|
polyvec_matrix_expand(mat, rho);
|
||||||
|
polyvecl_uniform_eta(&s1, rhoprime, 0);
|
||||||
|
polyveck_uniform_eta(&s2, rhoprime, L);
|
||||||
|
s1hat = s1;
|
||||||
|
polyvecl_ntt(&s1hat);
|
||||||
|
polyvec_matrix_pointwise_montgomery(&t1, mat, &s1hat);
|
||||||
|
polyveck_reduce(&t1);
|
||||||
|
polyveck_invntt_tomont(&t1);
|
||||||
|
polyveck_add(&t1, &t1, &s2);
|
||||||
|
polyveck_caddq(&t1);
|
||||||
|
polyveck_power2round(&t1, &t0, &t1);
|
||||||
|
pack_pk(pk, rho, &t1);
|
||||||
|
shake256(tr, TRBYTES, pk, CRYPTO_PUBLICKEYBYTES);
|
||||||
|
pack_sk(sk, rho, tr, key, &t0, &s1, &s2);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
int crypto_sign_keypair(uint8_t *pk, uint8_t *sk) {
|
||||||
|
uint8_t seedbuf[2*SEEDBYTES + CRHBYTES];
|
||||||
|
uint8_t tr[TRBYTES];
|
||||||
|
const uint8_t *rho, *rhoprime, *key;
|
||||||
|
polyvecl mat[K];
|
||||||
|
polyvecl s1, s1hat;
|
||||||
|
polyveck s2, t1, t0;
|
||||||
|
|
||||||
|
/* Get randomness for rho, rhoprime and key */
|
||||||
|
randombytes(seedbuf, SEEDBYTES);
|
||||||
|
seedbuf[SEEDBYTES+0] = K;
|
||||||
|
seedbuf[SEEDBYTES+1] = L;
|
||||||
|
shake256(seedbuf, 2*SEEDBYTES + CRHBYTES, seedbuf, SEEDBYTES+2);
|
||||||
|
rho = seedbuf;
|
||||||
|
rhoprime = rho + SEEDBYTES;
|
||||||
|
key = rhoprime + CRHBYTES;
|
||||||
|
|
||||||
|
/* Expand matrix */
|
||||||
|
polyvec_matrix_expand(mat, rho);
|
||||||
|
|
||||||
|
/* Sample short vectors s1 and s2 */
|
||||||
|
polyvecl_uniform_eta(&s1, rhoprime, 0);
|
||||||
|
polyveck_uniform_eta(&s2, rhoprime, L);
|
||||||
|
|
||||||
|
/* Matrix-vector multiplication */
|
||||||
|
s1hat = s1;
|
||||||
|
polyvecl_ntt(&s1hat);
|
||||||
|
polyvec_matrix_pointwise_montgomery(&t1, mat, &s1hat);
|
||||||
|
polyveck_reduce(&t1);
|
||||||
|
polyveck_invntt_tomont(&t1);
|
||||||
|
|
||||||
|
/* Add error vector s2 */
|
||||||
|
polyveck_add(&t1, &t1, &s2);
|
||||||
|
|
||||||
|
/* Extract t1 and write public key */
|
||||||
|
polyveck_caddq(&t1);
|
||||||
|
polyveck_power2round(&t1, &t0, &t1);
|
||||||
|
pack_pk(pk, rho, &t1);
|
||||||
|
|
||||||
|
/* Compute H(rho, t1) and write secret key */
|
||||||
|
shake256(tr, TRBYTES, pk, CRYPTO_PUBLICKEYBYTES);
|
||||||
|
pack_sk(sk, rho, tr, key, &t0, &s1, &s2);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*************************************************
|
||||||
|
* Name: crypto_sign_signature_internal
|
||||||
|
*
|
||||||
|
* Description: Computes signature. Internal API.
|
||||||
|
*
|
||||||
|
* Arguments: - uint8_t *sig: pointer to output signature (of length CRYPTO_BYTES)
|
||||||
|
* - size_t *siglen: pointer to output length of signature
|
||||||
|
* - uint8_t *m: pointer to message to be signed
|
||||||
|
* - size_t mlen: length of message
|
||||||
|
* - uint8_t *pre: pointer to prefix string
|
||||||
|
* - size_t prelen: length of prefix string
|
||||||
|
* - uint8_t *rnd: pointer to random seed
|
||||||
|
* - uint8_t *sk: pointer to bit-packed secret key
|
||||||
|
*
|
||||||
|
* Returns 0 (success)
|
||||||
|
**************************************************/
|
||||||
|
int crypto_sign_signature_internal(uint8_t *sig,
|
||||||
|
size_t *siglen,
|
||||||
|
const uint8_t *m,
|
||||||
|
size_t mlen,
|
||||||
|
const uint8_t *pre,
|
||||||
|
size_t prelen,
|
||||||
|
const uint8_t rnd[RNDBYTES],
|
||||||
|
const uint8_t *sk)
|
||||||
|
{
|
||||||
|
unsigned int n;
|
||||||
|
uint8_t seedbuf[2*SEEDBYTES + TRBYTES + 2*CRHBYTES];
|
||||||
|
uint8_t *rho, *tr, *key, *mu, *rhoprime;
|
||||||
|
uint16_t nonce = 0;
|
||||||
|
polyvecl mat[K], s1, y, z;
|
||||||
|
polyveck t0, s2, w1, w0, h;
|
||||||
|
poly cp;
|
||||||
|
keccak_state state;
|
||||||
|
|
||||||
|
rho = seedbuf;
|
||||||
|
tr = rho + SEEDBYTES;
|
||||||
|
key = tr + TRBYTES;
|
||||||
|
mu = key + SEEDBYTES;
|
||||||
|
rhoprime = mu + CRHBYTES;
|
||||||
|
unpack_sk(rho, tr, key, &t0, &s1, &s2, sk);
|
||||||
|
|
||||||
|
/* Compute mu = CRH(tr, pre, msg) */
|
||||||
|
shake256_init(&state);
|
||||||
|
shake256_absorb(&state, tr, TRBYTES);
|
||||||
|
shake256_absorb(&state, pre, prelen);
|
||||||
|
shake256_absorb(&state, m, mlen);
|
||||||
|
shake256_finalize(&state);
|
||||||
|
shake256_squeeze(mu, CRHBYTES, &state);
|
||||||
|
|
||||||
|
/* Compute rhoprime = CRH(key, rnd, mu) */
|
||||||
|
shake256_init(&state);
|
||||||
|
shake256_absorb(&state, key, SEEDBYTES);
|
||||||
|
shake256_absorb(&state, rnd, RNDBYTES);
|
||||||
|
shake256_absorb(&state, mu, CRHBYTES);
|
||||||
|
shake256_finalize(&state);
|
||||||
|
shake256_squeeze(rhoprime, CRHBYTES, &state);
|
||||||
|
|
||||||
|
/* Expand matrix and transform vectors */
|
||||||
|
polyvec_matrix_expand(mat, rho);
|
||||||
|
polyvecl_ntt(&s1);
|
||||||
|
polyveck_ntt(&s2);
|
||||||
|
polyveck_ntt(&t0);
|
||||||
|
|
||||||
|
rej:
|
||||||
|
/* Sample intermediate vector y */
|
||||||
|
polyvecl_uniform_gamma1(&y, rhoprime, nonce++);
|
||||||
|
|
||||||
|
/* Matrix-vector multiplication */
|
||||||
|
z = y;
|
||||||
|
polyvecl_ntt(&z);
|
||||||
|
polyvec_matrix_pointwise_montgomery(&w1, mat, &z);
|
||||||
|
polyveck_reduce(&w1);
|
||||||
|
polyveck_invntt_tomont(&w1);
|
||||||
|
|
||||||
|
/* Decompose w and call the random oracle */
|
||||||
|
polyveck_caddq(&w1);
|
||||||
|
polyveck_decompose(&w1, &w0, &w1);
|
||||||
|
polyveck_pack_w1(sig, &w1);
|
||||||
|
|
||||||
|
shake256_init(&state);
|
||||||
|
shake256_absorb(&state, mu, CRHBYTES);
|
||||||
|
shake256_absorb(&state, sig, K*POLYW1_PACKEDBYTES);
|
||||||
|
shake256_finalize(&state);
|
||||||
|
shake256_squeeze(sig, CTILDEBYTES, &state);
|
||||||
|
poly_challenge(&cp, sig);
|
||||||
|
poly_ntt(&cp);
|
||||||
|
|
||||||
|
/* Compute z, reject if it reveals secret */
|
||||||
|
polyvecl_pointwise_poly_montgomery(&z, &cp, &s1);
|
||||||
|
polyvecl_invntt_tomont(&z);
|
||||||
|
polyvecl_add(&z, &z, &y);
|
||||||
|
polyvecl_reduce(&z);
|
||||||
|
if(polyvecl_chknorm(&z, GAMMA1 - BETA))
|
||||||
|
goto rej;
|
||||||
|
|
||||||
|
/* Check that subtracting cs2 does not change high bits of w and low bits
|
||||||
|
* do not reveal secret information */
|
||||||
|
polyveck_pointwise_poly_montgomery(&h, &cp, &s2);
|
||||||
|
polyveck_invntt_tomont(&h);
|
||||||
|
polyveck_sub(&w0, &w0, &h);
|
||||||
|
polyveck_reduce(&w0);
|
||||||
|
if(polyveck_chknorm(&w0, GAMMA2 - BETA))
|
||||||
|
goto rej;
|
||||||
|
|
||||||
|
/* Compute hints for w1 */
|
||||||
|
polyveck_pointwise_poly_montgomery(&h, &cp, &t0);
|
||||||
|
polyveck_invntt_tomont(&h);
|
||||||
|
polyveck_reduce(&h);
|
||||||
|
if(polyveck_chknorm(&h, GAMMA2))
|
||||||
|
goto rej;
|
||||||
|
|
||||||
|
polyveck_add(&w0, &w0, &h);
|
||||||
|
n = polyveck_make_hint(&h, &w0, &w1);
|
||||||
|
if(n > OMEGA)
|
||||||
|
goto rej;
|
||||||
|
|
||||||
|
/* Write signature */
|
||||||
|
pack_sig(sig, sig, &z, &h);
|
||||||
|
*siglen = CRYPTO_BYTES;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*************************************************
|
||||||
|
* Name: crypto_sign_signature
|
||||||
|
*
|
||||||
|
* Description: Computes signature.
|
||||||
|
*
|
||||||
|
* Arguments: - uint8_t *sig: pointer to output signature (of length CRYPTO_BYTES)
|
||||||
|
* - size_t *siglen: pointer to output length of signature
|
||||||
|
* - uint8_t *m: pointer to message to be signed
|
||||||
|
* - size_t mlen: length of message
|
||||||
|
* - uint8_t *ctx: pointer to contex string
|
||||||
|
* - size_t ctxlen: length of contex string
|
||||||
|
* - uint8_t *sk: pointer to bit-packed secret key
|
||||||
|
*
|
||||||
|
* Returns 0 (success) or -1 (context string too long)
|
||||||
|
**************************************************/
|
||||||
|
int crypto_sign_signature(uint8_t *sig,
|
||||||
|
size_t *siglen,
|
||||||
|
const uint8_t *m,
|
||||||
|
size_t mlen,
|
||||||
|
const uint8_t *ctx,
|
||||||
|
size_t ctxlen,
|
||||||
|
const uint8_t *sk)
|
||||||
|
{
|
||||||
|
size_t i;
|
||||||
|
uint8_t pre[257];
|
||||||
|
uint8_t rnd[RNDBYTES];
|
||||||
|
|
||||||
|
if(ctxlen > 255)
|
||||||
|
return -1;
|
||||||
|
|
||||||
|
/* Prepare pre = (0, ctxlen, ctx) */
|
||||||
|
pre[0] = 0;
|
||||||
|
pre[1] = ctxlen;
|
||||||
|
for(i = 0; i < ctxlen; i++)
|
||||||
|
pre[2 + i] = ctx[i];
|
||||||
|
|
||||||
|
#ifdef DILITHIUM_RANDOMIZED_SIGNING
|
||||||
|
randombytes(rnd, RNDBYTES);
|
||||||
|
#else
|
||||||
|
for(i=0;i<RNDBYTES;i++)
|
||||||
|
rnd[i] = 0;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
crypto_sign_signature_internal(sig,siglen,m,mlen,pre,2+ctxlen,rnd,sk);
|
||||||
|
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 *ctx: pointer to context string
|
||||||
|
* - size_t ctxlen: length of context string
|
||||||
|
* - const uint8_t *sk: pointer to bit-packed secret key
|
||||||
|
*
|
||||||
|
* Returns 0 (success) or -1 (context string too long)
|
||||||
|
**************************************************/
|
||||||
|
int crypto_sign(uint8_t *sm,
|
||||||
|
size_t *smlen,
|
||||||
|
const uint8_t *m,
|
||||||
|
size_t mlen,
|
||||||
|
const uint8_t *ctx,
|
||||||
|
size_t ctxlen,
|
||||||
|
const uint8_t *sk)
|
||||||
|
{
|
||||||
|
int ret;
|
||||||
|
size_t i;
|
||||||
|
|
||||||
|
for(i = 0; i < mlen; ++i)
|
||||||
|
sm[CRYPTO_BYTES + mlen - 1 - i] = m[mlen - 1 - i];
|
||||||
|
ret = crypto_sign_signature(sm, smlen, sm + CRYPTO_BYTES, mlen, ctx, ctxlen, sk);
|
||||||
|
*smlen += mlen;
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif // !VEXTA_PQ_VERIFY_ONLY
|
||||||
|
|
||||||
|
/*************************************************
|
||||||
|
* Name: crypto_sign_verify_internal
|
||||||
|
*
|
||||||
|
* Description: Verifies signature. Internal API.
|
||||||
|
*
|
||||||
|
* Arguments: - uint8_t *m: pointer to input signature
|
||||||
|
* - size_t siglen: length of signature
|
||||||
|
* - const uint8_t *m: pointer to message
|
||||||
|
* - size_t mlen: length of message
|
||||||
|
* - const uint8_t *pre: pointer to prefix string
|
||||||
|
* - size_t prelen: length of prefix string
|
||||||
|
* - const uint8_t *pk: pointer to bit-packed public key
|
||||||
|
*
|
||||||
|
* Returns 0 if signature could be verified correctly and -1 otherwise
|
||||||
|
**************************************************/
|
||||||
|
int crypto_sign_verify_internal(const uint8_t *sig,
|
||||||
|
size_t siglen,
|
||||||
|
const uint8_t *m,
|
||||||
|
size_t mlen,
|
||||||
|
const uint8_t *pre,
|
||||||
|
size_t prelen,
|
||||||
|
const uint8_t *pk)
|
||||||
|
{
|
||||||
|
unsigned int i;
|
||||||
|
uint8_t buf[K*POLYW1_PACKEDBYTES];
|
||||||
|
uint8_t rho[SEEDBYTES];
|
||||||
|
uint8_t mu[CRHBYTES];
|
||||||
|
uint8_t c[CTILDEBYTES];
|
||||||
|
uint8_t c2[CTILDEBYTES];
|
||||||
|
poly cp;
|
||||||
|
polyvecl mat[K], z;
|
||||||
|
polyveck t1, w1, h;
|
||||||
|
keccak_state state;
|
||||||
|
|
||||||
|
if(siglen != CRYPTO_BYTES)
|
||||||
|
return -1;
|
||||||
|
|
||||||
|
unpack_pk(rho, &t1, pk);
|
||||||
|
if(unpack_sig(c, &z, &h, sig))
|
||||||
|
return -1;
|
||||||
|
if(polyvecl_chknorm(&z, GAMMA1 - BETA))
|
||||||
|
return -1;
|
||||||
|
|
||||||
|
/* Compute CRH(H(rho, t1), pre, msg) */
|
||||||
|
shake256(mu, TRBYTES, pk, CRYPTO_PUBLICKEYBYTES);
|
||||||
|
shake256_init(&state);
|
||||||
|
shake256_absorb(&state, mu, TRBYTES);
|
||||||
|
shake256_absorb(&state, pre, prelen);
|
||||||
|
shake256_absorb(&state, m, mlen);
|
||||||
|
shake256_finalize(&state);
|
||||||
|
shake256_squeeze(mu, CRHBYTES, &state);
|
||||||
|
|
||||||
|
/* Matrix-vector multiplication; compute Az - c2^dt1 */
|
||||||
|
poly_challenge(&cp, c);
|
||||||
|
polyvec_matrix_expand(mat, rho);
|
||||||
|
|
||||||
|
polyvecl_ntt(&z);
|
||||||
|
polyvec_matrix_pointwise_montgomery(&w1, mat, &z);
|
||||||
|
|
||||||
|
poly_ntt(&cp);
|
||||||
|
polyveck_shiftl(&t1);
|
||||||
|
polyveck_ntt(&t1);
|
||||||
|
polyveck_pointwise_poly_montgomery(&t1, &cp, &t1);
|
||||||
|
|
||||||
|
polyveck_sub(&w1, &w1, &t1);
|
||||||
|
polyveck_reduce(&w1);
|
||||||
|
polyveck_invntt_tomont(&w1);
|
||||||
|
|
||||||
|
/* Reconstruct w1 */
|
||||||
|
polyveck_caddq(&w1);
|
||||||
|
polyveck_use_hint(&w1, &w1, &h);
|
||||||
|
polyveck_pack_w1(buf, &w1);
|
||||||
|
|
||||||
|
/* Call random oracle and verify challenge */
|
||||||
|
shake256_init(&state);
|
||||||
|
shake256_absorb(&state, mu, CRHBYTES);
|
||||||
|
shake256_absorb(&state, buf, K*POLYW1_PACKEDBYTES);
|
||||||
|
shake256_finalize(&state);
|
||||||
|
shake256_squeeze(c2, CTILDEBYTES, &state);
|
||||||
|
for(i = 0; i < CTILDEBYTES; ++i)
|
||||||
|
if(c[i] != c2[i])
|
||||||
|
return -1;
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*************************************************
|
||||||
|
* Name: crypto_sign_verify
|
||||||
|
*
|
||||||
|
* Description: Verifies signature.
|
||||||
|
*
|
||||||
|
* Arguments: - uint8_t *m: pointer to input signature
|
||||||
|
* - size_t siglen: length of signature
|
||||||
|
* - const uint8_t *m: pointer to message
|
||||||
|
* - size_t mlen: length of message
|
||||||
|
* - const uint8_t *ctx: pointer to context string
|
||||||
|
* - size_t ctxlen: length of context string
|
||||||
|
* - const uint8_t *pk: pointer to bit-packed public key
|
||||||
|
*
|
||||||
|
* Returns 0 if signature could be verified correctly and -1 otherwise
|
||||||
|
**************************************************/
|
||||||
|
int crypto_sign_verify(const uint8_t *sig,
|
||||||
|
size_t siglen,
|
||||||
|
const uint8_t *m,
|
||||||
|
size_t mlen,
|
||||||
|
const uint8_t *ctx,
|
||||||
|
size_t ctxlen,
|
||||||
|
const uint8_t *pk)
|
||||||
|
{
|
||||||
|
size_t i;
|
||||||
|
uint8_t pre[257];
|
||||||
|
|
||||||
|
if(ctxlen > 255)
|
||||||
|
return -1;
|
||||||
|
|
||||||
|
pre[0] = 0;
|
||||||
|
pre[1] = ctxlen;
|
||||||
|
for(i = 0; i < ctxlen; i++)
|
||||||
|
pre[2 + i] = ctx[i];
|
||||||
|
|
||||||
|
return crypto_sign_verify_internal(sig,siglen,m,mlen,pre,2+ctxlen,pk);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*************************************************
|
||||||
|
* 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 *ctx: pointer to context tring
|
||||||
|
* - size_t ctxlen: length of context string
|
||||||
|
* - const uint8_t *pk: pointer to bit-packed public key
|
||||||
|
*
|
||||||
|
* Returns 0 if signed message could be verified correctly and -1 otherwise
|
||||||
|
**************************************************/
|
||||||
|
int crypto_sign_open(uint8_t *m,
|
||||||
|
size_t *mlen,
|
||||||
|
const uint8_t *sm,
|
||||||
|
size_t smlen,
|
||||||
|
const uint8_t *ctx,
|
||||||
|
size_t ctxlen,
|
||||||
|
const uint8_t *pk)
|
||||||
|
{
|
||||||
|
size_t i;
|
||||||
|
|
||||||
|
if(smlen < CRYPTO_BYTES)
|
||||||
|
goto badsig;
|
||||||
|
|
||||||
|
*mlen = smlen - CRYPTO_BYTES;
|
||||||
|
if(crypto_sign_verify(sm, CRYPTO_BYTES, sm + CRYPTO_BYTES, *mlen, ctx, ctxlen, pk))
|
||||||
|
goto badsig;
|
||||||
|
else {
|
||||||
|
/* All good, copy msg, return 0 */
|
||||||
|
for(i = 0; i < *mlen; ++i)
|
||||||
|
m[i] = sm[CRYPTO_BYTES + i];
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
badsig:
|
||||||
|
/* Signature verification failed */
|
||||||
|
*mlen = 0;
|
||||||
|
for(i = 0; i < smlen; ++i)
|
||||||
|
m[i] = 0;
|
||||||
|
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
56
app/src/main/cpp/pq/mldsa65/sign.h
Normal file
56
app/src/main/cpp/pq/mldsa65/sign.h
Normal file
|
|
@ -0,0 +1,56 @@
|
||||||
|
#ifndef SIGN_H
|
||||||
|
#define SIGN_H
|
||||||
|
|
||||||
|
#include <stddef.h>
|
||||||
|
#include <stdint.h>
|
||||||
|
#include "params.h"
|
||||||
|
#include "polyvec.h"
|
||||||
|
#include "poly.h"
|
||||||
|
|
||||||
|
#define crypto_sign_keypair DILITHIUM_NAMESPACE(keypair)
|
||||||
|
int crypto_sign_keypair(uint8_t *pk, uint8_t *sk);
|
||||||
|
|
||||||
|
#define crypto_sign_signature_internal DILITHIUM_NAMESPACE(signature_internal)
|
||||||
|
int crypto_sign_signature_internal(uint8_t *sig,
|
||||||
|
size_t *siglen,
|
||||||
|
const uint8_t *m,
|
||||||
|
size_t mlen,
|
||||||
|
const uint8_t *pre,
|
||||||
|
size_t prelen,
|
||||||
|
const uint8_t rnd[RNDBYTES],
|
||||||
|
const uint8_t *sk);
|
||||||
|
|
||||||
|
#define crypto_sign_signature DILITHIUM_NAMESPACE(signature)
|
||||||
|
int crypto_sign_signature(uint8_t *sig, size_t *siglen,
|
||||||
|
const uint8_t *m, size_t mlen,
|
||||||
|
const uint8_t *ctx, size_t ctxlen,
|
||||||
|
const uint8_t *sk);
|
||||||
|
|
||||||
|
#define crypto_sign DILITHIUM_NAMESPACETOP
|
||||||
|
int crypto_sign(uint8_t *sm, size_t *smlen,
|
||||||
|
const uint8_t *m, size_t mlen,
|
||||||
|
const uint8_t *ctx, size_t ctxlen,
|
||||||
|
const uint8_t *sk);
|
||||||
|
|
||||||
|
#define crypto_sign_verify_internal DILITHIUM_NAMESPACE(verify_internal)
|
||||||
|
int crypto_sign_verify_internal(const uint8_t *sig,
|
||||||
|
size_t siglen,
|
||||||
|
const uint8_t *m,
|
||||||
|
size_t mlen,
|
||||||
|
const uint8_t *pre,
|
||||||
|
size_t prelen,
|
||||||
|
const uint8_t *pk);
|
||||||
|
|
||||||
|
#define crypto_sign_verify DILITHIUM_NAMESPACE(verify)
|
||||||
|
int crypto_sign_verify(const uint8_t *sig, size_t siglen,
|
||||||
|
const uint8_t *m, size_t mlen,
|
||||||
|
const uint8_t *ctx, size_t ctxlen,
|
||||||
|
const uint8_t *pk);
|
||||||
|
|
||||||
|
#define crypto_sign_open DILITHIUM_NAMESPACE(open)
|
||||||
|
int crypto_sign_open(uint8_t *m, size_t *mlen,
|
||||||
|
const uint8_t *sm, size_t smlen,
|
||||||
|
const uint8_t *ctx, size_t ctxlen,
|
||||||
|
const uint8_t *pk);
|
||||||
|
|
||||||
|
#endif
|
||||||
28
app/src/main/cpp/pq/mldsa65/symmetric-shake.c
Normal file
28
app/src/main/cpp/pq/mldsa65/symmetric-shake.c
Normal file
|
|
@ -0,0 +1,28 @@
|
||||||
|
#include <stdint.h>
|
||||||
|
#include "params.h"
|
||||||
|
#include "symmetric.h"
|
||||||
|
#include "fips202.h"
|
||||||
|
|
||||||
|
void dilithium_shake128_stream_init(keccak_state *state, const uint8_t seed[SEEDBYTES], uint16_t nonce)
|
||||||
|
{
|
||||||
|
uint8_t t[2];
|
||||||
|
t[0] = nonce;
|
||||||
|
t[1] = nonce >> 8;
|
||||||
|
|
||||||
|
shake128_init(state);
|
||||||
|
shake128_absorb(state, seed, SEEDBYTES);
|
||||||
|
shake128_absorb(state, t, 2);
|
||||||
|
shake128_finalize(state);
|
||||||
|
}
|
||||||
|
|
||||||
|
void dilithium_shake256_stream_init(keccak_state *state, const uint8_t seed[CRHBYTES], uint16_t nonce)
|
||||||
|
{
|
||||||
|
uint8_t t[2];
|
||||||
|
t[0] = nonce;
|
||||||
|
t[1] = nonce >> 8;
|
||||||
|
|
||||||
|
shake256_init(state);
|
||||||
|
shake256_absorb(state, seed, CRHBYTES);
|
||||||
|
shake256_absorb(state, t, 2);
|
||||||
|
shake256_finalize(state);
|
||||||
|
}
|
||||||
34
app/src/main/cpp/pq/mldsa65/symmetric.h
Normal file
34
app/src/main/cpp/pq/mldsa65/symmetric.h
Normal file
|
|
@ -0,0 +1,34 @@
|
||||||
|
#ifndef SYMMETRIC_H
|
||||||
|
#define SYMMETRIC_H
|
||||||
|
|
||||||
|
#include <stdint.h>
|
||||||
|
#include "params.h"
|
||||||
|
|
||||||
|
#include "fips202.h"
|
||||||
|
|
||||||
|
typedef keccak_state stream128_state;
|
||||||
|
typedef keccak_state stream256_state;
|
||||||
|
|
||||||
|
#define dilithium_shake128_stream_init DILITHIUM_NAMESPACE(dilithium_shake128_stream_init)
|
||||||
|
void dilithium_shake128_stream_init(keccak_state *state,
|
||||||
|
const uint8_t seed[SEEDBYTES],
|
||||||
|
uint16_t nonce);
|
||||||
|
|
||||||
|
#define dilithium_shake256_stream_init DILITHIUM_NAMESPACE(dilithium_shake256_stream_init)
|
||||||
|
void dilithium_shake256_stream_init(keccak_state *state,
|
||||||
|
const uint8_t seed[CRHBYTES],
|
||||||
|
uint16_t nonce);
|
||||||
|
|
||||||
|
#define STREAM128_BLOCKBYTES SHAKE128_RATE
|
||||||
|
#define STREAM256_BLOCKBYTES SHAKE256_RATE
|
||||||
|
|
||||||
|
#define stream128_init(STATE, SEED, NONCE) \
|
||||||
|
dilithium_shake128_stream_init(STATE, SEED, NONCE)
|
||||||
|
#define stream128_squeezeblocks(OUT, OUTBLOCKS, STATE) \
|
||||||
|
shake128_squeezeblocks(OUT, OUTBLOCKS, STATE)
|
||||||
|
#define stream256_init(STATE, SEED, NONCE) \
|
||||||
|
dilithium_shake256_stream_init(STATE, SEED, NONCE)
|
||||||
|
#define stream256_squeezeblocks(OUT, OUTBLOCKS, STATE) \
|
||||||
|
shake256_squeezeblocks(OUT, OUTBLOCKS, STATE)
|
||||||
|
|
||||||
|
#endif
|
||||||
37
app/src/main/cpp/pq/pq.cpp
Normal file
37
app/src/main/cpp/pq/pq.cpp
Normal file
|
|
@ -0,0 +1,37 @@
|
||||||
|
// VEXTA: wrapper impl. Bridges to the namespaced C reference symbols.
|
||||||
|
#include "pq.h"
|
||||||
|
extern "C" {
|
||||||
|
// ML-DSA-65 (DILITHIUM_MODE=3 -> pqcrystals_dilithium3_ref_*)
|
||||||
|
int pqcrystals_dilithium3_ref_keypair(uint8_t* pk, uint8_t* sk);
|
||||||
|
int crypto_sign_keypair_from_seed(uint8_t* pk, uint8_t* sk, const uint8_t* seed);
|
||||||
|
int pqcrystals_dilithium3_ref_signature(uint8_t* sig, size_t* siglen,
|
||||||
|
const uint8_t* m, size_t mlen, const uint8_t* ctx, size_t ctxlen, const uint8_t* sk);
|
||||||
|
int pqcrystals_dilithium3_ref_verify(const uint8_t* sig, size_t siglen,
|
||||||
|
const uint8_t* m, size_t mlen, const uint8_t* ctx, size_t ctxlen, const uint8_t* pk);
|
||||||
|
// SPHINCS+ (bare crypto_sign_* in ref)
|
||||||
|
int crypto_sign_keypair(uint8_t* pk, uint8_t* sk);
|
||||||
|
int crypto_sign_seed_keypair(uint8_t* pk, uint8_t* sk, const uint8_t* seed);
|
||||||
|
int crypto_sign_signature(uint8_t* sig, size_t* siglen, const uint8_t* m, size_t mlen, const uint8_t* sk);
|
||||||
|
int crypto_sign_verify(const uint8_t* sig, size_t siglen, const uint8_t* m, size_t mlen, const uint8_t* pk);
|
||||||
|
}
|
||||||
|
namespace pq {
|
||||||
|
#ifndef VEXTA_PQ_VERIFY_ONLY
|
||||||
|
bool MLDSA65::keygen(uint8_t* pk, uint8_t* sk){ return pqcrystals_dilithium3_ref_keypair(pk,sk)==0; }
|
||||||
|
bool MLDSA65::keygen_from_seed(uint8_t* pk, uint8_t* sk, const uint8_t* seed){ return crypto_sign_keypair_from_seed(pk,sk,seed)==0; }
|
||||||
|
bool MLDSA65::sign(uint8_t* sig,size_t* sl,const uint8_t* m,size_t ml,const uint8_t* sk){
|
||||||
|
return pqcrystals_dilithium3_ref_signature(sig,sl,m,ml,nullptr,0,sk)==0; }
|
||||||
|
#endif
|
||||||
|
|
||||||
|
bool MLDSA65::verify(const uint8_t* sig,size_t sl,const uint8_t* m,size_t ml,const uint8_t* pk){
|
||||||
|
return pqcrystals_dilithium3_ref_verify(sig,sl,m,ml,nullptr,0,pk)==0; }
|
||||||
|
|
||||||
|
#ifndef VEXTA_PQ_VERIFY_ONLY
|
||||||
|
bool SPHINCS128s::keygen(uint8_t* pk,uint8_t* sk){ return crypto_sign_keypair(pk,sk)==0; }
|
||||||
|
bool SPHINCS128s::keygen_from_seed(uint8_t* pk,uint8_t* sk,const uint8_t* seed){ return crypto_sign_seed_keypair(pk,sk,seed)==0; }
|
||||||
|
bool SPHINCS128s::sign(uint8_t* sig,size_t* sl,const uint8_t* m,size_t ml,const uint8_t* sk){
|
||||||
|
return crypto_sign_signature(sig,sl,m,ml,sk)==0; }
|
||||||
|
#endif
|
||||||
|
|
||||||
|
bool SPHINCS128s::verify(const uint8_t* sig,size_t sl,const uint8_t* m,size_t ml,const uint8_t* pk){
|
||||||
|
return crypto_sign_verify(sig,sl,m,ml,pk)==0; }
|
||||||
|
}
|
||||||
30
app/src/main/cpp/pq/pq.h
Normal file
30
app/src/main/cpp/pq/pq.h
Normal file
|
|
@ -0,0 +1,30 @@
|
||||||
|
// VEXTA: uniform post-quantum signature interface over the vendored C references.
|
||||||
|
#ifndef VEXTA_CRYPTO_PQ_H
|
||||||
|
#define VEXTA_CRYPTO_PQ_H
|
||||||
|
#include <cstdint>
|
||||||
|
#include <cstddef>
|
||||||
|
#include <vector>
|
||||||
|
namespace pq {
|
||||||
|
|
||||||
|
struct MLDSA65 {
|
||||||
|
static constexpr size_t PUBKEY_BYTES = 1952; // ML-DSA-65 (FIPS 204)
|
||||||
|
static constexpr size_t SECKEY_BYTES = 4032;
|
||||||
|
static constexpr size_t SIG_BYTES = 3309;
|
||||||
|
static bool keygen(uint8_t* pk, uint8_t* sk);
|
||||||
|
static bool keygen_from_seed(uint8_t* pk, uint8_t* sk, const uint8_t* seed);
|
||||||
|
static bool sign(uint8_t* sig, size_t* siglen, const uint8_t* msg, size_t msglen, const uint8_t* sk);
|
||||||
|
static bool verify(const uint8_t* sig, size_t siglen, const uint8_t* msg, size_t msglen, const uint8_t* pk);
|
||||||
|
};
|
||||||
|
|
||||||
|
struct SPHINCS128s {
|
||||||
|
static constexpr size_t PUBKEY_BYTES = 32; // SLH-DSA sha2-128s (FIPS 205)
|
||||||
|
static constexpr size_t SECKEY_BYTES = 64;
|
||||||
|
static constexpr size_t SIG_BYTES = 7856;
|
||||||
|
static bool keygen(uint8_t* pk, uint8_t* sk);
|
||||||
|
static bool keygen_from_seed(uint8_t* pk, uint8_t* sk, const uint8_t* seed);
|
||||||
|
static bool sign(uint8_t* sig, size_t* siglen, const uint8_t* msg, size_t msglen, const uint8_t* sk);
|
||||||
|
static bool verify(const uint8_t* sig, size_t siglen, const uint8_t* msg, size_t msglen, const uint8_t* pk);
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace pq
|
||||||
|
#endif
|
||||||
2
app/src/main/cpp/pq/sphincsplus/LICENSE
Normal file
2
app/src/main/cpp/pq/sphincsplus/LICENSE
Normal file
|
|
@ -0,0 +1,2 @@
|
||||||
|
SPDX-License-Identifier: (LicenseRef-SPHINCS-PLUS-Public-Domain OR CC0-1.0 OR 0BSD OR MIT-0) AND MIT
|
||||||
|
|
||||||
104
app/src/main/cpp/pq/sphincsplus/address.c
Normal file
104
app/src/main/cpp/pq/sphincsplus/address.c
Normal file
|
|
@ -0,0 +1,104 @@
|
||||||
|
#include <stdint.h>
|
||||||
|
#include <string.h>
|
||||||
|
|
||||||
|
#include "address.h"
|
||||||
|
#include "params.h"
|
||||||
|
#include "utils.h"
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Specify which level of Merkle tree (the "layer") we're working on
|
||||||
|
*/
|
||||||
|
void set_layer_addr(uint32_t addr[8], uint32_t layer)
|
||||||
|
{
|
||||||
|
((unsigned char *)addr)[SPX_OFFSET_LAYER] = (unsigned char)layer;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Specify which Merkle tree within the level (the "tree address") we're working on
|
||||||
|
*/
|
||||||
|
void set_tree_addr(uint32_t addr[8], uint64_t tree)
|
||||||
|
{
|
||||||
|
#if (SPX_TREE_HEIGHT * (SPX_D - 1)) > 64
|
||||||
|
#error Subtree addressing is currently limited to at most 2^64 trees
|
||||||
|
#endif
|
||||||
|
ull_to_bytes(&((unsigned char *)addr)[SPX_OFFSET_TREE], 8, tree );
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Specify the reason we'll use this address structure for, that is, what
|
||||||
|
* hash will we compute with it. This is used so that unrelated types of
|
||||||
|
* hashes don't accidentally get the same address structure. The type will be
|
||||||
|
* one of the SPX_ADDR_TYPE constants
|
||||||
|
*/
|
||||||
|
void set_type(uint32_t addr[8], uint32_t type)
|
||||||
|
{
|
||||||
|
((unsigned char *)addr)[SPX_OFFSET_TYPE] = (unsigned char)type;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Copy the layer and tree fields of the address structure. This is used
|
||||||
|
* when we're doing multiple types of hashes within the same Merkle tree
|
||||||
|
*/
|
||||||
|
void copy_subtree_addr(uint32_t out[8], const uint32_t in[8])
|
||||||
|
{
|
||||||
|
memcpy( out, in, SPX_OFFSET_TREE+8 );
|
||||||
|
}
|
||||||
|
|
||||||
|
/* These functions are used for OTS addresses. */
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Specify which Merkle leaf we're working on; that is, which OTS keypair
|
||||||
|
* we're talking about.
|
||||||
|
*/
|
||||||
|
void set_keypair_addr(uint32_t addr[8], uint32_t keypair)
|
||||||
|
{
|
||||||
|
u32_to_bytes(&((unsigned char *)addr)[SPX_OFFSET_KP_ADDR], keypair);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Copy the layer, tree and keypair fields of the address structure. This is
|
||||||
|
* used when we're doing multiple things within the same OTS keypair
|
||||||
|
*/
|
||||||
|
void copy_keypair_addr(uint32_t out[8], const uint32_t in[8])
|
||||||
|
{
|
||||||
|
memcpy( out, in, SPX_OFFSET_TREE+8 );
|
||||||
|
memcpy( (unsigned char *)out + SPX_OFFSET_KP_ADDR, (unsigned char *)in + SPX_OFFSET_KP_ADDR, 4);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Specify which Merkle chain within the OTS we're working with
|
||||||
|
* (the chain address)
|
||||||
|
*/
|
||||||
|
void set_chain_addr(uint32_t addr[8], uint32_t chain)
|
||||||
|
{
|
||||||
|
((unsigned char *)addr)[SPX_OFFSET_CHAIN_ADDR] = (unsigned char)chain;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Specify where in the Merkle chain we are
|
||||||
|
* (the hash address)
|
||||||
|
*/
|
||||||
|
void set_hash_addr(uint32_t addr[8], uint32_t hash)
|
||||||
|
{
|
||||||
|
((unsigned char *)addr)[SPX_OFFSET_HASH_ADDR] = (unsigned char)hash;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* These functions are used for all hash tree addresses (including FORS). */
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Specify the height of the node in the Merkle/FORS tree we are in
|
||||||
|
* (the tree height)
|
||||||
|
*/
|
||||||
|
void set_tree_height(uint32_t addr[8], uint32_t tree_height)
|
||||||
|
{
|
||||||
|
((unsigned char *)addr)[SPX_OFFSET_TREE_HGT] = (unsigned char)tree_height;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Specify the distance from the left edge of the node in the Merkle/FORS tree
|
||||||
|
* (the tree index)
|
||||||
|
*/
|
||||||
|
void set_tree_index(uint32_t addr[8], uint32_t tree_index)
|
||||||
|
{
|
||||||
|
u32_to_bytes(&((unsigned char *)addr)[SPX_OFFSET_TREE_INDEX], tree_index );
|
||||||
|
}
|
||||||
51
app/src/main/cpp/pq/sphincsplus/address.h
Normal file
51
app/src/main/cpp/pq/sphincsplus/address.h
Normal file
|
|
@ -0,0 +1,51 @@
|
||||||
|
#ifndef SPX_ADDRESS_H
|
||||||
|
#define SPX_ADDRESS_H
|
||||||
|
|
||||||
|
#include <stdint.h>
|
||||||
|
#include "params.h"
|
||||||
|
|
||||||
|
/* The hash types that are passed to set_type */
|
||||||
|
#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
|
||||||
|
#define SPX_ADDR_TYPE_WOTSPRF 5
|
||||||
|
#define SPX_ADDR_TYPE_FORSPRF 6
|
||||||
|
|
||||||
|
#define set_layer_addr SPX_NAMESPACE(set_layer_addr)
|
||||||
|
void set_layer_addr(uint32_t addr[8], uint32_t layer);
|
||||||
|
|
||||||
|
#define set_tree_addr SPX_NAMESPACE(set_tree_addr)
|
||||||
|
void set_tree_addr(uint32_t addr[8], uint64_t tree);
|
||||||
|
|
||||||
|
#define set_type SPX_NAMESPACE(set_type)
|
||||||
|
void set_type(uint32_t addr[8], uint32_t type);
|
||||||
|
|
||||||
|
/* Copies the layer and tree part of one address into the other */
|
||||||
|
#define copy_subtree_addr SPX_NAMESPACE(copy_subtree_addr)
|
||||||
|
void copy_subtree_addr(uint32_t out[8], const uint32_t in[8]);
|
||||||
|
|
||||||
|
/* These functions are used for WOTS and FORS addresses. */
|
||||||
|
|
||||||
|
#define set_keypair_addr SPX_NAMESPACE(set_keypair_addr)
|
||||||
|
void set_keypair_addr(uint32_t addr[8], uint32_t keypair);
|
||||||
|
|
||||||
|
#define set_chain_addr SPX_NAMESPACE(set_chain_addr)
|
||||||
|
void set_chain_addr(uint32_t addr[8], uint32_t chain);
|
||||||
|
|
||||||
|
#define set_hash_addr SPX_NAMESPACE(set_hash_addr)
|
||||||
|
void set_hash_addr(uint32_t addr[8], uint32_t hash);
|
||||||
|
|
||||||
|
#define copy_keypair_addr SPX_NAMESPACE(copy_keypair_addr)
|
||||||
|
void copy_keypair_addr(uint32_t out[8], const uint32_t in[8]);
|
||||||
|
|
||||||
|
/* These functions are used for all hash tree addresses (including FORS). */
|
||||||
|
|
||||||
|
#define set_tree_height SPX_NAMESPACE(set_tree_height)
|
||||||
|
void set_tree_height(uint32_t addr[8], uint32_t tree_height);
|
||||||
|
|
||||||
|
#define set_tree_index SPX_NAMESPACE(set_tree_index)
|
||||||
|
void set_tree_index(uint32_t addr[8], uint32_t tree_index);
|
||||||
|
|
||||||
|
#endif
|
||||||
77
app/src/main/cpp/pq/sphincsplus/api.h
Normal file
77
app/src/main/cpp/pq/sphincsplus/api.h
Normal file
|
|
@ -0,0 +1,77 @@
|
||||||
|
#ifndef SPX_API_H
|
||||||
|
#define SPX_API_H
|
||||||
|
|
||||||
|
#include <stddef.h>
|
||||||
|
#include <stdint.h>
|
||||||
|
|
||||||
|
#include "params.h"
|
||||||
|
|
||||||
|
#define CRYPTO_ALGNAME "SPHINCS+"
|
||||||
|
|
||||||
|
#define CRYPTO_SECRETKEYBYTES SPX_SK_BYTES
|
||||||
|
#define CRYPTO_PUBLICKEYBYTES SPX_PK_BYTES
|
||||||
|
#define CRYPTO_BYTES SPX_BYTES
|
||||||
|
#define CRYPTO_SEEDBYTES 3*SPX_N
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Returns the length of a secret key, in bytes
|
||||||
|
*/
|
||||||
|
unsigned long long crypto_sign_secretkeybytes(void);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Returns the length of a public key, in bytes
|
||||||
|
*/
|
||||||
|
unsigned long long crypto_sign_publickeybytes(void);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Returns the length of a signature, in bytes
|
||||||
|
*/
|
||||||
|
unsigned long long crypto_sign_bytes(void);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Returns the length of the seed required to generate a key pair, in bytes
|
||||||
|
*/
|
||||||
|
unsigned long long 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 crypto_sign_seed_keypair(unsigned char *pk, unsigned char *sk,
|
||||||
|
const unsigned char *seed);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Generates a SPHINCS+ key pair.
|
||||||
|
* Format sk: [SK_SEED || SK_PRF || PUB_SEED || root]
|
||||||
|
* Format pk: [root || PUB_SEED]
|
||||||
|
*/
|
||||||
|
int crypto_sign_keypair(unsigned char *pk, unsigned char *sk);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns an array containing a detached signature.
|
||||||
|
*/
|
||||||
|
int 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 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 crypto_sign(unsigned char *sm, unsigned long long *smlen,
|
||||||
|
const unsigned char *m, unsigned long long mlen,
|
||||||
|
const unsigned char *sk);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Verifies a given signature-message pair under a given public key.
|
||||||
|
*/
|
||||||
|
int crypto_sign_open(unsigned char *m, unsigned long long *mlen,
|
||||||
|
const unsigned char *sm, unsigned long long smlen,
|
||||||
|
const unsigned char *pk);
|
||||||
|
|
||||||
|
#endif
|
||||||
28
app/src/main/cpp/pq/sphincsplus/context.h
Normal file
28
app/src/main/cpp/pq/sphincsplus/context.h
Normal file
|
|
@ -0,0 +1,28 @@
|
||||||
|
#ifndef SPX_CONTEXT_H
|
||||||
|
#define SPX_CONTEXT_H
|
||||||
|
|
||||||
|
#include <stdint.h>
|
||||||
|
|
||||||
|
#include "params.h"
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
uint8_t pub_seed[SPX_N];
|
||||||
|
uint8_t sk_seed[SPX_N];
|
||||||
|
|
||||||
|
#ifdef SPX_SHA2
|
||||||
|
// sha256 state that absorbed pub_seed
|
||||||
|
uint8_t state_seeded[40];
|
||||||
|
|
||||||
|
# if SPX_SHA512
|
||||||
|
// sha512 state that absorbed pub_seed
|
||||||
|
uint8_t state_seeded_512[72];
|
||||||
|
# endif
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifdef SPX_HARAKA
|
||||||
|
uint64_t tweaked512_rc64[10][8];
|
||||||
|
uint32_t tweaked256_rc32[10][8];
|
||||||
|
#endif
|
||||||
|
} spx_ctx;
|
||||||
|
|
||||||
|
#endif
|
||||||
598
app/src/main/cpp/pq/sphincsplus/fips202.c
Normal file
598
app/src/main/cpp/pq/sphincsplus/fips202.c
Normal file
|
|
@ -0,0 +1,598 @@
|
||||||
|
/* Based on the public domain implementation in
|
||||||
|
* crypto_hash/keccakc512/simple/ from http://bench.cr.yp.to/supercop.html
|
||||||
|
* by Ronny Van Keer
|
||||||
|
* and the public domain "TweetFips202" implementation
|
||||||
|
* from https://twitter.com/tweetfips202
|
||||||
|
* by Gilles Van Assche, Daniel J. Bernstein, and Peter Schwabe */
|
||||||
|
|
||||||
|
#include <stddef.h>
|
||||||
|
#include <stdint.h>
|
||||||
|
|
||||||
|
#include "fips202.h"
|
||||||
|
|
||||||
|
#define NROUNDS 24
|
||||||
|
#define ROL(a, offset) (((a) << (offset)) ^ ((a) >> (64 - (offset))))
|
||||||
|
|
||||||
|
/*************************************************
|
||||||
|
* Name: load64
|
||||||
|
*
|
||||||
|
* Description: Load 8 bytes into uint64_t in little-endian order
|
||||||
|
*
|
||||||
|
* Arguments: - const uint8_t *x: pointer to input byte array
|
||||||
|
*
|
||||||
|
* Returns the loaded 64-bit unsigned integer
|
||||||
|
**************************************************/
|
||||||
|
static uint64_t load64(const uint8_t *x) {
|
||||||
|
uint64_t r = 0;
|
||||||
|
for (size_t i = 0; i < 8; ++i) {
|
||||||
|
r |= (uint64_t)x[i] << 8 * i;
|
||||||
|
}
|
||||||
|
|
||||||
|
return r;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*************************************************
|
||||||
|
* Name: store64
|
||||||
|
*
|
||||||
|
* Description: Store a 64-bit integer to a byte array in little-endian order
|
||||||
|
*
|
||||||
|
* Arguments: - uint8_t *x: pointer to the output byte array
|
||||||
|
* - uint64_t u: input 64-bit unsigned integer
|
||||||
|
**************************************************/
|
||||||
|
static void store64(uint8_t *x, uint64_t u) {
|
||||||
|
for (size_t i = 0; i < 8; ++i) {
|
||||||
|
x[i] = (uint8_t) (u >> 8 * i);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Keccak round constants */
|
||||||
|
static const uint64_t KeccakF_RoundConstants[NROUNDS] = {
|
||||||
|
0x0000000000000001ULL, 0x0000000000008082ULL,
|
||||||
|
0x800000000000808aULL, 0x8000000080008000ULL,
|
||||||
|
0x000000000000808bULL, 0x0000000080000001ULL,
|
||||||
|
0x8000000080008081ULL, 0x8000000000008009ULL,
|
||||||
|
0x000000000000008aULL, 0x0000000000000088ULL,
|
||||||
|
0x0000000080008009ULL, 0x000000008000000aULL,
|
||||||
|
0x000000008000808bULL, 0x800000000000008bULL,
|
||||||
|
0x8000000000008089ULL, 0x8000000000008003ULL,
|
||||||
|
0x8000000000008002ULL, 0x8000000000000080ULL,
|
||||||
|
0x000000000000800aULL, 0x800000008000000aULL,
|
||||||
|
0x8000000080008081ULL, 0x8000000000008080ULL,
|
||||||
|
0x0000000080000001ULL, 0x8000000080008008ULL
|
||||||
|
};
|
||||||
|
|
||||||
|
/*************************************************
|
||||||
|
* Name: KeccakF1600_StatePermute
|
||||||
|
*
|
||||||
|
* Description: The Keccak F1600 Permutation
|
||||||
|
*
|
||||||
|
* Arguments: - uint64_t *state: pointer to input/output Keccak state
|
||||||
|
**************************************************/
|
||||||
|
static void KeccakF1600_StatePermute(uint64_t *state) {
|
||||||
|
int round;
|
||||||
|
|
||||||
|
uint64_t Aba, Abe, Abi, Abo, Abu;
|
||||||
|
uint64_t Aga, Age, Agi, Ago, Agu;
|
||||||
|
uint64_t Aka, Ake, Aki, Ako, Aku;
|
||||||
|
uint64_t Ama, Ame, Ami, Amo, Amu;
|
||||||
|
uint64_t Asa, Ase, Asi, Aso, Asu;
|
||||||
|
uint64_t BCa, BCe, BCi, BCo, BCu;
|
||||||
|
uint64_t Da, De, Di, Do, Du;
|
||||||
|
uint64_t Eba, Ebe, Ebi, Ebo, Ebu;
|
||||||
|
uint64_t Ega, Ege, Egi, Ego, Egu;
|
||||||
|
uint64_t Eka, Eke, Eki, Eko, Eku;
|
||||||
|
uint64_t Ema, Eme, Emi, Emo, Emu;
|
||||||
|
uint64_t Esa, Ese, Esi, Eso, Esu;
|
||||||
|
|
||||||
|
// copyFromState(A, state)
|
||||||
|
Aba = state[0];
|
||||||
|
Abe = state[1];
|
||||||
|
Abi = state[2];
|
||||||
|
Abo = state[3];
|
||||||
|
Abu = state[4];
|
||||||
|
Aga = state[5];
|
||||||
|
Age = state[6];
|
||||||
|
Agi = state[7];
|
||||||
|
Ago = state[8];
|
||||||
|
Agu = state[9];
|
||||||
|
Aka = state[10];
|
||||||
|
Ake = state[11];
|
||||||
|
Aki = state[12];
|
||||||
|
Ako = state[13];
|
||||||
|
Aku = state[14];
|
||||||
|
Ama = state[15];
|
||||||
|
Ame = state[16];
|
||||||
|
Ami = state[17];
|
||||||
|
Amo = state[18];
|
||||||
|
Amu = state[19];
|
||||||
|
Asa = state[20];
|
||||||
|
Ase = state[21];
|
||||||
|
Asi = state[22];
|
||||||
|
Aso = state[23];
|
||||||
|
Asu = state[24];
|
||||||
|
|
||||||
|
for (round = 0; round < NROUNDS; round += 2) {
|
||||||
|
// prepareTheta
|
||||||
|
BCa = Aba ^ Aga ^ Aka ^ Ama ^ Asa;
|
||||||
|
BCe = Abe ^ Age ^ Ake ^ Ame ^ Ase;
|
||||||
|
BCi = Abi ^ Agi ^ Aki ^ Ami ^ Asi;
|
||||||
|
BCo = Abo ^ Ago ^ Ako ^ Amo ^ Aso;
|
||||||
|
BCu = Abu ^ Agu ^ Aku ^ Amu ^ Asu;
|
||||||
|
|
||||||
|
// thetaRhoPiChiIotaPrepareTheta(round , A, E)
|
||||||
|
Da = BCu ^ ROL(BCe, 1);
|
||||||
|
De = BCa ^ ROL(BCi, 1);
|
||||||
|
Di = BCe ^ ROL(BCo, 1);
|
||||||
|
Do = BCi ^ ROL(BCu, 1);
|
||||||
|
Du = BCo ^ ROL(BCa, 1);
|
||||||
|
|
||||||
|
Aba ^= Da;
|
||||||
|
BCa = Aba;
|
||||||
|
Age ^= De;
|
||||||
|
BCe = ROL(Age, 44);
|
||||||
|
Aki ^= Di;
|
||||||
|
BCi = ROL(Aki, 43);
|
||||||
|
Amo ^= Do;
|
||||||
|
BCo = ROL(Amo, 21);
|
||||||
|
Asu ^= Du;
|
||||||
|
BCu = ROL(Asu, 14);
|
||||||
|
Eba = BCa ^ ((~BCe) & BCi);
|
||||||
|
Eba ^= KeccakF_RoundConstants[round];
|
||||||
|
Ebe = BCe ^ ((~BCi) & BCo);
|
||||||
|
Ebi = BCi ^ ((~BCo) & BCu);
|
||||||
|
Ebo = BCo ^ ((~BCu) & BCa);
|
||||||
|
Ebu = BCu ^ ((~BCa) & BCe);
|
||||||
|
|
||||||
|
Abo ^= Do;
|
||||||
|
BCa = ROL(Abo, 28);
|
||||||
|
Agu ^= Du;
|
||||||
|
BCe = ROL(Agu, 20);
|
||||||
|
Aka ^= Da;
|
||||||
|
BCi = ROL(Aka, 3);
|
||||||
|
Ame ^= De;
|
||||||
|
BCo = ROL(Ame, 45);
|
||||||
|
Asi ^= Di;
|
||||||
|
BCu = ROL(Asi, 61);
|
||||||
|
Ega = BCa ^ ((~BCe) & BCi);
|
||||||
|
Ege = BCe ^ ((~BCi) & BCo);
|
||||||
|
Egi = BCi ^ ((~BCo) & BCu);
|
||||||
|
Ego = BCo ^ ((~BCu) & BCa);
|
||||||
|
Egu = BCu ^ ((~BCa) & BCe);
|
||||||
|
|
||||||
|
Abe ^= De;
|
||||||
|
BCa = ROL(Abe, 1);
|
||||||
|
Agi ^= Di;
|
||||||
|
BCe = ROL(Agi, 6);
|
||||||
|
Ako ^= Do;
|
||||||
|
BCi = ROL(Ako, 25);
|
||||||
|
Amu ^= Du;
|
||||||
|
BCo = ROL(Amu, 8);
|
||||||
|
Asa ^= Da;
|
||||||
|
BCu = ROL(Asa, 18);
|
||||||
|
Eka = BCa ^ ((~BCe) & BCi);
|
||||||
|
Eke = BCe ^ ((~BCi) & BCo);
|
||||||
|
Eki = BCi ^ ((~BCo) & BCu);
|
||||||
|
Eko = BCo ^ ((~BCu) & BCa);
|
||||||
|
Eku = BCu ^ ((~BCa) & BCe);
|
||||||
|
|
||||||
|
Abu ^= Du;
|
||||||
|
BCa = ROL(Abu, 27);
|
||||||
|
Aga ^= Da;
|
||||||
|
BCe = ROL(Aga, 36);
|
||||||
|
Ake ^= De;
|
||||||
|
BCi = ROL(Ake, 10);
|
||||||
|
Ami ^= Di;
|
||||||
|
BCo = ROL(Ami, 15);
|
||||||
|
Aso ^= Do;
|
||||||
|
BCu = ROL(Aso, 56);
|
||||||
|
Ema = BCa ^ ((~BCe) & BCi);
|
||||||
|
Eme = BCe ^ ((~BCi) & BCo);
|
||||||
|
Emi = BCi ^ ((~BCo) & BCu);
|
||||||
|
Emo = BCo ^ ((~BCu) & BCa);
|
||||||
|
Emu = BCu ^ ((~BCa) & BCe);
|
||||||
|
|
||||||
|
Abi ^= Di;
|
||||||
|
BCa = ROL(Abi, 62);
|
||||||
|
Ago ^= Do;
|
||||||
|
BCe = ROL(Ago, 55);
|
||||||
|
Aku ^= Du;
|
||||||
|
BCi = ROL(Aku, 39);
|
||||||
|
Ama ^= Da;
|
||||||
|
BCo = ROL(Ama, 41);
|
||||||
|
Ase ^= De;
|
||||||
|
BCu = ROL(Ase, 2);
|
||||||
|
Esa = BCa ^ ((~BCe) & BCi);
|
||||||
|
Ese = BCe ^ ((~BCi) & BCo);
|
||||||
|
Esi = BCi ^ ((~BCo) & BCu);
|
||||||
|
Eso = BCo ^ ((~BCu) & BCa);
|
||||||
|
Esu = BCu ^ ((~BCa) & BCe);
|
||||||
|
|
||||||
|
// prepareTheta
|
||||||
|
BCa = Eba ^ Ega ^ Eka ^ Ema ^ Esa;
|
||||||
|
BCe = Ebe ^ Ege ^ Eke ^ Eme ^ Ese;
|
||||||
|
BCi = Ebi ^ Egi ^ Eki ^ Emi ^ Esi;
|
||||||
|
BCo = Ebo ^ Ego ^ Eko ^ Emo ^ Eso;
|
||||||
|
BCu = Ebu ^ Egu ^ Eku ^ Emu ^ Esu;
|
||||||
|
|
||||||
|
// thetaRhoPiChiIotaPrepareTheta(round+1, E, A)
|
||||||
|
Da = BCu ^ ROL(BCe, 1);
|
||||||
|
De = BCa ^ ROL(BCi, 1);
|
||||||
|
Di = BCe ^ ROL(BCo, 1);
|
||||||
|
Do = BCi ^ ROL(BCu, 1);
|
||||||
|
Du = BCo ^ ROL(BCa, 1);
|
||||||
|
|
||||||
|
Eba ^= Da;
|
||||||
|
BCa = Eba;
|
||||||
|
Ege ^= De;
|
||||||
|
BCe = ROL(Ege, 44);
|
||||||
|
Eki ^= Di;
|
||||||
|
BCi = ROL(Eki, 43);
|
||||||
|
Emo ^= Do;
|
||||||
|
BCo = ROL(Emo, 21);
|
||||||
|
Esu ^= Du;
|
||||||
|
BCu = ROL(Esu, 14);
|
||||||
|
Aba = BCa ^ ((~BCe) & BCi);
|
||||||
|
Aba ^= KeccakF_RoundConstants[round + 1];
|
||||||
|
Abe = BCe ^ ((~BCi) & BCo);
|
||||||
|
Abi = BCi ^ ((~BCo) & BCu);
|
||||||
|
Abo = BCo ^ ((~BCu) & BCa);
|
||||||
|
Abu = BCu ^ ((~BCa) & BCe);
|
||||||
|
|
||||||
|
Ebo ^= Do;
|
||||||
|
BCa = ROL(Ebo, 28);
|
||||||
|
Egu ^= Du;
|
||||||
|
BCe = ROL(Egu, 20);
|
||||||
|
Eka ^= Da;
|
||||||
|
BCi = ROL(Eka, 3);
|
||||||
|
Eme ^= De;
|
||||||
|
BCo = ROL(Eme, 45);
|
||||||
|
Esi ^= Di;
|
||||||
|
BCu = ROL(Esi, 61);
|
||||||
|
Aga = BCa ^ ((~BCe) & BCi);
|
||||||
|
Age = BCe ^ ((~BCi) & BCo);
|
||||||
|
Agi = BCi ^ ((~BCo) & BCu);
|
||||||
|
Ago = BCo ^ ((~BCu) & BCa);
|
||||||
|
Agu = BCu ^ ((~BCa) & BCe);
|
||||||
|
|
||||||
|
Ebe ^= De;
|
||||||
|
BCa = ROL(Ebe, 1);
|
||||||
|
Egi ^= Di;
|
||||||
|
BCe = ROL(Egi, 6);
|
||||||
|
Eko ^= Do;
|
||||||
|
BCi = ROL(Eko, 25);
|
||||||
|
Emu ^= Du;
|
||||||
|
BCo = ROL(Emu, 8);
|
||||||
|
Esa ^= Da;
|
||||||
|
BCu = ROL(Esa, 18);
|
||||||
|
Aka = BCa ^ ((~BCe) & BCi);
|
||||||
|
Ake = BCe ^ ((~BCi) & BCo);
|
||||||
|
Aki = BCi ^ ((~BCo) & BCu);
|
||||||
|
Ako = BCo ^ ((~BCu) & BCa);
|
||||||
|
Aku = BCu ^ ((~BCa) & BCe);
|
||||||
|
|
||||||
|
Ebu ^= Du;
|
||||||
|
BCa = ROL(Ebu, 27);
|
||||||
|
Ega ^= Da;
|
||||||
|
BCe = ROL(Ega, 36);
|
||||||
|
Eke ^= De;
|
||||||
|
BCi = ROL(Eke, 10);
|
||||||
|
Emi ^= Di;
|
||||||
|
BCo = ROL(Emi, 15);
|
||||||
|
Eso ^= Do;
|
||||||
|
BCu = ROL(Eso, 56);
|
||||||
|
Ama = BCa ^ ((~BCe) & BCi);
|
||||||
|
Ame = BCe ^ ((~BCi) & BCo);
|
||||||
|
Ami = BCi ^ ((~BCo) & BCu);
|
||||||
|
Amo = BCo ^ ((~BCu) & BCa);
|
||||||
|
Amu = BCu ^ ((~BCa) & BCe);
|
||||||
|
|
||||||
|
Ebi ^= Di;
|
||||||
|
BCa = ROL(Ebi, 62);
|
||||||
|
Ego ^= Do;
|
||||||
|
BCe = ROL(Ego, 55);
|
||||||
|
Eku ^= Du;
|
||||||
|
BCi = ROL(Eku, 39);
|
||||||
|
Ema ^= Da;
|
||||||
|
BCo = ROL(Ema, 41);
|
||||||
|
Ese ^= De;
|
||||||
|
BCu = ROL(Ese, 2);
|
||||||
|
Asa = BCa ^ ((~BCe) & BCi);
|
||||||
|
Ase = BCe ^ ((~BCi) & BCo);
|
||||||
|
Asi = BCi ^ ((~BCo) & BCu);
|
||||||
|
Aso = BCo ^ ((~BCu) & BCa);
|
||||||
|
Asu = BCu ^ ((~BCa) & BCe);
|
||||||
|
}
|
||||||
|
|
||||||
|
// copyToState(state, A)
|
||||||
|
state[0] = Aba;
|
||||||
|
state[1] = Abe;
|
||||||
|
state[2] = Abi;
|
||||||
|
state[3] = Abo;
|
||||||
|
state[4] = Abu;
|
||||||
|
state[5] = Aga;
|
||||||
|
state[6] = Age;
|
||||||
|
state[7] = Agi;
|
||||||
|
state[8] = Ago;
|
||||||
|
state[9] = Agu;
|
||||||
|
state[10] = Aka;
|
||||||
|
state[11] = Ake;
|
||||||
|
state[12] = Aki;
|
||||||
|
state[13] = Ako;
|
||||||
|
state[14] = Aku;
|
||||||
|
state[15] = Ama;
|
||||||
|
state[16] = Ame;
|
||||||
|
state[17] = Ami;
|
||||||
|
state[18] = Amo;
|
||||||
|
state[19] = Amu;
|
||||||
|
state[20] = Asa;
|
||||||
|
state[21] = Ase;
|
||||||
|
state[22] = Asi;
|
||||||
|
state[23] = Aso;
|
||||||
|
state[24] = Asu;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*************************************************
|
||||||
|
* Name: keccak_absorb
|
||||||
|
*
|
||||||
|
* Description: Absorb step of Keccak;
|
||||||
|
* non-incremental, starts by zeroeing the state.
|
||||||
|
*
|
||||||
|
* Arguments: - uint64_t *s: pointer to (uninitialized) output Keccak state
|
||||||
|
* - uint32_t r: rate in bytes (e.g., 168 for SHAKE128)
|
||||||
|
* - const uint8_t *m: pointer to input to be absorbed into s
|
||||||
|
* - size_t mlen: length of input in bytes
|
||||||
|
* - uint8_t p: domain-separation byte for different
|
||||||
|
* Keccak-derived functions
|
||||||
|
**************************************************/
|
||||||
|
static void keccak_absorb(uint64_t *s, uint32_t r, const uint8_t *m,
|
||||||
|
size_t mlen, uint8_t p) {
|
||||||
|
size_t i;
|
||||||
|
uint8_t t[200];
|
||||||
|
|
||||||
|
/* Zero state */
|
||||||
|
for (i = 0; i < 25; ++i) {
|
||||||
|
s[i] = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
while (mlen >= r) {
|
||||||
|
for (i = 0; i < r / 8; ++i) {
|
||||||
|
s[i] ^= load64(m + 8 * i);
|
||||||
|
}
|
||||||
|
|
||||||
|
KeccakF1600_StatePermute(s);
|
||||||
|
mlen -= r;
|
||||||
|
m += r;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (i = 0; i < r; ++i) {
|
||||||
|
t[i] = 0;
|
||||||
|
}
|
||||||
|
for (i = 0; i < mlen; ++i) {
|
||||||
|
t[i] = m[i];
|
||||||
|
}
|
||||||
|
t[i] = p;
|
||||||
|
t[r - 1] |= 128;
|
||||||
|
for (i = 0; i < r / 8; ++i) {
|
||||||
|
s[i] ^= load64(t + 8 * i);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/*************************************************
|
||||||
|
* Name: keccak_squeezeblocks
|
||||||
|
*
|
||||||
|
* Description: Squeeze step of Keccak. Squeezes full blocks of r bytes each.
|
||||||
|
* Modifies the state. Can be called multiple times to keep
|
||||||
|
* squeezing, i.e., is incremental.
|
||||||
|
*
|
||||||
|
* Arguments: - uint8_t *h: pointer to output blocks
|
||||||
|
* - size_t nblocks: number of blocks to be
|
||||||
|
* squeezed (written to h)
|
||||||
|
* - uint64_t *s: pointer to input/output Keccak state
|
||||||
|
* - uint32_t r: rate in bytes (e.g., 168 for SHAKE128)
|
||||||
|
**************************************************/
|
||||||
|
static void keccak_squeezeblocks(uint8_t *h, size_t nblocks,
|
||||||
|
uint64_t *s, uint32_t r) {
|
||||||
|
while (nblocks > 0) {
|
||||||
|
KeccakF1600_StatePermute(s);
|
||||||
|
for (size_t i = 0; i < (r >> 3); i++) {
|
||||||
|
store64(h + 8 * i, s[i]);
|
||||||
|
}
|
||||||
|
h += r;
|
||||||
|
nblocks--;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/*************************************************
|
||||||
|
* Name: keccak_inc_init
|
||||||
|
*
|
||||||
|
* Description: Initializes the incremental Keccak state to zero.
|
||||||
|
*
|
||||||
|
* Arguments: - uint64_t *s_inc: pointer to input/output incremental state
|
||||||
|
* First 25 values represent Keccak state.
|
||||||
|
* 26th value represents either the number of absorbed bytes
|
||||||
|
* that have not been permuted, or not-yet-squeezed bytes.
|
||||||
|
**************************************************/
|
||||||
|
static void keccak_inc_init(uint64_t *s_inc) {
|
||||||
|
size_t i;
|
||||||
|
|
||||||
|
for (i = 0; i < 25; ++i) {
|
||||||
|
s_inc[i] = 0;
|
||||||
|
}
|
||||||
|
s_inc[25] = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*************************************************
|
||||||
|
* Name: keccak_inc_absorb
|
||||||
|
*
|
||||||
|
* Description: Incremental keccak absorb
|
||||||
|
* Preceded by keccak_inc_init, succeeded by keccak_inc_finalize
|
||||||
|
*
|
||||||
|
* Arguments: - uint64_t *s_inc: pointer to input/output incremental state
|
||||||
|
* First 25 values represent Keccak state.
|
||||||
|
* 26th value represents either the number of absorbed bytes
|
||||||
|
* that have not been permuted, or not-yet-squeezed bytes.
|
||||||
|
* - uint32_t r: rate in bytes (e.g., 168 for SHAKE128)
|
||||||
|
* - const uint8_t *m: pointer to input to be absorbed into s
|
||||||
|
* - size_t mlen: length of input in bytes
|
||||||
|
**************************************************/
|
||||||
|
static void keccak_inc_absorb(uint64_t *s_inc, uint32_t r, const uint8_t *m,
|
||||||
|
size_t mlen) {
|
||||||
|
size_t i;
|
||||||
|
|
||||||
|
/* Recall that s_inc[25] is the non-absorbed bytes xored into the state */
|
||||||
|
while (mlen + s_inc[25] >= r) {
|
||||||
|
for (i = 0; i < r - s_inc[25]; i++) {
|
||||||
|
/* Take the i'th byte from message
|
||||||
|
xor with the s_inc[25] + i'th byte of the state; little-endian */
|
||||||
|
s_inc[(s_inc[25] + i) >> 3] ^= (uint64_t)m[i] << (8 * ((s_inc[25] + i) & 0x07));
|
||||||
|
}
|
||||||
|
mlen -= (size_t)(r - s_inc[25]);
|
||||||
|
m += r - s_inc[25];
|
||||||
|
s_inc[25] = 0;
|
||||||
|
|
||||||
|
KeccakF1600_StatePermute(s_inc);
|
||||||
|
}
|
||||||
|
|
||||||
|
for (i = 0; i < mlen; i++) {
|
||||||
|
s_inc[(s_inc[25] + i) >> 3] ^= (uint64_t)m[i] << (8 * ((s_inc[25] + i) & 0x07));
|
||||||
|
}
|
||||||
|
s_inc[25] += mlen;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*************************************************
|
||||||
|
* Name: keccak_inc_finalize
|
||||||
|
*
|
||||||
|
* Description: Finalizes Keccak absorb phase, prepares for squeezing
|
||||||
|
*
|
||||||
|
* Arguments: - uint64_t *s_inc: pointer to input/output incremental state
|
||||||
|
* First 25 values represent Keccak state.
|
||||||
|
* 26th value represents either the number of absorbed bytes
|
||||||
|
* that have not been permuted, or not-yet-squeezed bytes.
|
||||||
|
* - uint32_t r: rate in bytes (e.g., 168 for SHAKE128)
|
||||||
|
* - uint8_t p: domain-separation byte for different
|
||||||
|
* Keccak-derived functions
|
||||||
|
**************************************************/
|
||||||
|
static void keccak_inc_finalize(uint64_t *s_inc, uint32_t r, uint8_t p) {
|
||||||
|
/* After keccak_inc_absorb, we are guaranteed that s_inc[25] < r,
|
||||||
|
so we can always use one more byte for p in the current state. */
|
||||||
|
s_inc[s_inc[25] >> 3] ^= (uint64_t)p << (8 * (s_inc[25] & 0x07));
|
||||||
|
s_inc[(r - 1) >> 3] ^= (uint64_t)128 << (8 * ((r - 1) & 0x07));
|
||||||
|
s_inc[25] = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*************************************************
|
||||||
|
* Name: keccak_inc_squeeze
|
||||||
|
*
|
||||||
|
* Description: Incremental Keccak squeeze; can be called on byte-level
|
||||||
|
*
|
||||||
|
* Arguments: - uint8_t *h: pointer to output bytes
|
||||||
|
* - size_t outlen: number of bytes to be squeezed
|
||||||
|
* - uint64_t *s_inc: pointer to input/output incremental state
|
||||||
|
* First 25 values represent Keccak state.
|
||||||
|
* 26th value represents either the number of absorbed bytes
|
||||||
|
* that have not been permuted, or not-yet-squeezed bytes.
|
||||||
|
* - uint32_t r: rate in bytes (e.g., 168 for SHAKE128)
|
||||||
|
**************************************************/
|
||||||
|
static void keccak_inc_squeeze(uint8_t *h, size_t outlen,
|
||||||
|
uint64_t *s_inc, uint32_t r) {
|
||||||
|
size_t i;
|
||||||
|
|
||||||
|
/* First consume any bytes we still have sitting around */
|
||||||
|
for (i = 0; i < outlen && i < s_inc[25]; i++) {
|
||||||
|
/* There are s_inc[25] bytes left, so r - s_inc[25] is the first
|
||||||
|
available byte. We consume from there, i.e., up to r. */
|
||||||
|
h[i] = (uint8_t)(s_inc[(r - s_inc[25] + i) >> 3] >> (8 * ((r - s_inc[25] + i) & 0x07)));
|
||||||
|
}
|
||||||
|
h += i;
|
||||||
|
outlen -= i;
|
||||||
|
s_inc[25] -= i;
|
||||||
|
|
||||||
|
/* Then squeeze the remaining necessary blocks */
|
||||||
|
while (outlen > 0) {
|
||||||
|
KeccakF1600_StatePermute(s_inc);
|
||||||
|
|
||||||
|
for (i = 0; i < outlen && i < r; i++) {
|
||||||
|
h[i] = (uint8_t)(s_inc[i >> 3] >> (8 * (i & 0x07)));
|
||||||
|
}
|
||||||
|
h += i;
|
||||||
|
outlen -= i;
|
||||||
|
s_inc[25] = r - i;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void shake256_inc_init(uint64_t *s_inc) {
|
||||||
|
keccak_inc_init(s_inc);
|
||||||
|
}
|
||||||
|
|
||||||
|
void shake256_inc_absorb(uint64_t *s_inc, const uint8_t *input, size_t inlen) {
|
||||||
|
keccak_inc_absorb(s_inc, SHAKE256_RATE, input, inlen);
|
||||||
|
}
|
||||||
|
|
||||||
|
void shake256_inc_finalize(uint64_t *s_inc) {
|
||||||
|
keccak_inc_finalize(s_inc, SHAKE256_RATE, 0x1F);
|
||||||
|
}
|
||||||
|
|
||||||
|
void shake256_inc_squeeze(uint8_t *output, size_t outlen, uint64_t *s_inc) {
|
||||||
|
keccak_inc_squeeze(output, outlen, s_inc, SHAKE256_RATE);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*************************************************
|
||||||
|
* Name: shake256_absorb
|
||||||
|
*
|
||||||
|
* Description: Absorb step of the SHAKE256 XOF.
|
||||||
|
* non-incremental, starts by zeroeing the state.
|
||||||
|
*
|
||||||
|
* Arguments: - uint64_t *s: pointer to (uninitialized) output Keccak state
|
||||||
|
* - const uint8_t *input: pointer to input to be absorbed
|
||||||
|
* into s
|
||||||
|
* - size_t inlen: length of input in bytes
|
||||||
|
**************************************************/
|
||||||
|
void shake256_absorb(uint64_t *s, const uint8_t *input, size_t inlen) {
|
||||||
|
keccak_absorb(s, SHAKE256_RATE, input, inlen, 0x1F);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*************************************************
|
||||||
|
* Name: shake256_squeezeblocks
|
||||||
|
*
|
||||||
|
* Description: Squeeze step of SHAKE256 XOF. Squeezes full blocks of
|
||||||
|
* SHAKE256_RATE bytes each. Modifies the state. Can be called
|
||||||
|
* multiple times to keep squeezing, i.e., is incremental.
|
||||||
|
*
|
||||||
|
* Arguments: - uint8_t *output: pointer to output blocks
|
||||||
|
* - size_t nblocks: number of blocks to be squeezed
|
||||||
|
* (written to output)
|
||||||
|
* - uint64_t *s: pointer to input/output Keccak state
|
||||||
|
**************************************************/
|
||||||
|
void shake256_squeezeblocks(uint8_t *output, size_t nblocks, uint64_t *s) {
|
||||||
|
keccak_squeezeblocks(output, nblocks, s, SHAKE256_RATE);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*************************************************
|
||||||
|
* Name: shake256
|
||||||
|
*
|
||||||
|
* Description: SHAKE256 XOF with non-incremental API
|
||||||
|
*
|
||||||
|
* Arguments: - uint8_t *output: pointer to output
|
||||||
|
* - size_t outlen: requested output length in bytes
|
||||||
|
* - const uint8_t *input: pointer to input
|
||||||
|
* - size_t inlen: length of input in bytes
|
||||||
|
**************************************************/
|
||||||
|
void shake256(uint8_t *output, size_t outlen,
|
||||||
|
const uint8_t *input, size_t inlen) {
|
||||||
|
size_t nblocks = outlen / SHAKE256_RATE;
|
||||||
|
uint8_t t[SHAKE256_RATE];
|
||||||
|
uint64_t s[25];
|
||||||
|
|
||||||
|
shake256_absorb(s, input, inlen);
|
||||||
|
shake256_squeezeblocks(output, nblocks, s);
|
||||||
|
|
||||||
|
output += nblocks * SHAKE256_RATE;
|
||||||
|
outlen -= nblocks * SHAKE256_RATE;
|
||||||
|
|
||||||
|
if (outlen) {
|
||||||
|
shake256_squeezeblocks(t, 1, s);
|
||||||
|
for (size_t i = 0; i < outlen; ++i) {
|
||||||
|
output[i] = t[i];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
47
app/src/main/cpp/pq/sphincsplus/fips202.h
Normal file
47
app/src/main/cpp/pq/sphincsplus/fips202.h
Normal file
|
|
@ -0,0 +1,47 @@
|
||||||
|
#ifndef SPX_FIPS202_H
|
||||||
|
#define SPX_FIPS202_H
|
||||||
|
|
||||||
|
#include <stddef.h>
|
||||||
|
#include <stdint.h>
|
||||||
|
|
||||||
|
#define SHAKE128_RATE 168
|
||||||
|
#define SHAKE256_RATE 136
|
||||||
|
#define SHA3_256_RATE 136
|
||||||
|
#define SHA3_512_RATE 72
|
||||||
|
|
||||||
|
void shake128_absorb(uint64_t *s, const uint8_t *input, size_t inlen);
|
||||||
|
|
||||||
|
void shake128_squeezeblocks(uint8_t *output, size_t nblocks, uint64_t *s);
|
||||||
|
|
||||||
|
void shake128_inc_init(uint64_t *s_inc);
|
||||||
|
void shake128_inc_absorb(uint64_t *s_inc, const uint8_t *input, size_t inlen);
|
||||||
|
void shake128_inc_finalize(uint64_t *s_inc);
|
||||||
|
void shake128_inc_squeeze(uint8_t *output, size_t outlen, uint64_t *s_inc);
|
||||||
|
|
||||||
|
void shake256_absorb(uint64_t *s, const uint8_t *input, size_t inlen);
|
||||||
|
void shake256_squeezeblocks(uint8_t *output, size_t nblocks, uint64_t *s);
|
||||||
|
|
||||||
|
void shake256_inc_init(uint64_t *s_inc);
|
||||||
|
void shake256_inc_absorb(uint64_t *s_inc, const uint8_t *input, size_t inlen);
|
||||||
|
void shake256_inc_finalize(uint64_t *s_inc);
|
||||||
|
void shake256_inc_squeeze(uint8_t *output, size_t outlen, uint64_t *s_inc);
|
||||||
|
|
||||||
|
void shake128(uint8_t *output, size_t outlen,
|
||||||
|
const uint8_t *input, size_t inlen);
|
||||||
|
|
||||||
|
void shake256(uint8_t *output, size_t outlen,
|
||||||
|
const uint8_t *input, size_t inlen);
|
||||||
|
|
||||||
|
void sha3_256_inc_init(uint64_t *s_inc);
|
||||||
|
void sha3_256_inc_absorb(uint64_t *s_inc, const uint8_t *input, size_t inlen);
|
||||||
|
void sha3_256_inc_finalize(uint8_t *output, uint64_t *s_inc);
|
||||||
|
|
||||||
|
void sha3_256(uint8_t *output, const uint8_t *input, size_t inlen);
|
||||||
|
|
||||||
|
void sha3_512_inc_init(uint64_t *s_inc);
|
||||||
|
void sha3_512_inc_absorb(uint64_t *s_inc, const uint8_t *input, size_t inlen);
|
||||||
|
void sha3_512_inc_finalize(uint8_t *output, uint64_t *s_inc);
|
||||||
|
|
||||||
|
void sha3_512(uint8_t *output, const uint8_t *input, size_t inlen);
|
||||||
|
|
||||||
|
#endif
|
||||||
161
app/src/main/cpp/pq/sphincsplus/fors.c
Normal file
161
app/src/main/cpp/pq/sphincsplus/fors.c
Normal file
|
|
@ -0,0 +1,161 @@
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <stdint.h>
|
||||||
|
#include <string.h>
|
||||||
|
|
||||||
|
#include "fors.h"
|
||||||
|
#include "utils.h"
|
||||||
|
#include "utilsx1.h"
|
||||||
|
#include "hash.h"
|
||||||
|
#include "thash.h"
|
||||||
|
#include "address.h"
|
||||||
|
|
||||||
|
static void fors_gen_sk(unsigned char *sk, const spx_ctx *ctx,
|
||||||
|
uint32_t fors_leaf_addr[8])
|
||||||
|
{
|
||||||
|
prf_addr(sk, ctx, fors_leaf_addr);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void fors_sk_to_leaf(unsigned char *leaf, const unsigned char *sk,
|
||||||
|
const spx_ctx *ctx,
|
||||||
|
uint32_t fors_leaf_addr[8])
|
||||||
|
{
|
||||||
|
thash(leaf, sk, 1, ctx, fors_leaf_addr);
|
||||||
|
}
|
||||||
|
|
||||||
|
struct fors_gen_leaf_info {
|
||||||
|
uint32_t leaf_addrx[8];
|
||||||
|
};
|
||||||
|
|
||||||
|
static void fors_gen_leafx1(unsigned char *leaf,
|
||||||
|
const spx_ctx *ctx,
|
||||||
|
uint32_t addr_idx, void *info)
|
||||||
|
{
|
||||||
|
struct fors_gen_leaf_info *fors_info = info;
|
||||||
|
uint32_t *fors_leaf_addr = fors_info->leaf_addrx;
|
||||||
|
|
||||||
|
/* Only set the parts that the caller doesn't set */
|
||||||
|
set_tree_index(fors_leaf_addr, addr_idx);
|
||||||
|
set_type(fors_leaf_addr, SPX_ADDR_TYPE_FORSPRF);
|
||||||
|
fors_gen_sk(leaf, ctx, fors_leaf_addr);
|
||||||
|
|
||||||
|
set_type(fors_leaf_addr, SPX_ADDR_TYPE_FORSTREE);
|
||||||
|
fors_sk_to_leaf(leaf, leaf,
|
||||||
|
ctx, 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)) & 1u) << 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 fors_sign(unsigned char *sig, unsigned char *pk,
|
||||||
|
const unsigned char *m,
|
||||||
|
const spx_ctx *ctx,
|
||||||
|
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};
|
||||||
|
struct fors_gen_leaf_info fors_info = {0};
|
||||||
|
uint32_t *fors_leaf_addr = fors_info.leaf_addrx;
|
||||||
|
uint32_t fors_pk_addr[8] = {0};
|
||||||
|
uint32_t idx_offset;
|
||||||
|
unsigned int i;
|
||||||
|
|
||||||
|
copy_keypair_addr(fors_tree_addr, fors_addr);
|
||||||
|
copy_keypair_addr(fors_leaf_addr, fors_addr);
|
||||||
|
|
||||||
|
copy_keypair_addr(fors_pk_addr, fors_addr);
|
||||||
|
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);
|
||||||
|
|
||||||
|
set_tree_height(fors_tree_addr, 0);
|
||||||
|
set_tree_index(fors_tree_addr, indices[i] + idx_offset);
|
||||||
|
set_type(fors_tree_addr, SPX_ADDR_TYPE_FORSPRF);
|
||||||
|
|
||||||
|
/* Include the secret key part that produces the selected leaf node. */
|
||||||
|
fors_gen_sk(sig, ctx, fors_tree_addr);
|
||||||
|
set_type(fors_tree_addr, SPX_ADDR_TYPE_FORSTREE);
|
||||||
|
sig += SPX_N;
|
||||||
|
|
||||||
|
/* Compute the authentication path for this leaf node. */
|
||||||
|
treehashx1(roots + i*SPX_N, sig, ctx,
|
||||||
|
indices[i], idx_offset, SPX_FORS_HEIGHT, fors_gen_leafx1,
|
||||||
|
fors_tree_addr, &fors_info);
|
||||||
|
|
||||||
|
sig += SPX_N * SPX_FORS_HEIGHT;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Hash horizontally across all tree roots to derive the public key. */
|
||||||
|
thash(pk, roots, SPX_FORS_TREES, ctx, 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 fors_pk_from_sig(unsigned char *pk,
|
||||||
|
const unsigned char *sig, const unsigned char *m,
|
||||||
|
const spx_ctx* ctx,
|
||||||
|
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;
|
||||||
|
|
||||||
|
copy_keypair_addr(fors_tree_addr, fors_addr);
|
||||||
|
copy_keypair_addr(fors_pk_addr, fors_addr);
|
||||||
|
|
||||||
|
set_type(fors_tree_addr, SPX_ADDR_TYPE_FORSTREE);
|
||||||
|
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);
|
||||||
|
|
||||||
|
set_tree_height(fors_tree_addr, 0);
|
||||||
|
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, ctx, fors_tree_addr);
|
||||||
|
sig += SPX_N;
|
||||||
|
|
||||||
|
/* Derive the corresponding root node of this tree. */
|
||||||
|
compute_root(roots + i*SPX_N, leaf, indices[i], idx_offset,
|
||||||
|
sig, SPX_FORS_HEIGHT, ctx, fors_tree_addr);
|
||||||
|
sig += SPX_N * SPX_FORS_HEIGHT;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Hash horizontally across all tree roots to derive the public key. */
|
||||||
|
thash(pk, roots, SPX_FORS_TREES, ctx, fors_pk_addr);
|
||||||
|
}
|
||||||
32
app/src/main/cpp/pq/sphincsplus/fors.h
Normal file
32
app/src/main/cpp/pq/sphincsplus/fors.h
Normal file
|
|
@ -0,0 +1,32 @@
|
||||||
|
#ifndef SPX_FORS_H
|
||||||
|
#define SPX_FORS_H
|
||||||
|
|
||||||
|
#include <stdint.h>
|
||||||
|
|
||||||
|
#include "params.h"
|
||||||
|
#include "context.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.
|
||||||
|
*/
|
||||||
|
#define fors_sign SPX_NAMESPACE(fors_sign)
|
||||||
|
void fors_sign(unsigned char *sig, unsigned char *pk,
|
||||||
|
const unsigned char *m,
|
||||||
|
const spx_ctx* ctx,
|
||||||
|
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.
|
||||||
|
*/
|
||||||
|
#define fors_pk_from_sig SPX_NAMESPACE(fors_pk_from_sig)
|
||||||
|
void fors_pk_from_sig(unsigned char *pk,
|
||||||
|
const unsigned char *sig, const unsigned char *m,
|
||||||
|
const spx_ctx* ctx,
|
||||||
|
const uint32_t fors_addr[8]);
|
||||||
|
|
||||||
|
#endif
|
||||||
27
app/src/main/cpp/pq/sphincsplus/hash.h
Normal file
27
app/src/main/cpp/pq/sphincsplus/hash.h
Normal file
|
|
@ -0,0 +1,27 @@
|
||||||
|
#ifndef SPX_HASH_H
|
||||||
|
#define SPX_HASH_H
|
||||||
|
|
||||||
|
#include <stdint.h>
|
||||||
|
#include "context.h"
|
||||||
|
#include "params.h"
|
||||||
|
|
||||||
|
#define initialize_hash_function SPX_NAMESPACE(initialize_hash_function)
|
||||||
|
void initialize_hash_function(spx_ctx *ctx);
|
||||||
|
|
||||||
|
#define prf_addr SPX_NAMESPACE(prf_addr)
|
||||||
|
void prf_addr(unsigned char *out, const spx_ctx *ctx,
|
||||||
|
const uint32_t addr[8]);
|
||||||
|
|
||||||
|
#define gen_message_random SPX_NAMESPACE(gen_message_random)
|
||||||
|
void gen_message_random(unsigned char *R, const unsigned char *sk_prf,
|
||||||
|
const unsigned char *optrand,
|
||||||
|
const unsigned char *m, unsigned long long mlen,
|
||||||
|
const spx_ctx *ctx);
|
||||||
|
|
||||||
|
#define hash_message SPX_NAMESPACE(hash_message)
|
||||||
|
void hash_message(unsigned char *digest, uint64_t *tree, uint32_t *leaf_idx,
|
||||||
|
const unsigned char *R, const unsigned char *pk,
|
||||||
|
const unsigned char *m, unsigned long long mlen,
|
||||||
|
const spx_ctx *ctx);
|
||||||
|
|
||||||
|
#endif
|
||||||
197
app/src/main/cpp/pq/sphincsplus/hash_sha2.c
Normal file
197
app/src/main/cpp/pq/sphincsplus/hash_sha2.c
Normal file
|
|
@ -0,0 +1,197 @@
|
||||||
|
#include <stdint.h>
|
||||||
|
#include <string.h>
|
||||||
|
|
||||||
|
#include "address.h"
|
||||||
|
#include "utils.h"
|
||||||
|
#include "params.h"
|
||||||
|
#include "hash.h"
|
||||||
|
#include "sha2.h"
|
||||||
|
|
||||||
|
#if SPX_N >= 24
|
||||||
|
#define SPX_SHAX_OUTPUT_BYTES SPX_SHA512_OUTPUT_BYTES
|
||||||
|
#define SPX_SHAX_BLOCK_BYTES SPX_SHA512_BLOCK_BYTES
|
||||||
|
#define shaX_inc_init sha512_inc_init
|
||||||
|
#define shaX_inc_blocks sha512_inc_blocks
|
||||||
|
#define shaX_inc_finalize sha512_inc_finalize
|
||||||
|
#define shaX sha512
|
||||||
|
#define mgf1_X mgf1_512
|
||||||
|
#else
|
||||||
|
#define SPX_SHAX_OUTPUT_BYTES SPX_SHA256_OUTPUT_BYTES
|
||||||
|
#define SPX_SHAX_BLOCK_BYTES SPX_SHA256_BLOCK_BYTES
|
||||||
|
#define shaX_inc_init sha256_inc_init
|
||||||
|
#define shaX_inc_blocks sha256_inc_blocks
|
||||||
|
#define shaX_inc_finalize sha256_inc_finalize
|
||||||
|
#define shaX sha256
|
||||||
|
#define mgf1_X mgf1_256
|
||||||
|
#endif
|
||||||
|
|
||||||
|
|
||||||
|
/* For SHA, there is no immediate reason to initialize at the start,
|
||||||
|
so this function is an empty operation. */
|
||||||
|
void initialize_hash_function(spx_ctx *ctx)
|
||||||
|
{
|
||||||
|
seed_state(ctx);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Computes PRF(pk_seed, sk_seed, addr).
|
||||||
|
*/
|
||||||
|
void prf_addr(unsigned char *out, const spx_ctx *ctx,
|
||||||
|
const uint32_t addr[8])
|
||||||
|
{
|
||||||
|
uint8_t sha2_state[40];
|
||||||
|
unsigned char buf[SPX_SHA256_ADDR_BYTES + SPX_N];
|
||||||
|
unsigned char outbuf[SPX_SHA256_OUTPUT_BYTES];
|
||||||
|
|
||||||
|
/* Retrieve precomputed state containing pub_seed */
|
||||||
|
memcpy(sha2_state, ctx->state_seeded, 40 * sizeof(uint8_t));
|
||||||
|
|
||||||
|
/* Remainder: ADDR^c ‖ SK.seed */
|
||||||
|
memcpy(buf, addr, SPX_SHA256_ADDR_BYTES);
|
||||||
|
memcpy(buf + SPX_SHA256_ADDR_BYTES, ctx->sk_seed, SPX_N);
|
||||||
|
|
||||||
|
sha256_inc_finalize(outbuf, sha2_state, buf, SPX_SHA256_ADDR_BYTES + SPX_N);
|
||||||
|
|
||||||
|
memcpy(out, outbuf, SPX_N);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Computes the message-dependent randomness R, using a secret seed as a key
|
||||||
|
* for HMAC, and an optional randomization value prefixed to the message.
|
||||||
|
* This requires m to have at least SPX_SHAX_BLOCK_BYTES + SPX_N space
|
||||||
|
* available in front of the pointer, i.e. before the message to use for the
|
||||||
|
* prefix. This is necessary to prevent having to move the message around (and
|
||||||
|
* allocate memory for it).
|
||||||
|
*/
|
||||||
|
void gen_message_random(unsigned char *R, const unsigned char *sk_prf,
|
||||||
|
const unsigned char *optrand,
|
||||||
|
const unsigned char *m, unsigned long long mlen,
|
||||||
|
const spx_ctx *ctx)
|
||||||
|
{
|
||||||
|
(void)ctx;
|
||||||
|
|
||||||
|
unsigned char buf[SPX_SHAX_BLOCK_BYTES + SPX_SHAX_OUTPUT_BYTES];
|
||||||
|
uint8_t state[8 + SPX_SHAX_OUTPUT_BYTES];
|
||||||
|
int i;
|
||||||
|
|
||||||
|
#if SPX_N > SPX_SHAX_BLOCK_BYTES
|
||||||
|
#error "Currently only supports SPX_N of at most SPX_SHAX_BLOCK_BYTES"
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/* This implements HMAC-SHA */
|
||||||
|
for (i = 0; i < SPX_N; i++) {
|
||||||
|
buf[i] = 0x36 ^ sk_prf[i];
|
||||||
|
}
|
||||||
|
memset(buf + SPX_N, 0x36, SPX_SHAX_BLOCK_BYTES - SPX_N);
|
||||||
|
|
||||||
|
shaX_inc_init(state);
|
||||||
|
shaX_inc_blocks(state, buf, 1);
|
||||||
|
|
||||||
|
memcpy(buf, optrand, SPX_N);
|
||||||
|
|
||||||
|
/* If optrand + message cannot fill up an entire block */
|
||||||
|
if (SPX_N + mlen < SPX_SHAX_BLOCK_BYTES) {
|
||||||
|
memcpy(buf + SPX_N, m, mlen);
|
||||||
|
shaX_inc_finalize(buf + SPX_SHAX_BLOCK_BYTES, state,
|
||||||
|
buf, mlen + SPX_N);
|
||||||
|
}
|
||||||
|
/* Otherwise first fill a block, so that finalize only uses the message */
|
||||||
|
else {
|
||||||
|
memcpy(buf + SPX_N, m, SPX_SHAX_BLOCK_BYTES - SPX_N);
|
||||||
|
shaX_inc_blocks(state, buf, 1);
|
||||||
|
|
||||||
|
m += SPX_SHAX_BLOCK_BYTES - SPX_N;
|
||||||
|
mlen -= SPX_SHAX_BLOCK_BYTES - SPX_N;
|
||||||
|
shaX_inc_finalize(buf + SPX_SHAX_BLOCK_BYTES, state, m, mlen);
|
||||||
|
}
|
||||||
|
|
||||||
|
for (i = 0; i < SPX_N; i++) {
|
||||||
|
buf[i] = 0x5c ^ sk_prf[i];
|
||||||
|
}
|
||||||
|
memset(buf + SPX_N, 0x5c, SPX_SHAX_BLOCK_BYTES - SPX_N);
|
||||||
|
|
||||||
|
shaX(buf, buf, SPX_SHAX_BLOCK_BYTES + SPX_SHAX_OUTPUT_BYTES);
|
||||||
|
memcpy(R, buf, SPX_N);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 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 hash_message(unsigned char *digest, uint64_t *tree, uint32_t *leaf_idx,
|
||||||
|
const unsigned char *R, const unsigned char *pk,
|
||||||
|
const unsigned char *m, unsigned long long mlen,
|
||||||
|
const spx_ctx *ctx)
|
||||||
|
{
|
||||||
|
(void)ctx;
|
||||||
|
#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 seed[2*SPX_N + SPX_SHAX_OUTPUT_BYTES];
|
||||||
|
|
||||||
|
/* Round to nearest multiple of SPX_SHAX_BLOCK_BYTES */
|
||||||
|
#if (SPX_SHAX_BLOCK_BYTES & (SPX_SHAX_BLOCK_BYTES-1)) != 0
|
||||||
|
#error "Assumes that SPX_SHAX_BLOCK_BYTES is a power of 2"
|
||||||
|
#endif
|
||||||
|
#define SPX_INBLOCKS (((SPX_N + SPX_PK_BYTES + SPX_SHAX_BLOCK_BYTES - 1) & \
|
||||||
|
-SPX_SHAX_BLOCK_BYTES) / SPX_SHAX_BLOCK_BYTES)
|
||||||
|
unsigned char inbuf[SPX_INBLOCKS * SPX_SHAX_BLOCK_BYTES];
|
||||||
|
|
||||||
|
unsigned char buf[SPX_DGST_BYTES];
|
||||||
|
unsigned char *bufp = buf;
|
||||||
|
uint8_t state[8 + SPX_SHAX_OUTPUT_BYTES];
|
||||||
|
|
||||||
|
shaX_inc_init(state);
|
||||||
|
|
||||||
|
// seed: SHA-X(R ‖ PK.seed ‖ PK.root ‖ M)
|
||||||
|
memcpy(inbuf, R, SPX_N);
|
||||||
|
memcpy(inbuf + SPX_N, pk, SPX_PK_BYTES);
|
||||||
|
|
||||||
|
/* If R + pk + message cannot fill up an entire block */
|
||||||
|
if (SPX_N + SPX_PK_BYTES + mlen < SPX_INBLOCKS * SPX_SHAX_BLOCK_BYTES) {
|
||||||
|
memcpy(inbuf + SPX_N + SPX_PK_BYTES, m, mlen);
|
||||||
|
shaX_inc_finalize(seed + 2*SPX_N, state, inbuf, SPX_N + SPX_PK_BYTES + mlen);
|
||||||
|
}
|
||||||
|
/* Otherwise first fill a block, so that finalize only uses the message */
|
||||||
|
else {
|
||||||
|
memcpy(inbuf + SPX_N + SPX_PK_BYTES, m,
|
||||||
|
SPX_INBLOCKS * SPX_SHAX_BLOCK_BYTES - SPX_N - SPX_PK_BYTES);
|
||||||
|
shaX_inc_blocks(state, inbuf, SPX_INBLOCKS);
|
||||||
|
|
||||||
|
m += SPX_INBLOCKS * SPX_SHAX_BLOCK_BYTES - SPX_N - SPX_PK_BYTES;
|
||||||
|
mlen -= SPX_INBLOCKS * SPX_SHAX_BLOCK_BYTES - SPX_N - SPX_PK_BYTES;
|
||||||
|
shaX_inc_finalize(seed + 2*SPX_N, state, m, mlen);
|
||||||
|
}
|
||||||
|
|
||||||
|
// H_msg: MGF1-SHA-X(R ‖ PK.seed ‖ seed)
|
||||||
|
memcpy(seed, R, SPX_N);
|
||||||
|
memcpy(seed + SPX_N, pk, SPX_N);
|
||||||
|
|
||||||
|
/* By doing this in two steps, we prevent hashing the message twice;
|
||||||
|
otherwise each iteration in MGF1 would hash the message again. */
|
||||||
|
mgf1_X(bufp, SPX_DGST_BYTES, seed, 2*SPX_N + SPX_SHAX_OUTPUT_BYTES);
|
||||||
|
|
||||||
|
memcpy(digest, bufp, SPX_FORS_MSG_BYTES);
|
||||||
|
bufp += SPX_FORS_MSG_BYTES;
|
||||||
|
|
||||||
|
#if SPX_TREE_BITS > 64
|
||||||
|
#error For given height and depth, 64 bits cannot represent all subtrees
|
||||||
|
#endif
|
||||||
|
|
||||||
|
if (SPX_D == 1) {
|
||||||
|
*tree = 0;
|
||||||
|
} else {
|
||||||
|
*tree = bytes_to_ull(bufp, SPX_TREE_BYTES);
|
||||||
|
*tree &= (~(uint64_t)0) >> (64 - SPX_TREE_BITS);
|
||||||
|
}
|
||||||
|
bufp += SPX_TREE_BYTES;
|
||||||
|
|
||||||
|
*leaf_idx = (uint32_t)bytes_to_ull(bufp, SPX_LEAF_BYTES);
|
||||||
|
*leaf_idx &= (~(uint32_t)0) >> (32 - SPX_LEAF_BITS);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
61
app/src/main/cpp/pq/sphincsplus/merkle.c
Normal file
61
app/src/main/cpp/pq/sphincsplus/merkle.c
Normal file
|
|
@ -0,0 +1,61 @@
|
||||||
|
#include <stdint.h>
|
||||||
|
#include <string.h>
|
||||||
|
|
||||||
|
#include "utils.h"
|
||||||
|
#include "utilsx1.h"
|
||||||
|
#include "wots.h"
|
||||||
|
#include "wotsx1.h"
|
||||||
|
#include "merkle.h"
|
||||||
|
#include "address.h"
|
||||||
|
#include "params.h"
|
||||||
|
|
||||||
|
/*
|
||||||
|
* This generates a Merkle signature (WOTS signature followed by the Merkle
|
||||||
|
* authentication path). This is in this file because most of the complexity
|
||||||
|
* is involved with the WOTS signature; the Merkle authentication path logic
|
||||||
|
* is mostly hidden in treehashx4
|
||||||
|
*/
|
||||||
|
void merkle_sign(uint8_t *sig, unsigned char *root,
|
||||||
|
const spx_ctx *ctx,
|
||||||
|
uint32_t wots_addr[8], uint32_t tree_addr[8],
|
||||||
|
uint32_t idx_leaf)
|
||||||
|
{
|
||||||
|
unsigned char *auth_path = sig + SPX_WOTS_BYTES;
|
||||||
|
struct leaf_info_x1 info = { 0 };
|
||||||
|
unsigned steps[ SPX_WOTS_LEN ];
|
||||||
|
|
||||||
|
info.wots_sig = sig;
|
||||||
|
chain_lengths(steps, root);
|
||||||
|
info.wots_steps = steps;
|
||||||
|
|
||||||
|
set_type(&tree_addr[0], SPX_ADDR_TYPE_HASHTREE);
|
||||||
|
set_type(&info.pk_addr[0], SPX_ADDR_TYPE_WOTSPK);
|
||||||
|
copy_subtree_addr(&info.leaf_addr[0], wots_addr);
|
||||||
|
copy_subtree_addr(&info.pk_addr[0], wots_addr);
|
||||||
|
|
||||||
|
info.wots_sign_leaf = idx_leaf;
|
||||||
|
|
||||||
|
treehashx1(root, auth_path, ctx,
|
||||||
|
idx_leaf, 0,
|
||||||
|
SPX_TREE_HEIGHT,
|
||||||
|
wots_gen_leafx1,
|
||||||
|
tree_addr, &info);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Compute root node of the top-most subtree. */
|
||||||
|
void merkle_gen_root(unsigned char *root, const spx_ctx *ctx)
|
||||||
|
{
|
||||||
|
/* 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 + SPX_WOTS_BYTES];
|
||||||
|
uint32_t top_tree_addr[8] = {0};
|
||||||
|
uint32_t wots_addr[8] = {0};
|
||||||
|
|
||||||
|
set_layer_addr(top_tree_addr, SPX_D - 1);
|
||||||
|
set_layer_addr(wots_addr, SPX_D - 1);
|
||||||
|
|
||||||
|
merkle_sign(auth_path, root, ctx,
|
||||||
|
wots_addr, top_tree_addr,
|
||||||
|
(uint32_t)~0 /* ~0 means "don't bother generating an auth path */ );
|
||||||
|
}
|
||||||
18
app/src/main/cpp/pq/sphincsplus/merkle.h
Normal file
18
app/src/main/cpp/pq/sphincsplus/merkle.h
Normal file
|
|
@ -0,0 +1,18 @@
|
||||||
|
#if !defined( MERKLE_H_ )
|
||||||
|
#define MERKLE_H_
|
||||||
|
|
||||||
|
#include <stdint.h>
|
||||||
|
|
||||||
|
/* Generate a Merkle signature (WOTS signature followed by the Merkle */
|
||||||
|
/* authentication path) */
|
||||||
|
#define merkle_sign SPX_NAMESPACE(merkle_sign)
|
||||||
|
void merkle_sign(uint8_t *sig, unsigned char *root,
|
||||||
|
const spx_ctx* ctx,
|
||||||
|
uint32_t wots_addr[8], uint32_t tree_addr[8],
|
||||||
|
uint32_t idx_leaf);
|
||||||
|
|
||||||
|
/* Compute the root node of the top-most subtree. */
|
||||||
|
#define merkle_gen_root SPX_NAMESPACE(merkle_gen_root)
|
||||||
|
void merkle_gen_root(unsigned char *root, const spx_ctx* ctx);
|
||||||
|
|
||||||
|
#endif /* MERKLE_H_ */
|
||||||
5
app/src/main/cpp/pq/sphincsplus/params.h
Normal file
5
app/src/main/cpp/pq/sphincsplus/params.h
Normal file
|
|
@ -0,0 +1,5 @@
|
||||||
|
#define str(s) #s
|
||||||
|
#define xstr(s) str(s)
|
||||||
|
|
||||||
|
#include xstr(params/params-PARAMS.h)
|
||||||
|
|
||||||
|
|
@ -0,0 +1,85 @@
|
||||||
|
#ifndef SPX_PARAMS_H
|
||||||
|
#define SPX_PARAMS_H
|
||||||
|
|
||||||
|
#define SPX_NAMESPACE(s) SPX_##s
|
||||||
|
|
||||||
|
/* Hash output length in bytes. */
|
||||||
|
#define SPX_N 16
|
||||||
|
/* Height of the hypertree. */
|
||||||
|
#define SPX_FULL_HEIGHT 63
|
||||||
|
/* Number of subtree layer. */
|
||||||
|
#define SPX_D 7
|
||||||
|
/* FORS tree dimensions. */
|
||||||
|
#define SPX_FORS_HEIGHT 12
|
||||||
|
#define SPX_FORS_TREES 14
|
||||||
|
/* 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. */
|
||||||
|
|
||||||
|
/* This is a SHA2-based parameter set, hence whether we use SHA-256
|
||||||
|
* exclusively or we use both SHA-256 and SHA-512 is controlled by
|
||||||
|
* the following #define */
|
||||||
|
#define SPX_SHA512 0 /* Use SHA-256 for all hashes */
|
||||||
|
|
||||||
|
/* For clarity */
|
||||||
|
#define SPX_ADDR_BYTES 32
|
||||||
|
|
||||||
|
/* WOTS parameters. */
|
||||||
|
#if SPX_WOTS_W == 256
|
||||||
|
#define SPX_WOTS_LOGW 8
|
||||||
|
#elif SPX_WOTS_W == 16
|
||||||
|
#define SPX_WOTS_LOGW 4
|
||||||
|
#else
|
||||||
|
#error SPX_WOTS_W assumed 16 or 256
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#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 */
|
||||||
|
#if SPX_WOTS_W == 256
|
||||||
|
#if SPX_N <= 1
|
||||||
|
#define SPX_WOTS_LEN2 1
|
||||||
|
#elif SPX_N <= 256
|
||||||
|
#define SPX_WOTS_LEN2 2
|
||||||
|
#else
|
||||||
|
#error Did not precompute SPX_WOTS_LEN2 for n outside {2, .., 256}
|
||||||
|
#endif
|
||||||
|
#elif SPX_WOTS_W == 16
|
||||||
|
#if SPX_N <= 8
|
||||||
|
#define SPX_WOTS_LEN2 2
|
||||||
|
#elif SPX_N <= 136
|
||||||
|
#define SPX_WOTS_LEN2 3
|
||||||
|
#elif SPX_N <= 256
|
||||||
|
#define SPX_WOTS_LEN2 4
|
||||||
|
#else
|
||||||
|
#error Did not precompute SPX_WOTS_LEN2 for n outside {2, .., 256}
|
||||||
|
#endif
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#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)
|
||||||
|
|
||||||
|
#if SPX_TREE_HEIGHT * SPX_D != SPX_FULL_HEIGHT
|
||||||
|
#error SPX_D should always divide SPX_FULL_HEIGHT
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/* 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)
|
||||||
|
|
||||||
|
#include "../sha2_offsets.h"
|
||||||
|
|
||||||
|
#endif
|
||||||
15
app/src/main/cpp/pq/sphincsplus/randombytes.h
Normal file
15
app/src/main/cpp/pq/sphincsplus/randombytes.h
Normal file
|
|
@ -0,0 +1,15 @@
|
||||||
|
#ifndef VEXTA_PQ_RANDOMBYTES_H
|
||||||
|
#define VEXTA_PQ_RANDOMBYTES_H
|
||||||
|
#include <stddef.h>
|
||||||
|
#include <stdint.h>
|
||||||
|
#ifdef __cplusplus
|
||||||
|
extern "C" {
|
||||||
|
#endif
|
||||||
|
/* VEXTA: unified prototype. Definition lives in pq_randombytes.cpp and is
|
||||||
|
backed by Bitcoin's CSPRNG (GetStrongRandBytes). The upstream test-only
|
||||||
|
randombytes.c files were deleted — they must never ship in a wallet. */
|
||||||
|
void randombytes(uint8_t *out, size_t outlen);
|
||||||
|
#ifdef __cplusplus
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
#endif
|
||||||
700
app/src/main/cpp/pq/sphincsplus/sha2.c
Normal file
700
app/src/main/cpp/pq/sphincsplus/sha2.c
Normal file
|
|
@ -0,0 +1,700 @@
|
||||||
|
/* Based on the public domain implementation in
|
||||||
|
* crypto_hash/sha512/ref/ from http://bench.cr.yp.to/supercop.html
|
||||||
|
* by D. J. Bernstein */
|
||||||
|
|
||||||
|
#include <stddef.h>
|
||||||
|
#include <stdint.h>
|
||||||
|
#include <string.h>
|
||||||
|
|
||||||
|
#include "utils.h"
|
||||||
|
#include "sha2.h"
|
||||||
|
|
||||||
|
static uint32_t load_bigendian_32(const uint8_t *x) {
|
||||||
|
return (uint32_t)(x[3]) | (((uint32_t)(x[2])) << 8) |
|
||||||
|
(((uint32_t)(x[1])) << 16) | (((uint32_t)(x[0])) << 24);
|
||||||
|
}
|
||||||
|
|
||||||
|
static uint64_t load_bigendian_64(const uint8_t *x) {
|
||||||
|
return (uint64_t)(x[7]) | (((uint64_t)(x[6])) << 8) |
|
||||||
|
(((uint64_t)(x[5])) << 16) | (((uint64_t)(x[4])) << 24) |
|
||||||
|
(((uint64_t)(x[3])) << 32) | (((uint64_t)(x[2])) << 40) |
|
||||||
|
(((uint64_t)(x[1])) << 48) | (((uint64_t)(x[0])) << 56);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void store_bigendian_32(uint8_t *x, uint64_t u) {
|
||||||
|
x[3] = (uint8_t) u;
|
||||||
|
u >>= 8;
|
||||||
|
x[2] = (uint8_t) u;
|
||||||
|
u >>= 8;
|
||||||
|
x[1] = (uint8_t) u;
|
||||||
|
u >>= 8;
|
||||||
|
x[0] = (uint8_t) u;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void store_bigendian_64(uint8_t *x, uint64_t u) {
|
||||||
|
x[7] = (uint8_t) u;
|
||||||
|
u >>= 8;
|
||||||
|
x[6] = (uint8_t) u;
|
||||||
|
u >>= 8;
|
||||||
|
x[5] = (uint8_t) u;
|
||||||
|
u >>= 8;
|
||||||
|
x[4] = (uint8_t) u;
|
||||||
|
u >>= 8;
|
||||||
|
x[3] = (uint8_t) u;
|
||||||
|
u >>= 8;
|
||||||
|
x[2] = (uint8_t) u;
|
||||||
|
u >>= 8;
|
||||||
|
x[1] = (uint8_t) u;
|
||||||
|
u >>= 8;
|
||||||
|
x[0] = (uint8_t) u;
|
||||||
|
}
|
||||||
|
|
||||||
|
#define SHR(x, c) ((x) >> (c))
|
||||||
|
#define ROTR_32(x, c) (((x) >> (c)) | ((x) << (32 - (c))))
|
||||||
|
#define ROTR_64(x,c) (((x) >> (c)) | ((x) << (64 - (c))))
|
||||||
|
|
||||||
|
#define Ch(x, y, z) (((x) & (y)) ^ (~(x) & (z)))
|
||||||
|
#define Maj(x, y, z) (((x) & (y)) ^ ((x) & (z)) ^ ((y) & (z)))
|
||||||
|
|
||||||
|
#define Sigma0_32(x) (ROTR_32(x, 2) ^ ROTR_32(x,13) ^ ROTR_32(x,22))
|
||||||
|
#define Sigma1_32(x) (ROTR_32(x, 6) ^ ROTR_32(x,11) ^ ROTR_32(x,25))
|
||||||
|
#define sigma0_32(x) (ROTR_32(x, 7) ^ ROTR_32(x,18) ^ SHR(x, 3))
|
||||||
|
#define sigma1_32(x) (ROTR_32(x,17) ^ ROTR_32(x,19) ^ SHR(x,10))
|
||||||
|
|
||||||
|
#define Sigma0_64(x) (ROTR_64(x,28) ^ ROTR_64(x,34) ^ ROTR_64(x,39))
|
||||||
|
#define Sigma1_64(x) (ROTR_64(x,14) ^ ROTR_64(x,18) ^ ROTR_64(x,41))
|
||||||
|
#define sigma0_64(x) (ROTR_64(x, 1) ^ ROTR_64(x, 8) ^ SHR(x,7))
|
||||||
|
#define sigma1_64(x) (ROTR_64(x,19) ^ ROTR_64(x,61) ^ SHR(x,6))
|
||||||
|
|
||||||
|
#define M_32(w0, w14, w9, w1) w0 = sigma1_32(w14) + (w9) + sigma0_32(w1) + (w0);
|
||||||
|
#define M_64(w0, w14, w9, w1) w0 = sigma1_64(w14) + (w9) + sigma0_64(w1) + (w0);
|
||||||
|
|
||||||
|
#define EXPAND_32 \
|
||||||
|
M_32(w0, w14, w9, w1) \
|
||||||
|
M_32(w1, w15, w10, w2) \
|
||||||
|
M_32(w2, w0, w11, w3) \
|
||||||
|
M_32(w3, w1, w12, w4) \
|
||||||
|
M_32(w4, w2, w13, w5) \
|
||||||
|
M_32(w5, w3, w14, w6) \
|
||||||
|
M_32(w6, w4, w15, w7) \
|
||||||
|
M_32(w7, w5, w0, w8) \
|
||||||
|
M_32(w8, w6, w1, w9) \
|
||||||
|
M_32(w9, w7, w2, w10) \
|
||||||
|
M_32(w10, w8, w3, w11) \
|
||||||
|
M_32(w11, w9, w4, w12) \
|
||||||
|
M_32(w12, w10, w5, w13) \
|
||||||
|
M_32(w13, w11, w6, w14) \
|
||||||
|
M_32(w14, w12, w7, w15) \
|
||||||
|
M_32(w15, w13, w8, w0)
|
||||||
|
|
||||||
|
#define EXPAND_64 \
|
||||||
|
M_64(w0 ,w14,w9 ,w1 ) \
|
||||||
|
M_64(w1 ,w15,w10,w2 ) \
|
||||||
|
M_64(w2 ,w0 ,w11,w3 ) \
|
||||||
|
M_64(w3 ,w1 ,w12,w4 ) \
|
||||||
|
M_64(w4 ,w2 ,w13,w5 ) \
|
||||||
|
M_64(w5 ,w3 ,w14,w6 ) \
|
||||||
|
M_64(w6 ,w4 ,w15,w7 ) \
|
||||||
|
M_64(w7 ,w5 ,w0 ,w8 ) \
|
||||||
|
M_64(w8 ,w6 ,w1 ,w9 ) \
|
||||||
|
M_64(w9 ,w7 ,w2 ,w10) \
|
||||||
|
M_64(w10,w8 ,w3 ,w11) \
|
||||||
|
M_64(w11,w9 ,w4 ,w12) \
|
||||||
|
M_64(w12,w10,w5 ,w13) \
|
||||||
|
M_64(w13,w11,w6 ,w14) \
|
||||||
|
M_64(w14,w12,w7 ,w15) \
|
||||||
|
M_64(w15,w13,w8 ,w0 )
|
||||||
|
|
||||||
|
#define F_32(w, k) \
|
||||||
|
T1 = h + Sigma1_32(e) + Ch(e, f, g) + (k) + (w); \
|
||||||
|
T2 = Sigma0_32(a) + Maj(a, b, c); \
|
||||||
|
h = g; \
|
||||||
|
g = f; \
|
||||||
|
f = e; \
|
||||||
|
e = d + T1; \
|
||||||
|
d = c; \
|
||||||
|
c = b; \
|
||||||
|
b = a; \
|
||||||
|
a = T1 + T2;
|
||||||
|
|
||||||
|
#define F_64(w,k) \
|
||||||
|
T1 = h + Sigma1_64(e) + Ch(e,f,g) + k + w; \
|
||||||
|
T2 = Sigma0_64(a) + Maj(a,b,c); \
|
||||||
|
h = g; \
|
||||||
|
g = f; \
|
||||||
|
f = e; \
|
||||||
|
e = d + T1; \
|
||||||
|
d = c; \
|
||||||
|
c = b; \
|
||||||
|
b = a; \
|
||||||
|
a = T1 + T2;
|
||||||
|
|
||||||
|
static size_t crypto_hashblocks_sha256(uint8_t *statebytes,
|
||||||
|
const uint8_t *in, size_t inlen) {
|
||||||
|
uint32_t state[8];
|
||||||
|
uint32_t a;
|
||||||
|
uint32_t b;
|
||||||
|
uint32_t c;
|
||||||
|
uint32_t d;
|
||||||
|
uint32_t e;
|
||||||
|
uint32_t f;
|
||||||
|
uint32_t g;
|
||||||
|
uint32_t h;
|
||||||
|
uint32_t T1;
|
||||||
|
uint32_t T2;
|
||||||
|
|
||||||
|
a = load_bigendian_32(statebytes + 0);
|
||||||
|
state[0] = a;
|
||||||
|
b = load_bigendian_32(statebytes + 4);
|
||||||
|
state[1] = b;
|
||||||
|
c = load_bigendian_32(statebytes + 8);
|
||||||
|
state[2] = c;
|
||||||
|
d = load_bigendian_32(statebytes + 12);
|
||||||
|
state[3] = d;
|
||||||
|
e = load_bigendian_32(statebytes + 16);
|
||||||
|
state[4] = e;
|
||||||
|
f = load_bigendian_32(statebytes + 20);
|
||||||
|
state[5] = f;
|
||||||
|
g = load_bigendian_32(statebytes + 24);
|
||||||
|
state[6] = g;
|
||||||
|
h = load_bigendian_32(statebytes + 28);
|
||||||
|
state[7] = h;
|
||||||
|
|
||||||
|
while (inlen >= 64) {
|
||||||
|
uint32_t w0 = load_bigendian_32(in + 0);
|
||||||
|
uint32_t w1 = load_bigendian_32(in + 4);
|
||||||
|
uint32_t w2 = load_bigendian_32(in + 8);
|
||||||
|
uint32_t w3 = load_bigendian_32(in + 12);
|
||||||
|
uint32_t w4 = load_bigendian_32(in + 16);
|
||||||
|
uint32_t w5 = load_bigendian_32(in + 20);
|
||||||
|
uint32_t w6 = load_bigendian_32(in + 24);
|
||||||
|
uint32_t w7 = load_bigendian_32(in + 28);
|
||||||
|
uint32_t w8 = load_bigendian_32(in + 32);
|
||||||
|
uint32_t w9 = load_bigendian_32(in + 36);
|
||||||
|
uint32_t w10 = load_bigendian_32(in + 40);
|
||||||
|
uint32_t w11 = load_bigendian_32(in + 44);
|
||||||
|
uint32_t w12 = load_bigendian_32(in + 48);
|
||||||
|
uint32_t w13 = load_bigendian_32(in + 52);
|
||||||
|
uint32_t w14 = load_bigendian_32(in + 56);
|
||||||
|
uint32_t w15 = load_bigendian_32(in + 60);
|
||||||
|
|
||||||
|
F_32(w0, 0x428a2f98)
|
||||||
|
F_32(w1, 0x71374491)
|
||||||
|
F_32(w2, 0xb5c0fbcf)
|
||||||
|
F_32(w3, 0xe9b5dba5)
|
||||||
|
F_32(w4, 0x3956c25b)
|
||||||
|
F_32(w5, 0x59f111f1)
|
||||||
|
F_32(w6, 0x923f82a4)
|
||||||
|
F_32(w7, 0xab1c5ed5)
|
||||||
|
F_32(w8, 0xd807aa98)
|
||||||
|
F_32(w9, 0x12835b01)
|
||||||
|
F_32(w10, 0x243185be)
|
||||||
|
F_32(w11, 0x550c7dc3)
|
||||||
|
F_32(w12, 0x72be5d74)
|
||||||
|
F_32(w13, 0x80deb1fe)
|
||||||
|
F_32(w14, 0x9bdc06a7)
|
||||||
|
F_32(w15, 0xc19bf174)
|
||||||
|
|
||||||
|
EXPAND_32
|
||||||
|
|
||||||
|
F_32(w0, 0xe49b69c1)
|
||||||
|
F_32(w1, 0xefbe4786)
|
||||||
|
F_32(w2, 0x0fc19dc6)
|
||||||
|
F_32(w3, 0x240ca1cc)
|
||||||
|
F_32(w4, 0x2de92c6f)
|
||||||
|
F_32(w5, 0x4a7484aa)
|
||||||
|
F_32(w6, 0x5cb0a9dc)
|
||||||
|
F_32(w7, 0x76f988da)
|
||||||
|
F_32(w8, 0x983e5152)
|
||||||
|
F_32(w9, 0xa831c66d)
|
||||||
|
F_32(w10, 0xb00327c8)
|
||||||
|
F_32(w11, 0xbf597fc7)
|
||||||
|
F_32(w12, 0xc6e00bf3)
|
||||||
|
F_32(w13, 0xd5a79147)
|
||||||
|
F_32(w14, 0x06ca6351)
|
||||||
|
F_32(w15, 0x14292967)
|
||||||
|
|
||||||
|
EXPAND_32
|
||||||
|
|
||||||
|
F_32(w0, 0x27b70a85)
|
||||||
|
F_32(w1, 0x2e1b2138)
|
||||||
|
F_32(w2, 0x4d2c6dfc)
|
||||||
|
F_32(w3, 0x53380d13)
|
||||||
|
F_32(w4, 0x650a7354)
|
||||||
|
F_32(w5, 0x766a0abb)
|
||||||
|
F_32(w6, 0x81c2c92e)
|
||||||
|
F_32(w7, 0x92722c85)
|
||||||
|
F_32(w8, 0xa2bfe8a1)
|
||||||
|
F_32(w9, 0xa81a664b)
|
||||||
|
F_32(w10, 0xc24b8b70)
|
||||||
|
F_32(w11, 0xc76c51a3)
|
||||||
|
F_32(w12, 0xd192e819)
|
||||||
|
F_32(w13, 0xd6990624)
|
||||||
|
F_32(w14, 0xf40e3585)
|
||||||
|
F_32(w15, 0x106aa070)
|
||||||
|
|
||||||
|
EXPAND_32
|
||||||
|
|
||||||
|
F_32(w0, 0x19a4c116)
|
||||||
|
F_32(w1, 0x1e376c08)
|
||||||
|
F_32(w2, 0x2748774c)
|
||||||
|
F_32(w3, 0x34b0bcb5)
|
||||||
|
F_32(w4, 0x391c0cb3)
|
||||||
|
F_32(w5, 0x4ed8aa4a)
|
||||||
|
F_32(w6, 0x5b9cca4f)
|
||||||
|
F_32(w7, 0x682e6ff3)
|
||||||
|
F_32(w8, 0x748f82ee)
|
||||||
|
F_32(w9, 0x78a5636f)
|
||||||
|
F_32(w10, 0x84c87814)
|
||||||
|
F_32(w11, 0x8cc70208)
|
||||||
|
F_32(w12, 0x90befffa)
|
||||||
|
F_32(w13, 0xa4506ceb)
|
||||||
|
F_32(w14, 0xbef9a3f7)
|
||||||
|
F_32(w15, 0xc67178f2)
|
||||||
|
|
||||||
|
a += state[0];
|
||||||
|
b += state[1];
|
||||||
|
c += state[2];
|
||||||
|
d += state[3];
|
||||||
|
e += state[4];
|
||||||
|
f += state[5];
|
||||||
|
g += state[6];
|
||||||
|
h += state[7];
|
||||||
|
|
||||||
|
state[0] = a;
|
||||||
|
state[1] = b;
|
||||||
|
state[2] = c;
|
||||||
|
state[3] = d;
|
||||||
|
state[4] = e;
|
||||||
|
state[5] = f;
|
||||||
|
state[6] = g;
|
||||||
|
state[7] = h;
|
||||||
|
|
||||||
|
in += 64;
|
||||||
|
inlen -= 64;
|
||||||
|
}
|
||||||
|
|
||||||
|
store_bigendian_32(statebytes + 0, state[0]);
|
||||||
|
store_bigendian_32(statebytes + 4, state[1]);
|
||||||
|
store_bigendian_32(statebytes + 8, state[2]);
|
||||||
|
store_bigendian_32(statebytes + 12, state[3]);
|
||||||
|
store_bigendian_32(statebytes + 16, state[4]);
|
||||||
|
store_bigendian_32(statebytes + 20, state[5]);
|
||||||
|
store_bigendian_32(statebytes + 24, state[6]);
|
||||||
|
store_bigendian_32(statebytes + 28, state[7]);
|
||||||
|
|
||||||
|
return inlen;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int crypto_hashblocks_sha512(unsigned char *statebytes,const unsigned char *in,unsigned long long inlen)
|
||||||
|
{
|
||||||
|
uint64_t state[8];
|
||||||
|
uint64_t a;
|
||||||
|
uint64_t b;
|
||||||
|
uint64_t c;
|
||||||
|
uint64_t d;
|
||||||
|
uint64_t e;
|
||||||
|
uint64_t f;
|
||||||
|
uint64_t g;
|
||||||
|
uint64_t h;
|
||||||
|
uint64_t T1;
|
||||||
|
uint64_t T2;
|
||||||
|
|
||||||
|
a = load_bigendian_64(statebytes + 0); state[0] = a;
|
||||||
|
b = load_bigendian_64(statebytes + 8); state[1] = b;
|
||||||
|
c = load_bigendian_64(statebytes + 16); state[2] = c;
|
||||||
|
d = load_bigendian_64(statebytes + 24); state[3] = d;
|
||||||
|
e = load_bigendian_64(statebytes + 32); state[4] = e;
|
||||||
|
f = load_bigendian_64(statebytes + 40); state[5] = f;
|
||||||
|
g = load_bigendian_64(statebytes + 48); state[6] = g;
|
||||||
|
h = load_bigendian_64(statebytes + 56); state[7] = h;
|
||||||
|
|
||||||
|
while (inlen >= 128) {
|
||||||
|
uint64_t w0 = load_bigendian_64(in + 0);
|
||||||
|
uint64_t w1 = load_bigendian_64(in + 8);
|
||||||
|
uint64_t w2 = load_bigendian_64(in + 16);
|
||||||
|
uint64_t w3 = load_bigendian_64(in + 24);
|
||||||
|
uint64_t w4 = load_bigendian_64(in + 32);
|
||||||
|
uint64_t w5 = load_bigendian_64(in + 40);
|
||||||
|
uint64_t w6 = load_bigendian_64(in + 48);
|
||||||
|
uint64_t w7 = load_bigendian_64(in + 56);
|
||||||
|
uint64_t w8 = load_bigendian_64(in + 64);
|
||||||
|
uint64_t w9 = load_bigendian_64(in + 72);
|
||||||
|
uint64_t w10 = load_bigendian_64(in + 80);
|
||||||
|
uint64_t w11 = load_bigendian_64(in + 88);
|
||||||
|
uint64_t w12 = load_bigendian_64(in + 96);
|
||||||
|
uint64_t w13 = load_bigendian_64(in + 104);
|
||||||
|
uint64_t w14 = load_bigendian_64(in + 112);
|
||||||
|
uint64_t w15 = load_bigendian_64(in + 120);
|
||||||
|
|
||||||
|
F_64(w0 ,0x428a2f98d728ae22ULL)
|
||||||
|
F_64(w1 ,0x7137449123ef65cdULL)
|
||||||
|
F_64(w2 ,0xb5c0fbcfec4d3b2fULL)
|
||||||
|
F_64(w3 ,0xe9b5dba58189dbbcULL)
|
||||||
|
F_64(w4 ,0x3956c25bf348b538ULL)
|
||||||
|
F_64(w5 ,0x59f111f1b605d019ULL)
|
||||||
|
F_64(w6 ,0x923f82a4af194f9bULL)
|
||||||
|
F_64(w7 ,0xab1c5ed5da6d8118ULL)
|
||||||
|
F_64(w8 ,0xd807aa98a3030242ULL)
|
||||||
|
F_64(w9 ,0x12835b0145706fbeULL)
|
||||||
|
F_64(w10,0x243185be4ee4b28cULL)
|
||||||
|
F_64(w11,0x550c7dc3d5ffb4e2ULL)
|
||||||
|
F_64(w12,0x72be5d74f27b896fULL)
|
||||||
|
F_64(w13,0x80deb1fe3b1696b1ULL)
|
||||||
|
F_64(w14,0x9bdc06a725c71235ULL)
|
||||||
|
F_64(w15,0xc19bf174cf692694ULL)
|
||||||
|
|
||||||
|
EXPAND_64
|
||||||
|
|
||||||
|
F_64(w0 ,0xe49b69c19ef14ad2ULL)
|
||||||
|
F_64(w1 ,0xefbe4786384f25e3ULL)
|
||||||
|
F_64(w2 ,0x0fc19dc68b8cd5b5ULL)
|
||||||
|
F_64(w3 ,0x240ca1cc77ac9c65ULL)
|
||||||
|
F_64(w4 ,0x2de92c6f592b0275ULL)
|
||||||
|
F_64(w5 ,0x4a7484aa6ea6e483ULL)
|
||||||
|
F_64(w6 ,0x5cb0a9dcbd41fbd4ULL)
|
||||||
|
F_64(w7 ,0x76f988da831153b5ULL)
|
||||||
|
F_64(w8 ,0x983e5152ee66dfabULL)
|
||||||
|
F_64(w9 ,0xa831c66d2db43210ULL)
|
||||||
|
F_64(w10,0xb00327c898fb213fULL)
|
||||||
|
F_64(w11,0xbf597fc7beef0ee4ULL)
|
||||||
|
F_64(w12,0xc6e00bf33da88fc2ULL)
|
||||||
|
F_64(w13,0xd5a79147930aa725ULL)
|
||||||
|
F_64(w14,0x06ca6351e003826fULL)
|
||||||
|
F_64(w15,0x142929670a0e6e70ULL)
|
||||||
|
|
||||||
|
EXPAND_64
|
||||||
|
|
||||||
|
F_64(w0 ,0x27b70a8546d22ffcULL)
|
||||||
|
F_64(w1 ,0x2e1b21385c26c926ULL)
|
||||||
|
F_64(w2 ,0x4d2c6dfc5ac42aedULL)
|
||||||
|
F_64(w3 ,0x53380d139d95b3dfULL)
|
||||||
|
F_64(w4 ,0x650a73548baf63deULL)
|
||||||
|
F_64(w5 ,0x766a0abb3c77b2a8ULL)
|
||||||
|
F_64(w6 ,0x81c2c92e47edaee6ULL)
|
||||||
|
F_64(w7 ,0x92722c851482353bULL)
|
||||||
|
F_64(w8 ,0xa2bfe8a14cf10364ULL)
|
||||||
|
F_64(w9 ,0xa81a664bbc423001ULL)
|
||||||
|
F_64(w10,0xc24b8b70d0f89791ULL)
|
||||||
|
F_64(w11,0xc76c51a30654be30ULL)
|
||||||
|
F_64(w12,0xd192e819d6ef5218ULL)
|
||||||
|
F_64(w13,0xd69906245565a910ULL)
|
||||||
|
F_64(w14,0xf40e35855771202aULL)
|
||||||
|
F_64(w15,0x106aa07032bbd1b8ULL)
|
||||||
|
|
||||||
|
EXPAND_64
|
||||||
|
|
||||||
|
F_64(w0 ,0x19a4c116b8d2d0c8ULL)
|
||||||
|
F_64(w1 ,0x1e376c085141ab53ULL)
|
||||||
|
F_64(w2 ,0x2748774cdf8eeb99ULL)
|
||||||
|
F_64(w3 ,0x34b0bcb5e19b48a8ULL)
|
||||||
|
F_64(w4 ,0x391c0cb3c5c95a63ULL)
|
||||||
|
F_64(w5 ,0x4ed8aa4ae3418acbULL)
|
||||||
|
F_64(w6 ,0x5b9cca4f7763e373ULL)
|
||||||
|
F_64(w7 ,0x682e6ff3d6b2b8a3ULL)
|
||||||
|
F_64(w8 ,0x748f82ee5defb2fcULL)
|
||||||
|
F_64(w9 ,0x78a5636f43172f60ULL)
|
||||||
|
F_64(w10,0x84c87814a1f0ab72ULL)
|
||||||
|
F_64(w11,0x8cc702081a6439ecULL)
|
||||||
|
F_64(w12,0x90befffa23631e28ULL)
|
||||||
|
F_64(w13,0xa4506cebde82bde9ULL)
|
||||||
|
F_64(w14,0xbef9a3f7b2c67915ULL)
|
||||||
|
F_64(w15,0xc67178f2e372532bULL)
|
||||||
|
|
||||||
|
EXPAND_64
|
||||||
|
|
||||||
|
F_64(w0 ,0xca273eceea26619cULL)
|
||||||
|
F_64(w1 ,0xd186b8c721c0c207ULL)
|
||||||
|
F_64(w2 ,0xeada7dd6cde0eb1eULL)
|
||||||
|
F_64(w3 ,0xf57d4f7fee6ed178ULL)
|
||||||
|
F_64(w4 ,0x06f067aa72176fbaULL)
|
||||||
|
F_64(w5 ,0x0a637dc5a2c898a6ULL)
|
||||||
|
F_64(w6 ,0x113f9804bef90daeULL)
|
||||||
|
F_64(w7 ,0x1b710b35131c471bULL)
|
||||||
|
F_64(w8 ,0x28db77f523047d84ULL)
|
||||||
|
F_64(w9 ,0x32caab7b40c72493ULL)
|
||||||
|
F_64(w10,0x3c9ebe0a15c9bebcULL)
|
||||||
|
F_64(w11,0x431d67c49c100d4cULL)
|
||||||
|
F_64(w12,0x4cc5d4becb3e42b6ULL)
|
||||||
|
F_64(w13,0x597f299cfc657e2aULL)
|
||||||
|
F_64(w14,0x5fcb6fab3ad6faecULL)
|
||||||
|
F_64(w15,0x6c44198c4a475817ULL)
|
||||||
|
|
||||||
|
a += state[0];
|
||||||
|
b += state[1];
|
||||||
|
c += state[2];
|
||||||
|
d += state[3];
|
||||||
|
e += state[4];
|
||||||
|
f += state[5];
|
||||||
|
g += state[6];
|
||||||
|
h += state[7];
|
||||||
|
|
||||||
|
state[0] = a;
|
||||||
|
state[1] = b;
|
||||||
|
state[2] = c;
|
||||||
|
state[3] = d;
|
||||||
|
state[4] = e;
|
||||||
|
state[5] = f;
|
||||||
|
state[6] = g;
|
||||||
|
state[7] = h;
|
||||||
|
|
||||||
|
in += 128;
|
||||||
|
inlen -= 128;
|
||||||
|
}
|
||||||
|
|
||||||
|
store_bigendian_64(statebytes + 0,state[0]);
|
||||||
|
store_bigendian_64(statebytes + 8,state[1]);
|
||||||
|
store_bigendian_64(statebytes + 16,state[2]);
|
||||||
|
store_bigendian_64(statebytes + 24,state[3]);
|
||||||
|
store_bigendian_64(statebytes + 32,state[4]);
|
||||||
|
store_bigendian_64(statebytes + 40,state[5]);
|
||||||
|
store_bigendian_64(statebytes + 48,state[6]);
|
||||||
|
store_bigendian_64(statebytes + 56,state[7]);
|
||||||
|
|
||||||
|
return inlen;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static const uint8_t iv_256[32] = {
|
||||||
|
0x6a, 0x09, 0xe6, 0x67, 0xbb, 0x67, 0xae, 0x85,
|
||||||
|
0x3c, 0x6e, 0xf3, 0x72, 0xa5, 0x4f, 0xf5, 0x3a,
|
||||||
|
0x51, 0x0e, 0x52, 0x7f, 0x9b, 0x05, 0x68, 0x8c,
|
||||||
|
0x1f, 0x83, 0xd9, 0xab, 0x5b, 0xe0, 0xcd, 0x19
|
||||||
|
};
|
||||||
|
|
||||||
|
static const uint8_t iv_512[64] = {
|
||||||
|
0x6a, 0x09, 0xe6, 0x67, 0xf3, 0xbc, 0xc9, 0x08, 0xbb, 0x67, 0xae,
|
||||||
|
0x85, 0x84, 0xca, 0xa7, 0x3b, 0x3c, 0x6e, 0xf3, 0x72, 0xfe, 0x94,
|
||||||
|
0xf8, 0x2b, 0xa5, 0x4f, 0xf5, 0x3a, 0x5f, 0x1d, 0x36, 0xf1, 0x51,
|
||||||
|
0x0e, 0x52, 0x7f, 0xad, 0xe6, 0x82, 0xd1, 0x9b, 0x05, 0x68, 0x8c,
|
||||||
|
0x2b, 0x3e, 0x6c, 0x1f, 0x1f, 0x83, 0xd9, 0xab, 0xfb, 0x41, 0xbd,
|
||||||
|
0x6b, 0x5b, 0xe0, 0xcd, 0x19, 0x13, 0x7e, 0x21, 0x79
|
||||||
|
};
|
||||||
|
|
||||||
|
void sha256_inc_init(uint8_t *state) {
|
||||||
|
for (size_t i = 0; i < 32; ++i) {
|
||||||
|
state[i] = iv_256[i];
|
||||||
|
}
|
||||||
|
for (size_t i = 32; i < 40; ++i) {
|
||||||
|
state[i] = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void sha512_inc_init(uint8_t *state) {
|
||||||
|
for (size_t i = 0; i < 64; ++i) {
|
||||||
|
state[i] = iv_512[i];
|
||||||
|
}
|
||||||
|
for (size_t i = 64; i < 72; ++i) {
|
||||||
|
state[i] = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void sha256_inc_blocks(uint8_t *state, const uint8_t *in, size_t inblocks) {
|
||||||
|
uint64_t bytes = load_bigendian_64(state + 32);
|
||||||
|
|
||||||
|
crypto_hashblocks_sha256(state, in, 64 * inblocks);
|
||||||
|
bytes += 64 * inblocks;
|
||||||
|
|
||||||
|
store_bigendian_64(state + 32, bytes);
|
||||||
|
}
|
||||||
|
|
||||||
|
void sha512_inc_blocks(uint8_t *state, const uint8_t *in, size_t inblocks) {
|
||||||
|
uint64_t bytes = load_bigendian_64(state + 64);
|
||||||
|
|
||||||
|
crypto_hashblocks_sha512(state, in, 128 * inblocks);
|
||||||
|
bytes += 128 * inblocks;
|
||||||
|
|
||||||
|
store_bigendian_64(state + 64, bytes);
|
||||||
|
}
|
||||||
|
|
||||||
|
void sha256_inc_finalize(uint8_t *out, uint8_t *state, const uint8_t *in, size_t inlen) {
|
||||||
|
uint8_t padded[128];
|
||||||
|
uint64_t bytes = load_bigendian_64(state + 32) + inlen;
|
||||||
|
|
||||||
|
crypto_hashblocks_sha256(state, in, inlen);
|
||||||
|
in += inlen;
|
||||||
|
inlen &= 63;
|
||||||
|
in -= inlen;
|
||||||
|
|
||||||
|
for (size_t i = 0; i < inlen; ++i) {
|
||||||
|
padded[i] = in[i];
|
||||||
|
}
|
||||||
|
padded[inlen] = 0x80;
|
||||||
|
|
||||||
|
if (inlen < 56) {
|
||||||
|
for (size_t i = inlen + 1; i < 56; ++i) {
|
||||||
|
padded[i] = 0;
|
||||||
|
}
|
||||||
|
padded[56] = (uint8_t) (bytes >> 53);
|
||||||
|
padded[57] = (uint8_t) (bytes >> 45);
|
||||||
|
padded[58] = (uint8_t) (bytes >> 37);
|
||||||
|
padded[59] = (uint8_t) (bytes >> 29);
|
||||||
|
padded[60] = (uint8_t) (bytes >> 21);
|
||||||
|
padded[61] = (uint8_t) (bytes >> 13);
|
||||||
|
padded[62] = (uint8_t) (bytes >> 5);
|
||||||
|
padded[63] = (uint8_t) (bytes << 3);
|
||||||
|
crypto_hashblocks_sha256(state, padded, 64);
|
||||||
|
} else {
|
||||||
|
for (size_t i = inlen + 1; i < 120; ++i) {
|
||||||
|
padded[i] = 0;
|
||||||
|
}
|
||||||
|
padded[120] = (uint8_t) (bytes >> 53);
|
||||||
|
padded[121] = (uint8_t) (bytes >> 45);
|
||||||
|
padded[122] = (uint8_t) (bytes >> 37);
|
||||||
|
padded[123] = (uint8_t) (bytes >> 29);
|
||||||
|
padded[124] = (uint8_t) (bytes >> 21);
|
||||||
|
padded[125] = (uint8_t) (bytes >> 13);
|
||||||
|
padded[126] = (uint8_t) (bytes >> 5);
|
||||||
|
padded[127] = (uint8_t) (bytes << 3);
|
||||||
|
crypto_hashblocks_sha256(state, padded, 128);
|
||||||
|
}
|
||||||
|
|
||||||
|
for (size_t i = 0; i < 32; ++i) {
|
||||||
|
out[i] = state[i];
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
void sha512_inc_finalize(uint8_t *out, uint8_t *state, const uint8_t *in, size_t inlen) {
|
||||||
|
uint8_t padded[256];
|
||||||
|
uint64_t bytes = load_bigendian_64(state + 64) + inlen;
|
||||||
|
|
||||||
|
crypto_hashblocks_sha512(state, in, inlen);
|
||||||
|
in += inlen;
|
||||||
|
inlen &= 127;
|
||||||
|
in -= inlen;
|
||||||
|
|
||||||
|
for (size_t i = 0; i < inlen; ++i) {
|
||||||
|
padded[i] = in[i];
|
||||||
|
}
|
||||||
|
padded[inlen] = 0x80;
|
||||||
|
|
||||||
|
if (inlen < 112) {
|
||||||
|
for (size_t i = inlen + 1; i < 119; ++i) {
|
||||||
|
padded[i] = 0;
|
||||||
|
}
|
||||||
|
padded[119] = (uint8_t) (bytes >> 61);
|
||||||
|
padded[120] = (uint8_t) (bytes >> 53);
|
||||||
|
padded[121] = (uint8_t) (bytes >> 45);
|
||||||
|
padded[122] = (uint8_t) (bytes >> 37);
|
||||||
|
padded[123] = (uint8_t) (bytes >> 29);
|
||||||
|
padded[124] = (uint8_t) (bytes >> 21);
|
||||||
|
padded[125] = (uint8_t) (bytes >> 13);
|
||||||
|
padded[126] = (uint8_t) (bytes >> 5);
|
||||||
|
padded[127] = (uint8_t) (bytes << 3);
|
||||||
|
crypto_hashblocks_sha512(state, padded, 128);
|
||||||
|
} else {
|
||||||
|
for (size_t i = inlen + 1; i < 247; ++i) {
|
||||||
|
padded[i] = 0;
|
||||||
|
}
|
||||||
|
padded[247] = (uint8_t) (bytes >> 61);
|
||||||
|
padded[248] = (uint8_t) (bytes >> 53);
|
||||||
|
padded[249] = (uint8_t) (bytes >> 45);
|
||||||
|
padded[250] = (uint8_t) (bytes >> 37);
|
||||||
|
padded[251] = (uint8_t) (bytes >> 29);
|
||||||
|
padded[252] = (uint8_t) (bytes >> 21);
|
||||||
|
padded[253] = (uint8_t) (bytes >> 13);
|
||||||
|
padded[254] = (uint8_t) (bytes >> 5);
|
||||||
|
padded[255] = (uint8_t) (bytes << 3);
|
||||||
|
crypto_hashblocks_sha512(state, padded, 256);
|
||||||
|
}
|
||||||
|
|
||||||
|
for (size_t i = 0; i < 64; ++i) {
|
||||||
|
out[i] = state[i];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void sha256(uint8_t *out, const uint8_t *in, size_t inlen) {
|
||||||
|
uint8_t state[40];
|
||||||
|
|
||||||
|
sha256_inc_init(state);
|
||||||
|
sha256_inc_finalize(out, state, in, inlen);
|
||||||
|
}
|
||||||
|
|
||||||
|
void sha512(uint8_t *out, const uint8_t *in, size_t inlen) {
|
||||||
|
uint8_t state[72];
|
||||||
|
|
||||||
|
sha512_inc_init(state);
|
||||||
|
sha512_inc_finalize(out, state, in, inlen);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* mgf1 function based on the SHA-256 hash function
|
||||||
|
* Note that inlen should be sufficiently small that it still allows for
|
||||||
|
* an array to be allocated on the stack. Typically 'in' is merely a seed.
|
||||||
|
* Outputs outlen number of bytes
|
||||||
|
*/
|
||||||
|
void mgf1_256(unsigned char *out, unsigned long outlen,
|
||||||
|
const unsigned char *in, unsigned long inlen)
|
||||||
|
{
|
||||||
|
SPX_VLA(uint8_t, inbuf, inlen+4);
|
||||||
|
unsigned char outbuf[SPX_SHA256_OUTPUT_BYTES];
|
||||||
|
unsigned long i;
|
||||||
|
|
||||||
|
memcpy(inbuf, in, inlen);
|
||||||
|
|
||||||
|
/* While we can fit in at least another full block of SHA256 output.. */
|
||||||
|
for (i = 0; (i+1)*SPX_SHA256_OUTPUT_BYTES <= outlen; i++) {
|
||||||
|
u32_to_bytes(inbuf + inlen, i);
|
||||||
|
sha256(out, inbuf, inlen + 4);
|
||||||
|
out += SPX_SHA256_OUTPUT_BYTES;
|
||||||
|
}
|
||||||
|
/* Until we cannot anymore, and we fill the remainder. */
|
||||||
|
if (outlen > i*SPX_SHA256_OUTPUT_BYTES) {
|
||||||
|
u32_to_bytes(inbuf + inlen, i);
|
||||||
|
sha256(outbuf, inbuf, inlen + 4);
|
||||||
|
memcpy(out, outbuf, outlen - i*SPX_SHA256_OUTPUT_BYTES);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* mgf1 function based on the SHA-512 hash function
|
||||||
|
*/
|
||||||
|
void mgf1_512(unsigned char *out, unsigned long outlen,
|
||||||
|
const unsigned char *in, unsigned long inlen)
|
||||||
|
{
|
||||||
|
SPX_VLA(uint8_t, inbuf, inlen+4);
|
||||||
|
unsigned char outbuf[SPX_SHA512_OUTPUT_BYTES];
|
||||||
|
unsigned long i;
|
||||||
|
|
||||||
|
memcpy(inbuf, in, inlen);
|
||||||
|
|
||||||
|
/* While we can fit in at least another full block of SHA512 output.. */
|
||||||
|
for (i = 0; (i+1)*SPX_SHA512_OUTPUT_BYTES <= outlen; i++) {
|
||||||
|
u32_to_bytes(inbuf + inlen, i);
|
||||||
|
sha512(out, inbuf, inlen + 4);
|
||||||
|
out += SPX_SHA512_OUTPUT_BYTES;
|
||||||
|
}
|
||||||
|
/* Until we cannot anymore, and we fill the remainder. */
|
||||||
|
if (outlen > i*SPX_SHA512_OUTPUT_BYTES) {
|
||||||
|
u32_to_bytes(inbuf + inlen, i);
|
||||||
|
sha512(outbuf, inbuf, inlen + 4);
|
||||||
|
memcpy(out, outbuf, outlen - i*SPX_SHA512_OUTPUT_BYTES);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Absorb the constant pub_seed using one round of the compression function
|
||||||
|
* This initializes state_seeded and state_seeded_512, which can then be
|
||||||
|
* reused in thash
|
||||||
|
**/
|
||||||
|
void seed_state(spx_ctx *ctx) {
|
||||||
|
uint8_t block[SPX_SHA512_BLOCK_BYTES];
|
||||||
|
size_t i;
|
||||||
|
|
||||||
|
for (i = 0; i < SPX_N; ++i) {
|
||||||
|
block[i] = ctx->pub_seed[i];
|
||||||
|
}
|
||||||
|
for (i = SPX_N; i < SPX_SHA512_BLOCK_BYTES; ++i) {
|
||||||
|
block[i] = 0;
|
||||||
|
}
|
||||||
|
/* block has been properly initialized for both SHA-256 and SHA-512 */
|
||||||
|
|
||||||
|
sha256_inc_init(ctx->state_seeded);
|
||||||
|
sha256_inc_blocks(ctx->state_seeded, block, 1);
|
||||||
|
#if SPX_SHA512
|
||||||
|
sha512_inc_init(ctx->state_seeded_512);
|
||||||
|
sha512_inc_blocks(ctx->state_seeded_512, block, 1);
|
||||||
|
#endif
|
||||||
|
}
|
||||||
43
app/src/main/cpp/pq/sphincsplus/sha2.h
Normal file
43
app/src/main/cpp/pq/sphincsplus/sha2.h
Normal file
|
|
@ -0,0 +1,43 @@
|
||||||
|
#ifndef SPX_SHA2_H
|
||||||
|
#define SPX_SHA2_H
|
||||||
|
|
||||||
|
#include "params.h"
|
||||||
|
|
||||||
|
#define SPX_SHA256_BLOCK_BYTES 64
|
||||||
|
#define SPX_SHA256_OUTPUT_BYTES 32 /* This does not necessarily equal SPX_N */
|
||||||
|
|
||||||
|
#define SPX_SHA512_BLOCK_BYTES 128
|
||||||
|
#define SPX_SHA512_OUTPUT_BYTES 64
|
||||||
|
|
||||||
|
#if SPX_SHA256_OUTPUT_BYTES < SPX_N
|
||||||
|
#error Linking against SHA-256 with N larger than 32 bytes is not supported
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#define SPX_SHA256_ADDR_BYTES 22
|
||||||
|
|
||||||
|
#include <stddef.h>
|
||||||
|
#include <stdint.h>
|
||||||
|
|
||||||
|
void sha256_inc_init(uint8_t *state);
|
||||||
|
void sha256_inc_blocks(uint8_t *state, const uint8_t *in, size_t inblocks);
|
||||||
|
void sha256_inc_finalize(uint8_t *out, uint8_t *state, const uint8_t *in, size_t inlen);
|
||||||
|
void sha256(uint8_t *out, const uint8_t *in, size_t inlen);
|
||||||
|
|
||||||
|
void sha512_inc_init(uint8_t *state);
|
||||||
|
void sha512_inc_blocks(uint8_t *state, const uint8_t *in, size_t inblocks);
|
||||||
|
void sha512_inc_finalize(uint8_t *out, uint8_t *state, const uint8_t *in, size_t inlen);
|
||||||
|
void sha512(uint8_t *out, const uint8_t *in, size_t inlen);
|
||||||
|
|
||||||
|
#define mgf1_256 SPX_NAMESPACE(mgf1_256)
|
||||||
|
void mgf1_256(unsigned char *out, unsigned long outlen,
|
||||||
|
const unsigned char *in, unsigned long inlen);
|
||||||
|
|
||||||
|
#define mgf1_512 SPX_NAMESPACE(mgf1_512)
|
||||||
|
void mgf1_512(unsigned char *out, unsigned long outlen,
|
||||||
|
const unsigned char *in, unsigned long inlen);
|
||||||
|
|
||||||
|
#define seed_state SPX_NAMESPACE(seed_state)
|
||||||
|
void seed_state(spx_ctx *ctx);
|
||||||
|
|
||||||
|
|
||||||
|
#endif
|
||||||
20
app/src/main/cpp/pq/sphincsplus/sha2_offsets.h
Normal file
20
app/src/main/cpp/pq/sphincsplus/sha2_offsets.h
Normal file
|
|
@ -0,0 +1,20 @@
|
||||||
|
#ifndef SHA2_OFFSETS_H_
|
||||||
|
#define SHA2_OFFSETS_H_
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Offsets of various fields in the address structure when we use SHA2 as
|
||||||
|
* the Sphincs+ hash function
|
||||||
|
*/
|
||||||
|
|
||||||
|
#define SPX_OFFSET_LAYER 0 /* The byte used to specify the Merkle tree layer */
|
||||||
|
#define SPX_OFFSET_TREE 1 /* The start of the 8 byte field used to specify the tree */
|
||||||
|
#define SPX_OFFSET_TYPE 9 /* The byte used to specify the hash type (reason) */
|
||||||
|
#define SPX_OFFSET_KP_ADDR 10 /* The start of the 4 byte field used to specify the key pair address */
|
||||||
|
#define SPX_OFFSET_CHAIN_ADDR 17 /* The byte used to specify the chain address (which Winternitz chain) */
|
||||||
|
#define SPX_OFFSET_HASH_ADDR 21 /* The byte used to specify the hash address (where in the Winternitz chain) */
|
||||||
|
#define SPX_OFFSET_TREE_HGT 17 /* The byte used to specify the height of this node in the FORS or Merkle tree */
|
||||||
|
#define SPX_OFFSET_TREE_INDEX 18 /* The start of the 4 byte field used to specify the node in the FORS or Merkle tree */
|
||||||
|
|
||||||
|
#define SPX_SHA2 1
|
||||||
|
|
||||||
|
#endif /* SHA2_OFFSETS_H_ */
|
||||||
293
app/src/main/cpp/pq/sphincsplus/sign.c
Normal file
293
app/src/main/cpp/pq/sphincsplus/sign.c
Normal file
|
|
@ -0,0 +1,293 @@
|
||||||
|
#include <stddef.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include <stdint.h>
|
||||||
|
|
||||||
|
#include "api.h"
|
||||||
|
#include "params.h"
|
||||||
|
#include "wots.h"
|
||||||
|
#include "fors.h"
|
||||||
|
#include "hash.h"
|
||||||
|
#include "thash.h"
|
||||||
|
#include "address.h"
|
||||||
|
#include "randombytes.h"
|
||||||
|
#include "utils.h"
|
||||||
|
#include "merkle.h"
|
||||||
|
|
||||||
|
#ifndef VEXTA_PQ_VERIFY_ONLY
|
||||||
|
/*
|
||||||
|
* Returns the length of a secret key, in bytes
|
||||||
|
*/
|
||||||
|
unsigned long long crypto_sign_secretkeybytes(void)
|
||||||
|
{
|
||||||
|
return CRYPTO_SECRETKEYBYTES;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Returns the length of a public key, in bytes
|
||||||
|
*/
|
||||||
|
unsigned long long crypto_sign_publickeybytes(void)
|
||||||
|
{
|
||||||
|
return CRYPTO_PUBLICKEYBYTES;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Returns the length of a signature, in bytes
|
||||||
|
*/
|
||||||
|
unsigned long long crypto_sign_bytes(void)
|
||||||
|
{
|
||||||
|
return CRYPTO_BYTES;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Returns the length of the seed required to generate a key pair, in bytes
|
||||||
|
*/
|
||||||
|
unsigned long long crypto_sign_seedbytes(void)
|
||||||
|
{
|
||||||
|
return 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 crypto_sign_seed_keypair(unsigned char *pk, unsigned char *sk,
|
||||||
|
const unsigned char *seed)
|
||||||
|
{
|
||||||
|
spx_ctx ctx;
|
||||||
|
|
||||||
|
/* Initialize SK_SEED, SK_PRF and PUB_SEED from seed. */
|
||||||
|
memcpy(sk, seed, CRYPTO_SEEDBYTES);
|
||||||
|
|
||||||
|
memcpy(pk, sk + 2*SPX_N, SPX_N);
|
||||||
|
|
||||||
|
memcpy(ctx.pub_seed, pk, SPX_N);
|
||||||
|
memcpy(ctx.sk_seed, sk, SPX_N);
|
||||||
|
|
||||||
|
/* This hook allows the hash function instantiation to do whatever
|
||||||
|
preparation or computation it needs, based on the public seed. */
|
||||||
|
initialize_hash_function(&ctx);
|
||||||
|
|
||||||
|
/* Compute root node of the top-most subtree. */
|
||||||
|
merkle_gen_root(sk + 3*SPX_N, &ctx);
|
||||||
|
|
||||||
|
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 crypto_sign_keypair(unsigned char *pk, unsigned char *sk)
|
||||||
|
{
|
||||||
|
unsigned char seed[CRYPTO_SEEDBYTES];
|
||||||
|
randombytes(seed, CRYPTO_SEEDBYTES);
|
||||||
|
crypto_sign_seed_keypair(pk, sk, seed);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns an array containing a detached signature.
|
||||||
|
*/
|
||||||
|
int crypto_sign_signature(uint8_t *sig, size_t *siglen,
|
||||||
|
const uint8_t *m, size_t mlen, const uint8_t *sk)
|
||||||
|
{
|
||||||
|
spx_ctx ctx;
|
||||||
|
|
||||||
|
const unsigned char *sk_prf = sk + SPX_N;
|
||||||
|
const unsigned char *pk = sk + 2*SPX_N;
|
||||||
|
|
||||||
|
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};
|
||||||
|
|
||||||
|
memcpy(ctx.sk_seed, sk, SPX_N);
|
||||||
|
memcpy(ctx.pub_seed, pk, SPX_N);
|
||||||
|
|
||||||
|
/* This hook allows the hash function instantiation to do whatever
|
||||||
|
preparation or computation it needs, based on the public seed. */
|
||||||
|
initialize_hash_function(&ctx);
|
||||||
|
|
||||||
|
set_type(wots_addr, SPX_ADDR_TYPE_WOTS);
|
||||||
|
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. */
|
||||||
|
gen_message_random(sig, sk_prf, optrand, m, mlen, &ctx);
|
||||||
|
|
||||||
|
/* Derive the message digest and leaf index from R, PK and M. */
|
||||||
|
hash_message(mhash, &tree, &idx_leaf, sig, pk, m, mlen, &ctx);
|
||||||
|
sig += SPX_N;
|
||||||
|
|
||||||
|
set_tree_addr(wots_addr, tree);
|
||||||
|
set_keypair_addr(wots_addr, idx_leaf);
|
||||||
|
|
||||||
|
/* Sign the message hash using FORS. */
|
||||||
|
fors_sign(sig, root, mhash, &ctx, wots_addr);
|
||||||
|
sig += SPX_FORS_BYTES;
|
||||||
|
|
||||||
|
for (i = 0; i < SPX_D; i++) {
|
||||||
|
set_layer_addr(tree_addr, i);
|
||||||
|
set_tree_addr(tree_addr, tree);
|
||||||
|
|
||||||
|
copy_subtree_addr(wots_addr, tree_addr);
|
||||||
|
set_keypair_addr(wots_addr, idx_leaf);
|
||||||
|
|
||||||
|
merkle_sign(sig, root, &ctx, wots_addr, tree_addr, idx_leaf);
|
||||||
|
sig += SPX_WOTS_BYTES + 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;
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif // !VEXTA_PQ_VERIFY_ONLY
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Verifies a detached signature and message under a given public key.
|
||||||
|
*/
|
||||||
|
int crypto_sign_verify(const uint8_t *sig, size_t siglen,
|
||||||
|
const uint8_t *m, size_t mlen, const uint8_t *pk)
|
||||||
|
{
|
||||||
|
spx_ctx ctx;
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
|
memcpy(ctx.pub_seed, pk, SPX_N);
|
||||||
|
|
||||||
|
/* This hook allows the hash function instantiation to do whatever
|
||||||
|
preparation or computation it needs, based on the public seed. */
|
||||||
|
initialize_hash_function(&ctx);
|
||||||
|
|
||||||
|
set_type(wots_addr, SPX_ADDR_TYPE_WOTS);
|
||||||
|
set_type(tree_addr, SPX_ADDR_TYPE_HASHTREE);
|
||||||
|
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. */
|
||||||
|
hash_message(mhash, &tree, &idx_leaf, sig, pk, m, mlen, &ctx);
|
||||||
|
sig += SPX_N;
|
||||||
|
|
||||||
|
/* Layer correctly defaults to 0, so no need to set_layer_addr */
|
||||||
|
set_tree_addr(wots_addr, tree);
|
||||||
|
set_keypair_addr(wots_addr, idx_leaf);
|
||||||
|
|
||||||
|
fors_pk_from_sig(root, sig, mhash, &ctx, wots_addr);
|
||||||
|
sig += SPX_FORS_BYTES;
|
||||||
|
|
||||||
|
/* For each subtree.. */
|
||||||
|
for (i = 0; i < SPX_D; i++) {
|
||||||
|
set_layer_addr(tree_addr, i);
|
||||||
|
set_tree_addr(tree_addr, tree);
|
||||||
|
|
||||||
|
copy_subtree_addr(wots_addr, tree_addr);
|
||||||
|
set_keypair_addr(wots_addr, idx_leaf);
|
||||||
|
|
||||||
|
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. */
|
||||||
|
wots_pk_from_sig(wots_pk, sig, root, &ctx, wots_addr);
|
||||||
|
sig += SPX_WOTS_BYTES;
|
||||||
|
|
||||||
|
/* Compute the leaf node using the WOTS public key. */
|
||||||
|
thash(leaf, wots_pk, SPX_WOTS_LEN, &ctx, wots_pk_addr);
|
||||||
|
|
||||||
|
/* Compute the root node of this subtree. */
|
||||||
|
compute_root(root, leaf, idx_leaf, 0, sig, SPX_TREE_HEIGHT,
|
||||||
|
&ctx, 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)) {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
#ifndef VEXTA_PQ_VERIFY_ONLY
|
||||||
|
/**
|
||||||
|
* Returns an array containing the signature followed by the message.
|
||||||
|
*/
|
||||||
|
int crypto_sign(unsigned char *sm, unsigned long long *smlen,
|
||||||
|
const unsigned char *m, unsigned long long mlen,
|
||||||
|
const unsigned char *sk)
|
||||||
|
{
|
||||||
|
size_t siglen;
|
||||||
|
|
||||||
|
crypto_sign_signature(sm, &siglen, m, (size_t)mlen, sk);
|
||||||
|
|
||||||
|
memmove(sm + SPX_BYTES, m, mlen);
|
||||||
|
*smlen = siglen + mlen;
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif // !VEXTA_PQ_VERIFY_ONLY
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Verifies a given signature-message pair under a given public key.
|
||||||
|
*/
|
||||||
|
int crypto_sign_open(unsigned char *m, unsigned long long *mlen,
|
||||||
|
const unsigned char *sm, unsigned long long smlen,
|
||||||
|
const unsigned char *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 (crypto_sign_verify(sm, SPX_BYTES, sm + SPX_BYTES, (size_t)*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;
|
||||||
|
}
|
||||||
13
app/src/main/cpp/pq/sphincsplus/thash.h
Normal file
13
app/src/main/cpp/pq/sphincsplus/thash.h
Normal file
|
|
@ -0,0 +1,13 @@
|
||||||
|
#ifndef SPX_THASH_H
|
||||||
|
#define SPX_THASH_H
|
||||||
|
|
||||||
|
#include "context.h"
|
||||||
|
#include "params.h"
|
||||||
|
|
||||||
|
#include <stdint.h>
|
||||||
|
|
||||||
|
#define thash SPX_NAMESPACE(thash)
|
||||||
|
void thash(unsigned char *out, const unsigned char *in, unsigned int inblocks,
|
||||||
|
const spx_ctx *ctx, uint32_t addr[8]);
|
||||||
|
|
||||||
|
#endif
|
||||||
59
app/src/main/cpp/pq/sphincsplus/thash_sha2_simple.c
Normal file
59
app/src/main/cpp/pq/sphincsplus/thash_sha2_simple.c
Normal file
|
|
@ -0,0 +1,59 @@
|
||||||
|
#include <stdint.h>
|
||||||
|
#include <string.h>
|
||||||
|
|
||||||
|
#include "thash.h"
|
||||||
|
#include "address.h"
|
||||||
|
#include "params.h"
|
||||||
|
#include "utils.h"
|
||||||
|
#include "sha2.h"
|
||||||
|
|
||||||
|
#if SPX_SHA512
|
||||||
|
static void thash_512(unsigned char *out, const unsigned char *in, unsigned int inblocks,
|
||||||
|
const spx_ctx *ctx, uint32_t addr[8]);
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Takes an array of inblocks concatenated arrays of SPX_N bytes.
|
||||||
|
*/
|
||||||
|
void thash(unsigned char *out, const unsigned char *in, unsigned int inblocks,
|
||||||
|
const spx_ctx *ctx, uint32_t addr[8])
|
||||||
|
{
|
||||||
|
#if SPX_SHA512
|
||||||
|
if (inblocks > 1) {
|
||||||
|
thash_512(out, in, inblocks, ctx, addr);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
unsigned char outbuf[SPX_SHA256_OUTPUT_BYTES];
|
||||||
|
uint8_t sha2_state[40];
|
||||||
|
SPX_VLA(uint8_t, buf, SPX_SHA256_ADDR_BYTES + inblocks*SPX_N);
|
||||||
|
|
||||||
|
/* Retrieve precomputed state containing pub_seed */
|
||||||
|
memcpy(sha2_state, ctx->state_seeded, 40 * sizeof(uint8_t));
|
||||||
|
|
||||||
|
memcpy(buf, addr, SPX_SHA256_ADDR_BYTES);
|
||||||
|
memcpy(buf + SPX_SHA256_ADDR_BYTES, in, inblocks * SPX_N);
|
||||||
|
|
||||||
|
sha256_inc_finalize(outbuf, sha2_state, buf, SPX_SHA256_ADDR_BYTES + inblocks*SPX_N);
|
||||||
|
memcpy(out, outbuf, SPX_N);
|
||||||
|
}
|
||||||
|
|
||||||
|
#if SPX_SHA512
|
||||||
|
static void thash_512(unsigned char *out, const unsigned char *in, unsigned int inblocks,
|
||||||
|
const spx_ctx *ctx, uint32_t addr[8])
|
||||||
|
{
|
||||||
|
unsigned char outbuf[SPX_SHA512_OUTPUT_BYTES];
|
||||||
|
uint8_t sha2_state[72];
|
||||||
|
SPX_VLA(uint8_t, buf, SPX_SHA256_ADDR_BYTES + inblocks*SPX_N);
|
||||||
|
|
||||||
|
/* Retrieve precomputed state containing pub_seed */
|
||||||
|
memcpy(sha2_state, ctx->state_seeded_512, 72 * sizeof(uint8_t));
|
||||||
|
|
||||||
|
memcpy(buf, addr, SPX_SHA256_ADDR_BYTES);
|
||||||
|
memcpy(buf + SPX_SHA256_ADDR_BYTES, in, inblocks * SPX_N);
|
||||||
|
|
||||||
|
sha512_inc_finalize(outbuf, sha2_state, buf, SPX_SHA256_ADDR_BYTES + inblocks*SPX_N);
|
||||||
|
memcpy(out, outbuf, SPX_N);
|
||||||
|
}
|
||||||
|
#endif
|
||||||
154
app/src/main/cpp/pq/sphincsplus/utils.c
Normal file
154
app/src/main/cpp/pq/sphincsplus/utils.c
Normal file
|
|
@ -0,0 +1,154 @@
|
||||||
|
#include <string.h>
|
||||||
|
|
||||||
|
#include "utils.h"
|
||||||
|
#include "params.h"
|
||||||
|
#include "hash.h"
|
||||||
|
#include "thash.h"
|
||||||
|
#include "address.h"
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Converts the value of 'in' to 'outlen' bytes in big-endian byte order.
|
||||||
|
*/
|
||||||
|
void ull_to_bytes(unsigned char *out, unsigned int outlen,
|
||||||
|
unsigned long long in)
|
||||||
|
{
|
||||||
|
int i;
|
||||||
|
|
||||||
|
/* Iterate over out in decreasing order, for big-endianness. */
|
||||||
|
for (i = (signed int)outlen - 1; i >= 0; i--) {
|
||||||
|
out[i] = in & 0xff;
|
||||||
|
in = in >> 8;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void u32_to_bytes(unsigned char *out, uint32_t in)
|
||||||
|
{
|
||||||
|
out[0] = (unsigned char)(in >> 24);
|
||||||
|
out[1] = (unsigned char)(in >> 16);
|
||||||
|
out[2] = (unsigned char)(in >> 8);
|
||||||
|
out[3] = (unsigned char)in;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Converts the inlen bytes in 'in' from big-endian byte order to an integer.
|
||||||
|
*/
|
||||||
|
unsigned long long bytes_to_ull(const unsigned char *in, unsigned int inlen)
|
||||||
|
{
|
||||||
|
unsigned long long retval = 0;
|
||||||
|
unsigned int i;
|
||||||
|
|
||||||
|
for (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 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 spx_ctx *ctx, 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. */
|
||||||
|
set_tree_height(addr, i + 1);
|
||||||
|
set_tree_index(addr, leaf_idx + idx_offset);
|
||||||
|
|
||||||
|
/* Pick the right or left neighbor, depending on parity of the node. */
|
||||||
|
if (leaf_idx & 1) {
|
||||||
|
thash(buffer + SPX_N, buffer, 2, ctx, addr);
|
||||||
|
memcpy(buffer, auth_path, SPX_N);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
thash(buffer, buffer, 2, ctx, 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;
|
||||||
|
set_tree_height(addr, tree_height);
|
||||||
|
set_tree_index(addr, leaf_idx + idx_offset);
|
||||||
|
thash(root, buffer, 2, ctx, 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.
|
||||||
|
*/
|
||||||
|
void treehash(unsigned char *root, unsigned char *auth_path, const spx_ctx* ctx,
|
||||||
|
uint32_t leaf_idx, uint32_t idx_offset, uint32_t tree_height,
|
||||||
|
void (*gen_leaf)(
|
||||||
|
unsigned char* /* leaf */,
|
||||||
|
const spx_ctx* /* ctx */,
|
||||||
|
uint32_t /* addr_idx */, const uint32_t[8] /* tree_addr */),
|
||||||
|
uint32_t tree_addr[8])
|
||||||
|
{
|
||||||
|
SPX_VLA(uint8_t, stack, (tree_height+1)*SPX_N);
|
||||||
|
SPX_VLA(unsigned int, heights, tree_height+1);
|
||||||
|
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, ctx, 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. */
|
||||||
|
set_tree_height(tree_addr, heights[offset - 1] + 1);
|
||||||
|
set_tree_index(tree_addr,
|
||||||
|
tree_idx + (idx_offset >> (heights[offset-1] + 1)));
|
||||||
|
/* Hash the top-most nodes from the stack together. */
|
||||||
|
thash(stack + (offset - 2)*SPX_N,
|
||||||
|
stack + (offset - 2)*SPX_N, 2, ctx, 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);
|
||||||
|
}
|
||||||
64
app/src/main/cpp/pq/sphincsplus/utils.h
Normal file
64
app/src/main/cpp/pq/sphincsplus/utils.h
Normal file
|
|
@ -0,0 +1,64 @@
|
||||||
|
#ifndef SPX_UTILS_H
|
||||||
|
#define SPX_UTILS_H
|
||||||
|
|
||||||
|
#include <stdint.h>
|
||||||
|
#include "params.h"
|
||||||
|
#include "context.h"
|
||||||
|
|
||||||
|
|
||||||
|
/* To support MSVC use alloca() instead of VLAs. See #20. */
|
||||||
|
#ifdef _MSC_VER
|
||||||
|
/* MSVC defines _alloca in malloc.h */
|
||||||
|
# include <malloc.h>
|
||||||
|
/* Note: _malloca(), which is recommended over deprecated _alloca,
|
||||||
|
requires that you call _freea(). So we stick with _alloca */
|
||||||
|
# define SPX_VLA(__t,__x,__s) __t *__x = (__t*)_alloca((__s)*sizeof(__t))
|
||||||
|
#else
|
||||||
|
# define SPX_VLA(__t,__x,__s) __t __x[__s]
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Converts the value of 'in' to 'outlen' bytes in big-endian byte order.
|
||||||
|
*/
|
||||||
|
#define ull_to_bytes SPX_NAMESPACE(ull_to_bytes)
|
||||||
|
void ull_to_bytes(unsigned char *out, unsigned int outlen,
|
||||||
|
unsigned long long in);
|
||||||
|
#define u32_to_bytes SPX_NAMESPACE(u32_to_bytes)
|
||||||
|
void u32_to_bytes(unsigned char *out, uint32_t in);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Converts the inlen bytes in 'in' from big-endian byte order to an integer.
|
||||||
|
*/
|
||||||
|
#define bytes_to_ull SPX_NAMESPACE(bytes_to_ull)
|
||||||
|
unsigned long long bytes_to_ull(const unsigned char *in, unsigned int 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.
|
||||||
|
*/
|
||||||
|
#define compute_root SPX_NAMESPACE(compute_root)
|
||||||
|
void 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 spx_ctx *ctx, 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.
|
||||||
|
*/
|
||||||
|
#define treehash SPX_NAMESPACE(treehash)
|
||||||
|
void treehash(unsigned char *root, unsigned char *auth_path,
|
||||||
|
const spx_ctx* ctx,
|
||||||
|
uint32_t leaf_idx, uint32_t idx_offset, uint32_t tree_height,
|
||||||
|
void (*gen_leaf)(
|
||||||
|
unsigned char* /* leaf */,
|
||||||
|
const spx_ctx* ctx /* ctx */,
|
||||||
|
uint32_t /* addr_idx */, const uint32_t[8] /* tree_addr */),
|
||||||
|
uint32_t tree_addr[8]);
|
||||||
|
|
||||||
|
|
||||||
|
#endif
|
||||||
100
app/src/main/cpp/pq/sphincsplus/utilsx1.c
Normal file
100
app/src/main/cpp/pq/sphincsplus/utilsx1.c
Normal file
|
|
@ -0,0 +1,100 @@
|
||||||
|
#include <string.h>
|
||||||
|
|
||||||
|
#include "utils.h"
|
||||||
|
#include "utilsx1.h"
|
||||||
|
#include "params.h"
|
||||||
|
#include "thash.h"
|
||||||
|
#include "address.h"
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Generate the entire Merkle tree, computing the authentication path for
|
||||||
|
* leaf_idx, 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)
|
||||||
|
*
|
||||||
|
* This expects tree_addr to be initialized to the addr structures for the
|
||||||
|
* Merkle tree nodes
|
||||||
|
*
|
||||||
|
* Applies the offset idx_offset to indices before building addresses, so that
|
||||||
|
* it is possible to continue counting indices across trees.
|
||||||
|
*
|
||||||
|
* This works by using the standard Merkle tree building algorithm,
|
||||||
|
*/
|
||||||
|
void treehashx1(unsigned char *root, unsigned char *auth_path,
|
||||||
|
const spx_ctx* ctx,
|
||||||
|
uint32_t leaf_idx, uint32_t idx_offset,
|
||||||
|
uint32_t tree_height,
|
||||||
|
void (*gen_leaf)(
|
||||||
|
unsigned char* /* Where to write the leaves */,
|
||||||
|
const spx_ctx* /* ctx */,
|
||||||
|
uint32_t idx, void *info),
|
||||||
|
uint32_t tree_addr[8],
|
||||||
|
void *info)
|
||||||
|
{
|
||||||
|
/* This is where we keep the intermediate nodes */
|
||||||
|
SPX_VLA(uint8_t, stack, tree_height*SPX_N);
|
||||||
|
|
||||||
|
uint32_t idx;
|
||||||
|
uint32_t max_idx = (uint32_t)((1 << tree_height) - 1);
|
||||||
|
for (idx = 0;; idx++) {
|
||||||
|
unsigned char current[2*SPX_N]; /* Current logical node is at */
|
||||||
|
/* index[SPX_N]. We do this to minimize the number of copies */
|
||||||
|
/* needed during a thash */
|
||||||
|
gen_leaf( ¤t[SPX_N], ctx, idx + idx_offset,
|
||||||
|
info );
|
||||||
|
|
||||||
|
/* Now combine the freshly generated right node with previously */
|
||||||
|
/* generated left ones */
|
||||||
|
uint32_t internal_idx_offset = idx_offset;
|
||||||
|
uint32_t internal_idx = idx;
|
||||||
|
uint32_t internal_leaf = leaf_idx;
|
||||||
|
uint32_t h; /* The height we are in the Merkle tree */
|
||||||
|
for (h=0;; h++, internal_idx >>= 1, internal_leaf >>= 1) {
|
||||||
|
|
||||||
|
/* Check if we hit the top of the tree */
|
||||||
|
if (h == tree_height) {
|
||||||
|
/* We hit the root; return it */
|
||||||
|
memcpy( root, ¤t[SPX_N], SPX_N );
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Check if the node we have is a part of the
|
||||||
|
* authentication path; if it is, write it out
|
||||||
|
*/
|
||||||
|
if ((internal_idx ^ internal_leaf) == 0x01) {
|
||||||
|
memcpy( &auth_path[ h * SPX_N ],
|
||||||
|
¤t[SPX_N],
|
||||||
|
SPX_N );
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Check if we're at a left child; if so, stop going up the stack
|
||||||
|
* Exception: if we've reached the end of the tree, keep on going
|
||||||
|
* (so we combine the last 4 nodes into the one root node in two
|
||||||
|
* more iterations)
|
||||||
|
*/
|
||||||
|
if ((internal_idx & 1) == 0 && idx < max_idx) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Ok, we're at a right node */
|
||||||
|
/* Now combine the left and right logical nodes together */
|
||||||
|
|
||||||
|
/* Set the address of the node we're creating. */
|
||||||
|
internal_idx_offset >>= 1;
|
||||||
|
set_tree_height(tree_addr, h + 1);
|
||||||
|
set_tree_index(tree_addr, internal_idx/2 + internal_idx_offset );
|
||||||
|
|
||||||
|
unsigned char *left = &stack[h * SPX_N];
|
||||||
|
memcpy( ¤t[0], left, SPX_N );
|
||||||
|
thash( ¤t[1 * SPX_N],
|
||||||
|
¤t[0 * SPX_N],
|
||||||
|
2, ctx, tree_addr);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* We've hit a left child; save the current for when we get the */
|
||||||
|
/* corresponding right right */
|
||||||
|
memcpy( &stack[h * SPX_N], ¤t[SPX_N], SPX_N);
|
||||||
|
}
|
||||||
|
}
|
||||||
26
app/src/main/cpp/pq/sphincsplus/utilsx1.h
Normal file
26
app/src/main/cpp/pq/sphincsplus/utilsx1.h
Normal file
|
|
@ -0,0 +1,26 @@
|
||||||
|
#ifndef SPX_UTILSX4_H
|
||||||
|
#define SPX_UTILSX4_H
|
||||||
|
|
||||||
|
#include <stdint.h>
|
||||||
|
#include "params.h"
|
||||||
|
#include "context.h"
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 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.
|
||||||
|
*/
|
||||||
|
#define treehashx1 SPX_NAMESPACE(treehashx1)
|
||||||
|
void treehashx1(unsigned char *root, unsigned char *auth_path,
|
||||||
|
const spx_ctx* ctx,
|
||||||
|
uint32_t leaf_idx, uint32_t idx_offset, uint32_t tree_height,
|
||||||
|
void (*gen_leaf)(
|
||||||
|
unsigned char* /* Where to write the leaf */,
|
||||||
|
const spx_ctx* /* ctx */,
|
||||||
|
uint32_t addr_idx, void *info),
|
||||||
|
uint32_t tree_addrx4[8], void *info);
|
||||||
|
|
||||||
|
#endif
|
||||||
112
app/src/main/cpp/pq/sphincsplus/wots.c
Normal file
112
app/src/main/cpp/pq/sphincsplus/wots.c
Normal file
|
|
@ -0,0 +1,112 @@
|
||||||
|
#include <stdint.h>
|
||||||
|
#include <string.h>
|
||||||
|
|
||||||
|
#include "utils.h"
|
||||||
|
#include "utilsx1.h"
|
||||||
|
#include "hash.h"
|
||||||
|
#include "thash.h"
|
||||||
|
#include "wots.h"
|
||||||
|
#include "wotsx1.h"
|
||||||
|
#include "address.h"
|
||||||
|
#include "params.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 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 spx_ctx *ctx, 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++) {
|
||||||
|
set_hash_addr(addr, i);
|
||||||
|
thash(out, out, 1, ctx, 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(unsigned int *output, const int out_len,
|
||||||
|
const unsigned char *input)
|
||||||
|
{
|
||||||
|
int in = 0;
|
||||||
|
int out = 0;
|
||||||
|
unsigned char total;
|
||||||
|
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(unsigned int *csum_base_w,
|
||||||
|
const unsigned int *msg_base_w)
|
||||||
|
{
|
||||||
|
unsigned 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)) % 8);
|
||||||
|
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. */
|
||||||
|
void chain_lengths(unsigned int *lengths, const unsigned char *msg)
|
||||||
|
{
|
||||||
|
base_w(lengths, SPX_WOTS_LEN1, msg);
|
||||||
|
wots_checksum(lengths + SPX_WOTS_LEN1, lengths);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Takes a WOTS signature and an n-byte message, computes a WOTS public key.
|
||||||
|
*
|
||||||
|
* Writes the computed public key to 'pk'.
|
||||||
|
*/
|
||||||
|
void wots_pk_from_sig(unsigned char *pk,
|
||||||
|
const unsigned char *sig, const unsigned char *msg,
|
||||||
|
const spx_ctx *ctx, uint32_t addr[8])
|
||||||
|
{
|
||||||
|
unsigned int lengths[SPX_WOTS_LEN];
|
||||||
|
uint32_t i;
|
||||||
|
|
||||||
|
chain_lengths(lengths, msg);
|
||||||
|
|
||||||
|
for (i = 0; i < SPX_WOTS_LEN; i++) {
|
||||||
|
set_chain_addr(addr, i);
|
||||||
|
gen_chain(pk + i*SPX_N, sig + i*SPX_N,
|
||||||
|
lengths[i], SPX_WOTS_W - 1 - lengths[i], ctx, addr);
|
||||||
|
}
|
||||||
|
}
|
||||||
25
app/src/main/cpp/pq/sphincsplus/wots.h
Normal file
25
app/src/main/cpp/pq/sphincsplus/wots.h
Normal file
|
|
@ -0,0 +1,25 @@
|
||||||
|
#ifndef SPX_WOTS_H
|
||||||
|
#define SPX_WOTS_H
|
||||||
|
|
||||||
|
#include <stdint.h>
|
||||||
|
|
||||||
|
#include "params.h"
|
||||||
|
#include "context.h"
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Takes a WOTS signature and an n-byte message, computes a WOTS public key.
|
||||||
|
*
|
||||||
|
* Writes the computed public key to 'pk'.
|
||||||
|
*/
|
||||||
|
#define wots_pk_from_sig SPX_NAMESPACE(wots_pk_from_sig)
|
||||||
|
void wots_pk_from_sig(unsigned char *pk,
|
||||||
|
const unsigned char *sig, const unsigned char *msg,
|
||||||
|
const spx_ctx *ctx, uint32_t addr[8]);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Compute the chain lengths needed for a given message hash
|
||||||
|
*/
|
||||||
|
#define chain_lengths SPX_NAMESPACE(chain_lengths)
|
||||||
|
void chain_lengths(unsigned int *lengths, const unsigned char *msg);
|
||||||
|
|
||||||
|
#endif
|
||||||
73
app/src/main/cpp/pq/sphincsplus/wotsx1.c
Normal file
73
app/src/main/cpp/pq/sphincsplus/wotsx1.c
Normal file
|
|
@ -0,0 +1,73 @@
|
||||||
|
#include <stdint.h>
|
||||||
|
#include <string.h>
|
||||||
|
|
||||||
|
#include "utils.h"
|
||||||
|
#include "hash.h"
|
||||||
|
#include "thash.h"
|
||||||
|
#include "wots.h"
|
||||||
|
#include "wotsx1.h"
|
||||||
|
#include "address.h"
|
||||||
|
#include "params.h"
|
||||||
|
|
||||||
|
/*
|
||||||
|
* This generates a WOTS public key
|
||||||
|
* It also generates the WOTS signature if leaf_info indicates
|
||||||
|
* that we're signing with this WOTS key
|
||||||
|
*/
|
||||||
|
void wots_gen_leafx1(unsigned char *dest,
|
||||||
|
const spx_ctx *ctx,
|
||||||
|
uint32_t leaf_idx, void *v_info) {
|
||||||
|
struct leaf_info_x1 *info = v_info;
|
||||||
|
uint32_t *leaf_addr = info->leaf_addr;
|
||||||
|
uint32_t *pk_addr = info->pk_addr;
|
||||||
|
unsigned int i, k;
|
||||||
|
unsigned char pk_buffer[ SPX_WOTS_BYTES ];
|
||||||
|
unsigned char *buffer;
|
||||||
|
uint32_t wots_k_mask;
|
||||||
|
|
||||||
|
if (leaf_idx == info->wots_sign_leaf) {
|
||||||
|
/* We're traversing the leaf that's signing; generate the WOTS */
|
||||||
|
/* signature */
|
||||||
|
wots_k_mask = 0;
|
||||||
|
} else {
|
||||||
|
/* Nope, we're just generating pk's; turn off the signature logic */
|
||||||
|
wots_k_mask = (uint32_t)~0;
|
||||||
|
}
|
||||||
|
|
||||||
|
set_keypair_addr( leaf_addr, leaf_idx );
|
||||||
|
set_keypair_addr( pk_addr, leaf_idx );
|
||||||
|
|
||||||
|
for (i = 0, buffer = pk_buffer; i < SPX_WOTS_LEN; i++, buffer += SPX_N) {
|
||||||
|
uint32_t wots_k = info->wots_steps[i] | wots_k_mask; /* Set wots_k to */
|
||||||
|
/* the step if we're generating a signature, ~0 if we're not */
|
||||||
|
|
||||||
|
/* Start with the secret seed */
|
||||||
|
set_chain_addr(leaf_addr, i);
|
||||||
|
set_hash_addr(leaf_addr, 0);
|
||||||
|
set_type(leaf_addr, SPX_ADDR_TYPE_WOTSPRF);
|
||||||
|
|
||||||
|
prf_addr(buffer, ctx, leaf_addr);
|
||||||
|
|
||||||
|
set_type(leaf_addr, SPX_ADDR_TYPE_WOTS);
|
||||||
|
|
||||||
|
/* Iterate down the WOTS chain */
|
||||||
|
for (k=0;; k++) {
|
||||||
|
/* Check if this is the value that needs to be saved as a */
|
||||||
|
/* part of the WOTS signature */
|
||||||
|
if (k == wots_k) {
|
||||||
|
memcpy( info->wots_sig + i * SPX_N, buffer, SPX_N );
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Check if we hit the top of the chain */
|
||||||
|
if (k == SPX_WOTS_W - 1) break;
|
||||||
|
|
||||||
|
/* Iterate one step on the chain */
|
||||||
|
set_hash_addr(leaf_addr, k);
|
||||||
|
|
||||||
|
thash(buffer, buffer, 1, ctx, leaf_addr);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Do the final thash to generate the public keys */
|
||||||
|
thash(dest, pk_buffer, SPX_WOTS_LEN, ctx, pk_addr);
|
||||||
|
}
|
||||||
36
app/src/main/cpp/pq/sphincsplus/wotsx1.h
Normal file
36
app/src/main/cpp/pq/sphincsplus/wotsx1.h
Normal file
|
|
@ -0,0 +1,36 @@
|
||||||
|
#if !defined( WOTSX1_H_ )
|
||||||
|
#define WOTSX1_H_
|
||||||
|
|
||||||
|
#include <string.h>
|
||||||
|
|
||||||
|
/*
|
||||||
|
* This is here to provide an interface to the internal wots_gen_leafx1
|
||||||
|
* routine. While this routine is not referenced in the package outside of
|
||||||
|
* wots.c, it is called from the stand-alone benchmark code to characterize
|
||||||
|
* the performance
|
||||||
|
*/
|
||||||
|
struct leaf_info_x1 {
|
||||||
|
unsigned char *wots_sig;
|
||||||
|
uint32_t wots_sign_leaf; /* The index of the WOTS we're using to sign */
|
||||||
|
uint32_t *wots_steps;
|
||||||
|
uint32_t leaf_addr[8];
|
||||||
|
uint32_t pk_addr[8];
|
||||||
|
};
|
||||||
|
|
||||||
|
/* Macro to set the leaf_info to something 'benign', that is, it would */
|
||||||
|
/* run with the same time as it does during the real signing process */
|
||||||
|
/* Used only by the benchmark code */
|
||||||
|
#define INITIALIZE_LEAF_INFO_X1(info, addr, step_buffer) { \
|
||||||
|
info.wots_sig = 0; \
|
||||||
|
info.wots_sign_leaf = ~0u; \
|
||||||
|
info.wots_steps = step_buffer; \
|
||||||
|
memcpy( &info.leaf_addr[0], addr, 32 ); \
|
||||||
|
memcpy( &info.pk_addr[0], addr, 32 ); \
|
||||||
|
}
|
||||||
|
|
||||||
|
#define wots_gen_leafx1 SPX_NAMESPACE(wots_gen_leafx1)
|
||||||
|
void wots_gen_leafx1(unsigned char *dest,
|
||||||
|
const spx_ctx *ctx,
|
||||||
|
uint32_t leaf_idx, void *v_info);
|
||||||
|
|
||||||
|
#endif /* WOTSX1_H_ */
|
||||||
345
app/src/main/cpp/vexta_pq_jni.cpp
Normal file
345
app/src/main/cpp/vexta_pq_jni.cpp
Normal file
|
|
@ -0,0 +1,345 @@
|
||||||
|
#include <jni.h>
|
||||||
|
|
||||||
|
#include <array>
|
||||||
|
#include <cstdint>
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
|
#include "pq/pq.h"
|
||||||
|
|
||||||
|
namespace {
|
||||||
|
|
||||||
|
jbyteArray to_jbyte_array(
|
||||||
|
JNIEnv* env,
|
||||||
|
const uint8_t* data,
|
||||||
|
size_t size)
|
||||||
|
{
|
||||||
|
jbyteArray result =
|
||||||
|
env->NewByteArray(static_cast<jsize>(size));
|
||||||
|
|
||||||
|
if (result == nullptr) {
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
env->SetByteArrayRegion(
|
||||||
|
result,
|
||||||
|
0,
|
||||||
|
static_cast<jsize>(size),
|
||||||
|
reinterpret_cast<const jbyte*>(data)
|
||||||
|
);
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool copy_seed(
|
||||||
|
JNIEnv* env,
|
||||||
|
jbyteArray seed_array,
|
||||||
|
std::array<uint8_t, 64>& seed)
|
||||||
|
{
|
||||||
|
if (seed_array == nullptr ||
|
||||||
|
env->GetArrayLength(seed_array) != 64) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
env->GetByteArrayRegion(
|
||||||
|
seed_array,
|
||||||
|
0,
|
||||||
|
64,
|
||||||
|
reinterpret_cast<jbyte*>(seed.data())
|
||||||
|
);
|
||||||
|
|
||||||
|
return !env->ExceptionCheck();
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace
|
||||||
|
|
||||||
|
extern "C"
|
||||||
|
JNIEXPORT jstring JNICALL
|
||||||
|
Java_org_vextaproject_wallet_VextaPQ_nativeVersion(
|
||||||
|
JNIEnv* env,
|
||||||
|
jobject)
|
||||||
|
{
|
||||||
|
return env->NewStringUTF("Vexta PQ JNI 0.2");
|
||||||
|
}
|
||||||
|
|
||||||
|
extern "C"
|
||||||
|
JNIEXPORT jobjectArray JNICALL
|
||||||
|
Java_org_vextaproject_wallet_VextaPQ_mldsaKeypairFromSeed(
|
||||||
|
JNIEnv* env,
|
||||||
|
jobject,
|
||||||
|
jbyteArray seed_array)
|
||||||
|
{
|
||||||
|
std::array<uint8_t, 64> seed{};
|
||||||
|
if (!copy_seed(env, seed_array, seed)) {
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::vector<uint8_t> public_key(
|
||||||
|
pq::MLDSA65::PUBKEY_BYTES
|
||||||
|
);
|
||||||
|
std::vector<uint8_t> secret_key(
|
||||||
|
pq::MLDSA65::SECKEY_BYTES
|
||||||
|
);
|
||||||
|
|
||||||
|
if (!pq::MLDSA65::keygen_from_seed(
|
||||||
|
public_key.data(),
|
||||||
|
secret_key.data(),
|
||||||
|
seed.data())) {
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
jclass byte_array_class = env->FindClass("[B");
|
||||||
|
jobjectArray result =
|
||||||
|
env->NewObjectArray(2, byte_array_class, nullptr);
|
||||||
|
|
||||||
|
if (result == nullptr) {
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
env->SetObjectArrayElement(
|
||||||
|
result,
|
||||||
|
0,
|
||||||
|
to_jbyte_array(
|
||||||
|
env,
|
||||||
|
public_key.data(),
|
||||||
|
public_key.size()
|
||||||
|
)
|
||||||
|
);
|
||||||
|
|
||||||
|
env->SetObjectArrayElement(
|
||||||
|
result,
|
||||||
|
1,
|
||||||
|
to_jbyte_array(
|
||||||
|
env,
|
||||||
|
secret_key.data(),
|
||||||
|
secret_key.size()
|
||||||
|
)
|
||||||
|
);
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
extern "C"
|
||||||
|
JNIEXPORT jobjectArray JNICALL
|
||||||
|
Java_org_vextaproject_wallet_VextaPQ_sphincsKeypairFromSeed(
|
||||||
|
JNIEnv* env,
|
||||||
|
jobject,
|
||||||
|
jbyteArray seed_array)
|
||||||
|
{
|
||||||
|
std::array<uint8_t, 64> seed{};
|
||||||
|
if (!copy_seed(env, seed_array, seed)) {
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::vector<uint8_t> public_key(
|
||||||
|
pq::SPHINCS128s::PUBKEY_BYTES
|
||||||
|
);
|
||||||
|
std::vector<uint8_t> secret_key(
|
||||||
|
pq::SPHINCS128s::SECKEY_BYTES
|
||||||
|
);
|
||||||
|
|
||||||
|
if (!pq::SPHINCS128s::keygen_from_seed(
|
||||||
|
public_key.data(),
|
||||||
|
secret_key.data(),
|
||||||
|
seed.data())) {
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
jclass byte_array_class = env->FindClass("[B");
|
||||||
|
jobjectArray result =
|
||||||
|
env->NewObjectArray(2, byte_array_class, nullptr);
|
||||||
|
|
||||||
|
if (result == nullptr) {
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
env->SetObjectArrayElement(
|
||||||
|
result,
|
||||||
|
0,
|
||||||
|
to_jbyte_array(
|
||||||
|
env,
|
||||||
|
public_key.data(),
|
||||||
|
public_key.size()
|
||||||
|
)
|
||||||
|
);
|
||||||
|
|
||||||
|
env->SetObjectArrayElement(
|
||||||
|
result,
|
||||||
|
1,
|
||||||
|
to_jbyte_array(
|
||||||
|
env,
|
||||||
|
secret_key.data(),
|
||||||
|
secret_key.size()
|
||||||
|
)
|
||||||
|
);
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
namespace {
|
||||||
|
|
||||||
|
bool copy_byte_array(
|
||||||
|
JNIEnv* env,
|
||||||
|
jbyteArray input,
|
||||||
|
std::vector<uint8_t>& output)
|
||||||
|
{
|
||||||
|
if (input == nullptr) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
const jsize length = env->GetArrayLength(input);
|
||||||
|
|
||||||
|
output.resize(static_cast<size_t>(length));
|
||||||
|
|
||||||
|
if (length > 0) {
|
||||||
|
env->GetByteArrayRegion(
|
||||||
|
input,
|
||||||
|
0,
|
||||||
|
length,
|
||||||
|
reinterpret_cast<jbyte*>(output.data())
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
return !env->ExceptionCheck();
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace
|
||||||
|
|
||||||
|
extern "C"
|
||||||
|
JNIEXPORT jbyteArray JNICALL
|
||||||
|
Java_org_vextaproject_wallet_VextaPQ_mldsaSign(
|
||||||
|
JNIEnv* env,
|
||||||
|
jobject,
|
||||||
|
jbyteArray message_array,
|
||||||
|
jbyteArray secret_key_array)
|
||||||
|
{
|
||||||
|
std::vector<uint8_t> message;
|
||||||
|
std::vector<uint8_t> secret_key;
|
||||||
|
|
||||||
|
if (!copy_byte_array(env, message_array, message) ||
|
||||||
|
!copy_byte_array(env, secret_key_array, secret_key) ||
|
||||||
|
secret_key.size() != pq::MLDSA65::SECKEY_BYTES) {
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::vector<uint8_t> signature(
|
||||||
|
pq::MLDSA65::SIG_BYTES
|
||||||
|
);
|
||||||
|
|
||||||
|
size_t signature_length = signature.size();
|
||||||
|
|
||||||
|
if (!pq::MLDSA65::sign(
|
||||||
|
signature.data(),
|
||||||
|
&signature_length,
|
||||||
|
message.data(),
|
||||||
|
message.size(),
|
||||||
|
secret_key.data())) {
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
signature.resize(signature_length);
|
||||||
|
|
||||||
|
return to_jbyte_array(
|
||||||
|
env,
|
||||||
|
signature.data(),
|
||||||
|
signature.size()
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
extern "C"
|
||||||
|
JNIEXPORT jboolean JNICALL
|
||||||
|
Java_org_vextaproject_wallet_VextaPQ_mldsaVerify(
|
||||||
|
JNIEnv* env,
|
||||||
|
jobject,
|
||||||
|
jbyteArray signature_array,
|
||||||
|
jbyteArray message_array,
|
||||||
|
jbyteArray public_key_array)
|
||||||
|
{
|
||||||
|
std::vector<uint8_t> signature;
|
||||||
|
std::vector<uint8_t> message;
|
||||||
|
std::vector<uint8_t> public_key;
|
||||||
|
|
||||||
|
if (!copy_byte_array(env, signature_array, signature) ||
|
||||||
|
!copy_byte_array(env, message_array, message) ||
|
||||||
|
!copy_byte_array(env, public_key_array, public_key) ||
|
||||||
|
public_key.size() != pq::MLDSA65::PUBKEY_BYTES) {
|
||||||
|
return JNI_FALSE;
|
||||||
|
}
|
||||||
|
|
||||||
|
return pq::MLDSA65::verify(
|
||||||
|
signature.data(),
|
||||||
|
signature.size(),
|
||||||
|
message.data(),
|
||||||
|
message.size(),
|
||||||
|
public_key.data()
|
||||||
|
) ? JNI_TRUE : JNI_FALSE;
|
||||||
|
}
|
||||||
|
|
||||||
|
extern "C"
|
||||||
|
JNIEXPORT jbyteArray JNICALL
|
||||||
|
Java_org_vextaproject_wallet_VextaPQ_sphincsSign(
|
||||||
|
JNIEnv* env,
|
||||||
|
jobject,
|
||||||
|
jbyteArray message_array,
|
||||||
|
jbyteArray secret_key_array)
|
||||||
|
{
|
||||||
|
std::vector<uint8_t> message;
|
||||||
|
std::vector<uint8_t> secret_key;
|
||||||
|
|
||||||
|
if (!copy_byte_array(env, message_array, message) ||
|
||||||
|
!copy_byte_array(env, secret_key_array, secret_key) ||
|
||||||
|
secret_key.size() != pq::SPHINCS128s::SECKEY_BYTES) {
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::vector<uint8_t> signature(
|
||||||
|
pq::SPHINCS128s::SIG_BYTES
|
||||||
|
);
|
||||||
|
|
||||||
|
size_t signature_length = signature.size();
|
||||||
|
|
||||||
|
if (!pq::SPHINCS128s::sign(
|
||||||
|
signature.data(),
|
||||||
|
&signature_length,
|
||||||
|
message.data(),
|
||||||
|
message.size(),
|
||||||
|
secret_key.data())) {
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
signature.resize(signature_length);
|
||||||
|
|
||||||
|
return to_jbyte_array(
|
||||||
|
env,
|
||||||
|
signature.data(),
|
||||||
|
signature.size()
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
extern "C"
|
||||||
|
JNIEXPORT jboolean JNICALL
|
||||||
|
Java_org_vextaproject_wallet_VextaPQ_sphincsVerify(
|
||||||
|
JNIEnv* env,
|
||||||
|
jobject,
|
||||||
|
jbyteArray signature_array,
|
||||||
|
jbyteArray message_array,
|
||||||
|
jbyteArray public_key_array)
|
||||||
|
{
|
||||||
|
std::vector<uint8_t> signature;
|
||||||
|
std::vector<uint8_t> message;
|
||||||
|
std::vector<uint8_t> public_key;
|
||||||
|
|
||||||
|
if (!copy_byte_array(env, signature_array, signature) ||
|
||||||
|
!copy_byte_array(env, message_array, message) ||
|
||||||
|
!copy_byte_array(env, public_key_array, public_key) ||
|
||||||
|
public_key.size() != pq::SPHINCS128s::PUBKEY_BYTES) {
|
||||||
|
return JNI_FALSE;
|
||||||
|
}
|
||||||
|
|
||||||
|
return pq::SPHINCS128s::verify(
|
||||||
|
signature.data(),
|
||||||
|
signature.size(),
|
||||||
|
message.data(),
|
||||||
|
message.size(),
|
||||||
|
public_key.data()
|
||||||
|
) ? JNI_TRUE : JNI_FALSE;
|
||||||
|
}
|
||||||
93
app/src/main/cpp/vexta_pq_randombytes.cpp
Normal file
93
app/src/main/cpp/vexta_pq_randombytes.cpp
Normal file
|
|
@ -0,0 +1,93 @@
|
||||||
|
#include <cerrno>
|
||||||
|
#include <cstddef>
|
||||||
|
#include <cstdint>
|
||||||
|
#include <cstdlib>
|
||||||
|
|
||||||
|
#include <fcntl.h>
|
||||||
|
#include <sys/syscall.h>
|
||||||
|
#include <unistd.h>
|
||||||
|
|
||||||
|
namespace {
|
||||||
|
|
||||||
|
bool fill_from_getrandom(uint8_t* out, size_t outlen)
|
||||||
|
{
|
||||||
|
#ifdef SYS_getrandom
|
||||||
|
while (outlen > 0) {
|
||||||
|
const long result = syscall(
|
||||||
|
SYS_getrandom,
|
||||||
|
out,
|
||||||
|
outlen,
|
||||||
|
0
|
||||||
|
);
|
||||||
|
|
||||||
|
if (result > 0) {
|
||||||
|
out += static_cast<size_t>(result);
|
||||||
|
outlen -= static_cast<size_t>(result);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (result < 0 && errno == EINTR) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
#else
|
||||||
|
(void)out;
|
||||||
|
(void)outlen;
|
||||||
|
return false;
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
bool fill_from_urandom(uint8_t* out, size_t outlen)
|
||||||
|
{
|
||||||
|
const int fd = open("/dev/urandom", O_RDONLY | O_CLOEXEC);
|
||||||
|
if (fd < 0) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
while (outlen > 0) {
|
||||||
|
const ssize_t result = read(fd, out, outlen);
|
||||||
|
|
||||||
|
if (result > 0) {
|
||||||
|
out += static_cast<size_t>(result);
|
||||||
|
outlen -= static_cast<size_t>(result);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (result < 0 && errno == EINTR) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
close(fd);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
close(fd);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace
|
||||||
|
|
||||||
|
extern "C" void randombytes(uint8_t* out, size_t outlen)
|
||||||
|
{
|
||||||
|
if (outlen == 0) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (out == nullptr) {
|
||||||
|
std::abort();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (fill_from_getrandom(out, outlen)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (fill_from_urandom(out, outlen)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::abort();
|
||||||
|
}
|
||||||
|
|
@ -23,13 +23,20 @@ object BlockScanner {
|
||||||
private const val genesisHash =
|
private const val genesisHash =
|
||||||
"0000027626959d894ae1c8a6f9050cdcbdf58eb0037ab06345cb5502955370f1"
|
"0000027626959d894ae1c8a6f9050cdcbdf58eb0037ab06345cb5502955370f1"
|
||||||
|
|
||||||
|
enum class AddressType {
|
||||||
|
STANDARD,
|
||||||
|
MLDSA,
|
||||||
|
SLHDSA
|
||||||
|
}
|
||||||
|
|
||||||
data class SpendableUtxo(
|
data class SpendableUtxo(
|
||||||
val txid: String,
|
val txid: String,
|
||||||
val outputIndex: Long,
|
val outputIndex: Long,
|
||||||
val value: Long,
|
val value: Long,
|
||||||
val height: Int,
|
val height: Int,
|
||||||
val addressIndex: Int,
|
val addressIndex: Int,
|
||||||
val isCoinbase: Boolean
|
val isCoinbase: Boolean,
|
||||||
|
val addressType: AddressType = AddressType.STANDARD
|
||||||
)
|
)
|
||||||
|
|
||||||
data class WalletTransaction(
|
data class WalletTransaction(
|
||||||
|
|
@ -59,7 +66,8 @@ object BlockScanner {
|
||||||
val value: Long,
|
val value: Long,
|
||||||
val height: Int,
|
val height: Int,
|
||||||
val addressIndex: Int,
|
val addressIndex: Int,
|
||||||
val isCoinbase: Boolean
|
val isCoinbase: Boolean,
|
||||||
|
val addressType: AddressType
|
||||||
)
|
)
|
||||||
|
|
||||||
private data class ParsedTransaction(
|
private data class ParsedTransaction(
|
||||||
|
|
@ -86,13 +94,17 @@ object BlockScanner {
|
||||||
matchingHeights: List<Int>,
|
matchingHeights: List<Int>,
|
||||||
walletScript: ByteArray,
|
walletScript: ByteArray,
|
||||||
addressIndex: Int,
|
addressIndex: Int,
|
||||||
|
addressType: AddressType = AddressType.STANDARD,
|
||||||
initialUtxos: List<SpendableUtxo> = emptyList(),
|
initialUtxos: List<SpendableUtxo> = emptyList(),
|
||||||
progress: (Int, Int) -> Unit
|
progress: (Int, Int) -> Unit
|
||||||
): Result {
|
): Result {
|
||||||
if (matchingHeights.isEmpty()) {
|
if (matchingHeights.isEmpty()) {
|
||||||
val existing =
|
val existing =
|
||||||
initialUtxos
|
initialUtxos
|
||||||
.filter { it.addressIndex == addressIndex }
|
.filter {
|
||||||
|
it.addressIndex == addressIndex &&
|
||||||
|
it.addressType == addressType
|
||||||
|
}
|
||||||
.sortedWith(
|
.sortedWith(
|
||||||
compareBy<SpendableUtxo> { it.height }
|
compareBy<SpendableUtxo> { it.height }
|
||||||
.thenBy { it.txid }
|
.thenBy { it.txid }
|
||||||
|
|
@ -128,7 +140,10 @@ object BlockScanner {
|
||||||
val utxos = linkedMapOf<String, Utxo>()
|
val utxos = linkedMapOf<String, Utxo>()
|
||||||
|
|
||||||
initialUtxos
|
initialUtxos
|
||||||
.filter { it.addressIndex == addressIndex }
|
.filter {
|
||||||
|
it.addressIndex == addressIndex &&
|
||||||
|
it.addressType == addressType
|
||||||
|
}
|
||||||
.forEach { existing ->
|
.forEach { existing ->
|
||||||
val txidWire =
|
val txidWire =
|
||||||
existing.txid
|
existing.txid
|
||||||
|
|
@ -144,7 +159,8 @@ object BlockScanner {
|
||||||
value = existing.value,
|
value = existing.value,
|
||||||
height = existing.height,
|
height = existing.height,
|
||||||
addressIndex = existing.addressIndex,
|
addressIndex = existing.addressIndex,
|
||||||
isCoinbase = existing.isCoinbase
|
isCoinbase = existing.isCoinbase,
|
||||||
|
addressType = existing.addressType
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
var receivedTransactions = 0
|
var receivedTransactions = 0
|
||||||
|
|
@ -241,7 +257,8 @@ object BlockScanner {
|
||||||
value = transactionOutput.value,
|
value = transactionOutput.value,
|
||||||
height = height,
|
height = height,
|
||||||
addressIndex = addressIndex,
|
addressIndex = addressIndex,
|
||||||
isCoinbase = transaction.isCoinbase
|
isCoinbase = transaction.isCoinbase,
|
||||||
|
addressType = addressType
|
||||||
)
|
)
|
||||||
|
|
||||||
receivedWalletValue +=
|
receivedWalletValue +=
|
||||||
|
|
@ -290,7 +307,8 @@ object BlockScanner {
|
||||||
value = utxo.value,
|
value = utxo.value,
|
||||||
height = utxo.height,
|
height = utxo.height,
|
||||||
addressIndex = utxo.addressIndex,
|
addressIndex = utxo.addressIndex,
|
||||||
isCoinbase = utxo.isCoinbase
|
isCoinbase = utxo.isCoinbase,
|
||||||
|
addressType = utxo.addressType
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
39
app/src/main/java/org/vextaproject/wallet/VextaPQ.kt
Normal file
39
app/src/main/java/org/vextaproject/wallet/VextaPQ.kt
Normal file
|
|
@ -0,0 +1,39 @@
|
||||||
|
package org.vextaproject.wallet
|
||||||
|
|
||||||
|
object VextaPQ {
|
||||||
|
init {
|
||||||
|
System.loadLibrary("vextapq")
|
||||||
|
}
|
||||||
|
|
||||||
|
external fun nativeVersion(): String
|
||||||
|
|
||||||
|
external fun mldsaKeypairFromSeed(
|
||||||
|
seed: ByteArray
|
||||||
|
): Array<ByteArray>?
|
||||||
|
|
||||||
|
external fun sphincsKeypairFromSeed(
|
||||||
|
seed: ByteArray
|
||||||
|
): Array<ByteArray>?
|
||||||
|
|
||||||
|
external fun mldsaSign(
|
||||||
|
message: ByteArray,
|
||||||
|
secretKey: ByteArray
|
||||||
|
): ByteArray?
|
||||||
|
|
||||||
|
external fun mldsaVerify(
|
||||||
|
signature: ByteArray,
|
||||||
|
message: ByteArray,
|
||||||
|
publicKey: ByteArray
|
||||||
|
): Boolean
|
||||||
|
|
||||||
|
external fun sphincsSign(
|
||||||
|
message: ByteArray,
|
||||||
|
secretKey: ByteArray
|
||||||
|
): ByteArray?
|
||||||
|
|
||||||
|
external fun sphincsVerify(
|
||||||
|
signature: ByteArray,
|
||||||
|
message: ByteArray,
|
||||||
|
publicKey: ByteArray
|
||||||
|
): Boolean
|
||||||
|
}
|
||||||
|
|
@ -48,6 +48,13 @@ object VextaTransactionSender {
|
||||||
val fee: Long
|
val fee: Long
|
||||||
)
|
)
|
||||||
|
|
||||||
|
data class PqSigningKey(
|
||||||
|
val addressType: BlockScanner.AddressType,
|
||||||
|
val publicKey: ByteArray,
|
||||||
|
val secretKey: ByteArray
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
private const val COINBASE_MATURITY = 100
|
private const val COINBASE_MATURITY = 100
|
||||||
|
|
||||||
private fun validateCoinbaseMaturity(
|
private fun validateCoinbaseMaturity(
|
||||||
|
|
@ -90,9 +97,9 @@ object VextaTransactionSender {
|
||||||
Math.addExact(sum, utxo.value)
|
Math.addExact(sum, utxo.value)
|
||||||
}
|
}
|
||||||
|
|
||||||
val fee = estimateFee(
|
val fee = estimateFeeFromOutputVirtualBytes(
|
||||||
inputCount = spendableUtxos.size,
|
inputs = spendableUtxos,
|
||||||
outputCount = 1
|
outputVirtualBytes = listOf(43L)
|
||||||
)
|
)
|
||||||
|
|
||||||
val amount = total - fee
|
val amount = total - fee
|
||||||
|
|
@ -112,6 +119,8 @@ object VextaTransactionSender {
|
||||||
recipientAddress: String,
|
recipientAddress: String,
|
||||||
amountSatoshis: Long,
|
amountSatoshis: Long,
|
||||||
privateKeysByIndex: Map<Int, ECKey>,
|
privateKeysByIndex: Map<Int, ECKey>,
|
||||||
|
pqSigningKeys:
|
||||||
|
Map<Pair<BlockScanner.AddressType, Int>, PqSigningKey>,
|
||||||
changePubKeyHash: ByteArray,
|
changePubKeyHash: ByteArray,
|
||||||
chainHeight: Int
|
chainHeight: Int
|
||||||
): CreatedTransaction {
|
): CreatedTransaction {
|
||||||
|
|
@ -127,20 +136,33 @@ object VextaTransactionSender {
|
||||||
"Invalid change public-key hash"
|
"Invalid change public-key hash"
|
||||||
}
|
}
|
||||||
|
|
||||||
require(privateKeysByIndex.isNotEmpty()) {
|
require(
|
||||||
"No wallet private keys supplied"
|
privateKeysByIndex.isNotEmpty() ||
|
||||||
|
pqSigningKeys.isNotEmpty()
|
||||||
|
) {
|
||||||
|
"No wallet signing keys supplied"
|
||||||
}
|
}
|
||||||
|
|
||||||
val recipientScript = addressToScript(recipientAddress)
|
val recipientScript = addressToScript(recipientAddress)
|
||||||
val changeScript =
|
val changeScript =
|
||||||
byteArrayOf(0x00, 0x14) + changePubKeyHash
|
byteArrayOf(0x00, 0x14) + changePubKeyHash
|
||||||
|
|
||||||
val orderedUtxos = spendableUtxos.sortedWith(
|
val orderedUtxos =
|
||||||
compareBy<BlockScanner.SpendableUtxo> { it.height }
|
spendableUtxos
|
||||||
|
.sortedWith(
|
||||||
|
compareBy<BlockScanner.SpendableUtxo> {
|
||||||
|
it.height
|
||||||
|
}
|
||||||
.thenBy { it.txid }
|
.thenBy { it.txid }
|
||||||
.thenBy { it.outputIndex }
|
.thenBy { it.outputIndex }
|
||||||
)
|
)
|
||||||
|
|
||||||
|
if (orderedUtxos.isEmpty()) {
|
||||||
|
throw IllegalStateException(
|
||||||
|
"No spendable outputs available"
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
val maxSpend =
|
val maxSpend =
|
||||||
calculateMaxSpend(
|
calculateMaxSpend(
|
||||||
orderedUtxos,
|
orderedUtxos,
|
||||||
|
|
@ -172,8 +194,11 @@ object VextaTransactionSender {
|
||||||
Math.addExact(selectedValue, utxo.value)
|
Math.addExact(selectedValue, utxo.value)
|
||||||
|
|
||||||
val estimatedFee = estimateFee(
|
val estimatedFee = estimateFee(
|
||||||
inputCount = selected.size,
|
inputs = selected,
|
||||||
outputCount = 2
|
outputScripts = listOf(
|
||||||
|
recipientScript,
|
||||||
|
changeScript
|
||||||
|
)
|
||||||
)
|
)
|
||||||
|
|
||||||
if (
|
if (
|
||||||
|
|
@ -191,8 +216,11 @@ object VextaTransactionSender {
|
||||||
}
|
}
|
||||||
|
|
||||||
fee = estimateFee(
|
fee = estimateFee(
|
||||||
inputCount = selected.size,
|
inputs = selected,
|
||||||
outputCount = 2
|
outputScripts = listOf(
|
||||||
|
recipientScript,
|
||||||
|
changeScript
|
||||||
|
)
|
||||||
)
|
)
|
||||||
|
|
||||||
if (selectedValue < amountSatoshis + fee) {
|
if (selectedValue < amountSatoshis + fee) {
|
||||||
|
|
@ -267,6 +295,8 @@ object VextaTransactionSender {
|
||||||
}
|
}
|
||||||
|
|
||||||
selected.forEachIndexed { index, utxo ->
|
selected.forEachIndexed { index, utxo ->
|
||||||
|
when (utxo.addressType) {
|
||||||
|
BlockScanner.AddressType.STANDARD -> {
|
||||||
val privateKey =
|
val privateKey =
|
||||||
privateKeysByIndex[utxo.addressIndex]
|
privateKeysByIndex[utxo.addressIndex]
|
||||||
?: throw IllegalStateException(
|
?: throw IllegalStateException(
|
||||||
|
|
@ -292,7 +322,9 @@ object VextaTransactionSender {
|
||||||
false
|
false
|
||||||
)
|
)
|
||||||
|
|
||||||
transaction.getInput(index.toLong()).setWitness(
|
transaction
|
||||||
|
.getInput(index.toLong())
|
||||||
|
.setWitness(
|
||||||
TransactionWitness.redeemP2WPKH(
|
TransactionWitness.redeemP2WPKH(
|
||||||
signature,
|
signature,
|
||||||
privateKey
|
privateKey
|
||||||
|
|
@ -300,6 +332,92 @@ object VextaTransactionSender {
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
BlockScanner.AddressType.MLDSA,
|
||||||
|
BlockScanner.AddressType.SLHDSA -> {
|
||||||
|
val pqKey =
|
||||||
|
pqSigningKeys[
|
||||||
|
utxo.addressType to
|
||||||
|
utxo.addressIndex
|
||||||
|
] ?: throw IllegalStateException(
|
||||||
|
"Missing PQ signing key for " +
|
||||||
|
"${utxo.addressType} address index " +
|
||||||
|
utxo.addressIndex
|
||||||
|
)
|
||||||
|
|
||||||
|
require(
|
||||||
|
pqKey.addressType == utxo.addressType
|
||||||
|
) {
|
||||||
|
"PQ signing key type mismatch"
|
||||||
|
}
|
||||||
|
|
||||||
|
val witnessVersion =
|
||||||
|
when (utxo.addressType) {
|
||||||
|
BlockScanner.AddressType.MLDSA -> 2
|
||||||
|
BlockScanner.AddressType.SLHDSA -> 3
|
||||||
|
BlockScanner.AddressType.STANDARD ->
|
||||||
|
error("Standard input is not PQ")
|
||||||
|
}
|
||||||
|
|
||||||
|
val witnessProgram =
|
||||||
|
MessageDigest
|
||||||
|
.getInstance("SHA-256")
|
||||||
|
.digest(pqKey.publicKey)
|
||||||
|
|
||||||
|
val signatureHash =
|
||||||
|
calculatePqrSignatureHash(
|
||||||
|
selected = selected,
|
||||||
|
outputs = outputs,
|
||||||
|
inputIndex = index,
|
||||||
|
witnessVersion = witnessVersion,
|
||||||
|
witnessProgram = witnessProgram,
|
||||||
|
amount = utxo.value,
|
||||||
|
version = 2,
|
||||||
|
lockTime = 0L,
|
||||||
|
inputSequence = sequence
|
||||||
|
)
|
||||||
|
|
||||||
|
val signature =
|
||||||
|
try {
|
||||||
|
when (utxo.addressType) {
|
||||||
|
BlockScanner.AddressType.MLDSA ->
|
||||||
|
requireNotNull(
|
||||||
|
VextaPQ.mldsaSign(
|
||||||
|
signatureHash,
|
||||||
|
pqKey.secretKey
|
||||||
|
)
|
||||||
|
) {
|
||||||
|
"ML-DSA signing failed"
|
||||||
|
}
|
||||||
|
|
||||||
|
BlockScanner.AddressType.SLHDSA ->
|
||||||
|
requireNotNull(
|
||||||
|
VextaPQ.sphincsSign(
|
||||||
|
signatureHash,
|
||||||
|
pqKey.secretKey
|
||||||
|
)
|
||||||
|
) {
|
||||||
|
"SLH-DSA signing failed"
|
||||||
|
}
|
||||||
|
|
||||||
|
BlockScanner.AddressType.STANDARD ->
|
||||||
|
error("Standard input is not PQ")
|
||||||
|
}
|
||||||
|
} finally {
|
||||||
|
pqKey.secretKey.fill(0)
|
||||||
|
}
|
||||||
|
|
||||||
|
transaction
|
||||||
|
.getInput(index.toLong())
|
||||||
|
.setWitness(
|
||||||
|
createPqWitness(
|
||||||
|
signature,
|
||||||
|
pqKey.publicKey
|
||||||
|
)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
val rawTransaction =
|
val rawTransaction =
|
||||||
transaction.bitcoinSerialize()
|
transaction.bitcoinSerialize()
|
||||||
|
|
||||||
|
|
@ -340,6 +458,468 @@ object VextaTransactionSender {
|
||||||
return successfulPeers
|
return successfulPeers
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private fun calculatePqrSignatureHash(
|
||||||
|
selected: List<BlockScanner.SpendableUtxo>,
|
||||||
|
outputs: List<TransactionOutputData>,
|
||||||
|
inputIndex: Int,
|
||||||
|
witnessVersion: Int,
|
||||||
|
witnessProgram: ByteArray,
|
||||||
|
amount: Long,
|
||||||
|
version: Int = 2,
|
||||||
|
lockTime: Long = 0L,
|
||||||
|
inputSequence: Long = sequence
|
||||||
|
): ByteArray {
|
||||||
|
require(inputIndex in selected.indices) {
|
||||||
|
"Invalid PQR input index"
|
||||||
|
}
|
||||||
|
|
||||||
|
require(witnessVersion == 2 || witnessVersion == 3) {
|
||||||
|
"Invalid PQR witness version"
|
||||||
|
}
|
||||||
|
|
||||||
|
require(witnessProgram.size == 32) {
|
||||||
|
"Invalid PQR witness program"
|
||||||
|
}
|
||||||
|
|
||||||
|
val hashPrevouts = doubleSha256(
|
||||||
|
ByteArrayOutputStream().apply {
|
||||||
|
selected.forEach { utxo ->
|
||||||
|
write(hashDisplayToWire(utxo.txid))
|
||||||
|
writeUInt32LE(this, utxo.outputIndex)
|
||||||
|
}
|
||||||
|
}.toByteArray()
|
||||||
|
)
|
||||||
|
|
||||||
|
val hashSequence = doubleSha256(
|
||||||
|
ByteArrayOutputStream().apply {
|
||||||
|
selected.forEach {
|
||||||
|
writeUInt32LE(this, inputSequence)
|
||||||
|
}
|
||||||
|
}.toByteArray()
|
||||||
|
)
|
||||||
|
|
||||||
|
val hashOutputs = doubleSha256(
|
||||||
|
ByteArrayOutputStream().apply {
|
||||||
|
outputs.forEach { transactionOutput ->
|
||||||
|
writeInt64LE(this, transactionOutput.value)
|
||||||
|
writeCompactSize(
|
||||||
|
this,
|
||||||
|
transactionOutput.script.size.toLong()
|
||||||
|
)
|
||||||
|
write(transactionOutput.script)
|
||||||
|
}
|
||||||
|
}.toByteArray()
|
||||||
|
)
|
||||||
|
|
||||||
|
val currentInput = selected[inputIndex]
|
||||||
|
|
||||||
|
val scriptCode =
|
||||||
|
byteArrayOf(
|
||||||
|
if (witnessVersion == 2) {
|
||||||
|
0x52
|
||||||
|
} else {
|
||||||
|
0x53
|
||||||
|
},
|
||||||
|
0x20
|
||||||
|
) + witnessProgram
|
||||||
|
|
||||||
|
val preimage = ByteArrayOutputStream().apply {
|
||||||
|
write(0x00)
|
||||||
|
writeInt32LE(this, version)
|
||||||
|
|
||||||
|
write(hashPrevouts)
|
||||||
|
write(hashSequence)
|
||||||
|
|
||||||
|
write(hashDisplayToWire(currentInput.txid))
|
||||||
|
writeUInt32LE(this, currentInput.outputIndex)
|
||||||
|
|
||||||
|
writeCompactSize(this, scriptCode.size.toLong())
|
||||||
|
write(scriptCode)
|
||||||
|
|
||||||
|
writeInt64LE(this, amount)
|
||||||
|
writeUInt32LE(this, inputSequence)
|
||||||
|
|
||||||
|
write(hashOutputs)
|
||||||
|
|
||||||
|
writeUInt32LE(this, lockTime)
|
||||||
|
writeUInt32LE(this, 1L)
|
||||||
|
}.toByteArray()
|
||||||
|
|
||||||
|
return doubleSha256(preimage)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun runPqrSignatureHashSelfTest(): Pair<String, String> {
|
||||||
|
val selected = listOf(
|
||||||
|
BlockScanner.SpendableUtxo(
|
||||||
|
txid =
|
||||||
|
"0000000000000000000000000000000000000000000000000000000000000001",
|
||||||
|
outputIndex = 3L,
|
||||||
|
value = 987654L,
|
||||||
|
height = 1,
|
||||||
|
addressIndex = 0,
|
||||||
|
isCoinbase = false,
|
||||||
|
addressType = BlockScanner.AddressType.MLDSA
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
|
val outputs = listOf(
|
||||||
|
TransactionOutputData(
|
||||||
|
value = 123456L,
|
||||||
|
script = byteArrayOf(0x51)
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
|
val program =
|
||||||
|
ByteArray(32) { index ->
|
||||||
|
index.toByte()
|
||||||
|
}
|
||||||
|
|
||||||
|
val v2 = calculatePqrSignatureHash(
|
||||||
|
selected = selected,
|
||||||
|
outputs = outputs,
|
||||||
|
inputIndex = 0,
|
||||||
|
witnessVersion = 2,
|
||||||
|
witnessProgram = program,
|
||||||
|
amount = 987654L,
|
||||||
|
version = 2,
|
||||||
|
lockTime = 500L,
|
||||||
|
inputSequence = 0xfffffffeL
|
||||||
|
)
|
||||||
|
|
||||||
|
val v3 = calculatePqrSignatureHash(
|
||||||
|
selected = selected,
|
||||||
|
outputs = outputs,
|
||||||
|
inputIndex = 0,
|
||||||
|
witnessVersion = 3,
|
||||||
|
witnessProgram = program,
|
||||||
|
amount = 987654L,
|
||||||
|
version = 2,
|
||||||
|
lockTime = 500L,
|
||||||
|
inputSequence = 0xfffffffeL
|
||||||
|
)
|
||||||
|
|
||||||
|
val v2CoreHex = v2.reversedArray().toHex()
|
||||||
|
val v3CoreHex = v3.reversedArray().toHex()
|
||||||
|
|
||||||
|
check(
|
||||||
|
v2CoreHex ==
|
||||||
|
"ba39c447b4577db974473a1e7ed2182686672f2900045a40aee6b4dc5811995b"
|
||||||
|
) {
|
||||||
|
"PQR V2 signature hash self-test failed: $v2CoreHex"
|
||||||
|
}
|
||||||
|
|
||||||
|
check(
|
||||||
|
v3CoreHex ==
|
||||||
|
"dab3188530f1094ef87087451da77bfba77dfc6e63d1730bb9d0475cebe76618"
|
||||||
|
) {
|
||||||
|
"PQR V3 signature hash self-test failed: $v3CoreHex"
|
||||||
|
}
|
||||||
|
|
||||||
|
return v2CoreHex to v3CoreHex
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun createPqWitness(
|
||||||
|
signature: ByteArray,
|
||||||
|
publicKey: ByteArray
|
||||||
|
): TransactionWitness {
|
||||||
|
require(signature.isNotEmpty()) {
|
||||||
|
"PQ signature is empty"
|
||||||
|
}
|
||||||
|
|
||||||
|
require(publicKey.isNotEmpty()) {
|
||||||
|
"PQ public key is empty"
|
||||||
|
}
|
||||||
|
|
||||||
|
return TransactionWitness(2).apply {
|
||||||
|
setPush(0, signature)
|
||||||
|
setPush(1, publicKey)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun runPqWitnessSelfTest(): List<String> {
|
||||||
|
val seed =
|
||||||
|
ByteArray(64) { index ->
|
||||||
|
index.toByte()
|
||||||
|
}
|
||||||
|
|
||||||
|
val mldsa =
|
||||||
|
requireNotNull(
|
||||||
|
VextaPQ.mldsaKeypairFromSeed(seed)
|
||||||
|
)
|
||||||
|
|
||||||
|
val sphincs =
|
||||||
|
requireNotNull(
|
||||||
|
VextaPQ.sphincsKeypairFromSeed(seed)
|
||||||
|
)
|
||||||
|
|
||||||
|
val message =
|
||||||
|
"Vexta PQ witness self-test"
|
||||||
|
.toByteArray(Charsets.UTF_8)
|
||||||
|
|
||||||
|
val mldsaSignature =
|
||||||
|
requireNotNull(
|
||||||
|
VextaPQ.mldsaSign(
|
||||||
|
message,
|
||||||
|
mldsa[1]
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
|
val sphincsSignature =
|
||||||
|
requireNotNull(
|
||||||
|
VextaPQ.sphincsSign(
|
||||||
|
message,
|
||||||
|
sphincs[1]
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
|
val mldsaWitness =
|
||||||
|
createPqWitness(
|
||||||
|
mldsaSignature,
|
||||||
|
mldsa[0]
|
||||||
|
)
|
||||||
|
|
||||||
|
val sphincsWitness =
|
||||||
|
createPqWitness(
|
||||||
|
sphincsSignature,
|
||||||
|
sphincs[0]
|
||||||
|
)
|
||||||
|
|
||||||
|
check(mldsaWitness.pushCount == 2)
|
||||||
|
check(mldsaWitness.getPush(0).size == 3309)
|
||||||
|
check(mldsaWitness.getPush(1).size == 1952)
|
||||||
|
|
||||||
|
check(sphincsWitness.pushCount == 2)
|
||||||
|
check(sphincsWitness.getPush(0).size == 7856)
|
||||||
|
check(sphincsWitness.getPush(1).size == 32)
|
||||||
|
|
||||||
|
mldsa[1].fill(0)
|
||||||
|
sphincs[1].fill(0)
|
||||||
|
|
||||||
|
return listOf(
|
||||||
|
"MLDSA_WITNESS_PUSHES=${mldsaWitness.pushCount}",
|
||||||
|
"MLDSA_SIG_BYTES=${mldsaWitness.getPush(0).size}",
|
||||||
|
"MLDSA_PUBKEY_BYTES=${mldsaWitness.getPush(1).size}",
|
||||||
|
"SLHDSA_WITNESS_PUSHES=${sphincsWitness.pushCount}",
|
||||||
|
"SLHDSA_SIG_BYTES=${sphincsWitness.getPush(0).size}",
|
||||||
|
"SLHDSA_PUBKEY_BYTES=${sphincsWitness.getPush(1).size}",
|
||||||
|
"PQ_WITNESS_RESULT=PASS"
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun runPqSignedTransactionSelfTest(): List<String> {
|
||||||
|
val recipientAddress =
|
||||||
|
"vtx1z6engqms3emse5lycna6yt7gd6svu7nfd28dccr7mfs8s54pz8rysd6lu5u"
|
||||||
|
|
||||||
|
val recipientScript =
|
||||||
|
addressToScript(recipientAddress)
|
||||||
|
|
||||||
|
val changePubKeyHash =
|
||||||
|
ByteArray(20) { index ->
|
||||||
|
(0x40 + index).toByte()
|
||||||
|
}
|
||||||
|
|
||||||
|
val changeScript =
|
||||||
|
byteArrayOf(0x00, 0x14) + changePubKeyHash
|
||||||
|
|
||||||
|
val amountSatoshis = 100_000L
|
||||||
|
val utxoValue = 10_000_000L
|
||||||
|
|
||||||
|
fun testType(
|
||||||
|
addressType: BlockScanner.AddressType,
|
||||||
|
witnessVersion: Int,
|
||||||
|
expectedSignatureBytes: Int,
|
||||||
|
expectedPublicKeyBytes: Int,
|
||||||
|
txidSuffix: String
|
||||||
|
): List<String> {
|
||||||
|
val seed =
|
||||||
|
ByteArray(64) { index ->
|
||||||
|
index.toByte()
|
||||||
|
}
|
||||||
|
|
||||||
|
val keyPair =
|
||||||
|
when (addressType) {
|
||||||
|
BlockScanner.AddressType.MLDSA ->
|
||||||
|
requireNotNull(
|
||||||
|
VextaPQ.mldsaKeypairFromSeed(seed)
|
||||||
|
)
|
||||||
|
|
||||||
|
BlockScanner.AddressType.SLHDSA ->
|
||||||
|
requireNotNull(
|
||||||
|
VextaPQ.sphincsKeypairFromSeed(seed)
|
||||||
|
)
|
||||||
|
|
||||||
|
BlockScanner.AddressType.STANDARD ->
|
||||||
|
error("Standard is not a PQ self-test type")
|
||||||
|
}
|
||||||
|
|
||||||
|
require(keyPair.size == 2)
|
||||||
|
|
||||||
|
val publicKey = keyPair[0]
|
||||||
|
val secretKey = keyPair[1]
|
||||||
|
|
||||||
|
val syntheticTxid =
|
||||||
|
txidSuffix.padStart(64, '0')
|
||||||
|
|
||||||
|
val utxo =
|
||||||
|
BlockScanner.SpendableUtxo(
|
||||||
|
txid = syntheticTxid,
|
||||||
|
outputIndex = 0L,
|
||||||
|
value = utxoValue,
|
||||||
|
height = 1,
|
||||||
|
addressIndex = 0,
|
||||||
|
isCoinbase = false,
|
||||||
|
addressType = addressType
|
||||||
|
)
|
||||||
|
|
||||||
|
val pqSigningKeys =
|
||||||
|
mapOf(
|
||||||
|
(addressType to 0) to
|
||||||
|
PqSigningKey(
|
||||||
|
addressType = addressType,
|
||||||
|
publicKey = publicKey,
|
||||||
|
secretKey = secretKey
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
|
val created =
|
||||||
|
createAndSign(
|
||||||
|
spendableUtxos = listOf(utxo),
|
||||||
|
recipientAddress = recipientAddress,
|
||||||
|
amountSatoshis = amountSatoshis,
|
||||||
|
privateKeysByIndex = emptyMap(),
|
||||||
|
pqSigningKeys = pqSigningKeys,
|
||||||
|
changePubKeyHash = changePubKeyHash,
|
||||||
|
chainHeight = 10_000
|
||||||
|
)
|
||||||
|
|
||||||
|
val parsed =
|
||||||
|
Transaction(
|
||||||
|
MainNetParams.get(),
|
||||||
|
created.rawTransaction
|
||||||
|
)
|
||||||
|
|
||||||
|
check(parsed.inputs.size == 1)
|
||||||
|
|
||||||
|
val witness =
|
||||||
|
parsed.getInput(0L).getWitness()
|
||||||
|
|
||||||
|
check(witness.pushCount == 2)
|
||||||
|
|
||||||
|
val signature =
|
||||||
|
witness.getPush(0)
|
||||||
|
|
||||||
|
val parsedPublicKey =
|
||||||
|
witness.getPush(1)
|
||||||
|
|
||||||
|
check(signature.size == expectedSignatureBytes)
|
||||||
|
check(parsedPublicKey.size == expectedPublicKeyBytes)
|
||||||
|
check(parsedPublicKey.contentEquals(publicKey))
|
||||||
|
|
||||||
|
check(
|
||||||
|
parsed.txId.toString() ==
|
||||||
|
created.txid
|
||||||
|
)
|
||||||
|
|
||||||
|
val witnessProgram =
|
||||||
|
MessageDigest
|
||||||
|
.getInstance("SHA-256")
|
||||||
|
.digest(publicKey)
|
||||||
|
|
||||||
|
val outputs =
|
||||||
|
mutableListOf(
|
||||||
|
TransactionOutputData(
|
||||||
|
value = created.amount,
|
||||||
|
script = recipientScript
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
|
if (created.change > 0L) {
|
||||||
|
outputs.add(
|
||||||
|
TransactionOutputData(
|
||||||
|
value = created.change,
|
||||||
|
script = changeScript
|
||||||
|
)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
val signatureHash =
|
||||||
|
calculatePqrSignatureHash(
|
||||||
|
selected = listOf(utxo),
|
||||||
|
outputs = outputs,
|
||||||
|
inputIndex = 0,
|
||||||
|
witnessVersion = witnessVersion,
|
||||||
|
witnessProgram = witnessProgram,
|
||||||
|
amount = utxo.value,
|
||||||
|
version = 2,
|
||||||
|
lockTime = 0L,
|
||||||
|
inputSequence = sequence
|
||||||
|
)
|
||||||
|
|
||||||
|
val verified =
|
||||||
|
when (addressType) {
|
||||||
|
BlockScanner.AddressType.MLDSA ->
|
||||||
|
VextaPQ.mldsaVerify(
|
||||||
|
signature,
|
||||||
|
signatureHash,
|
||||||
|
publicKey
|
||||||
|
)
|
||||||
|
|
||||||
|
BlockScanner.AddressType.SLHDSA ->
|
||||||
|
VextaPQ.sphincsVerify(
|
||||||
|
signature,
|
||||||
|
signatureHash,
|
||||||
|
publicKey
|
||||||
|
)
|
||||||
|
|
||||||
|
BlockScanner.AddressType.STANDARD ->
|
||||||
|
false
|
||||||
|
}
|
||||||
|
|
||||||
|
check(verified)
|
||||||
|
|
||||||
|
seed.fill(0)
|
||||||
|
signatureHash.fill(0)
|
||||||
|
|
||||||
|
return listOf(
|
||||||
|
"${addressType.name}_SIGNED_TX_BYTES=" +
|
||||||
|
created.rawTransaction.size,
|
||||||
|
"${addressType.name}_SIGNED_TX_WITNESS_PUSHES=" +
|
||||||
|
witness.pushCount,
|
||||||
|
"${addressType.name}_SIGNED_TX_SIG_BYTES=" +
|
||||||
|
signature.size,
|
||||||
|
"${addressType.name}_SIGNED_TX_PUBKEY_BYTES=" +
|
||||||
|
parsedPublicKey.size,
|
||||||
|
"${addressType.name}_SIGNED_TX_VERIFY=$verified",
|
||||||
|
"${addressType.name}_SIGNED_TX_TXID_MATCH=true"
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
val results =
|
||||||
|
mutableListOf<String>()
|
||||||
|
|
||||||
|
results +=
|
||||||
|
testType(
|
||||||
|
addressType = BlockScanner.AddressType.MLDSA,
|
||||||
|
witnessVersion = 2,
|
||||||
|
expectedSignatureBytes = 3309,
|
||||||
|
expectedPublicKeyBytes = 1952,
|
||||||
|
txidSuffix = "01"
|
||||||
|
)
|
||||||
|
|
||||||
|
results +=
|
||||||
|
testType(
|
||||||
|
addressType = BlockScanner.AddressType.SLHDSA,
|
||||||
|
witnessVersion = 3,
|
||||||
|
expectedSignatureBytes = 7856,
|
||||||
|
expectedPublicKeyBytes = 32,
|
||||||
|
txidSuffix = "02"
|
||||||
|
)
|
||||||
|
|
||||||
|
results += "PQ_SIGNED_TX_RESULT=PASS"
|
||||||
|
|
||||||
|
return results
|
||||||
|
}
|
||||||
|
|
||||||
private fun serializeTransaction(
|
private fun serializeTransaction(
|
||||||
selected: List<BlockScanner.SpendableUtxo>,
|
selected: List<BlockScanner.SpendableUtxo>,
|
||||||
outputs: List<TransactionOutputData>,
|
outputs: List<TransactionOutputData>,
|
||||||
|
|
@ -401,14 +981,64 @@ object VextaTransactionSender {
|
||||||
}.toByteArray()
|
}.toByteArray()
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun estimateFee(
|
private fun estimatedInputVirtualBytes(
|
||||||
inputCount: Int,
|
addressType: BlockScanner.AddressType
|
||||||
outputCount: Int
|
|
||||||
): Long {
|
): Long {
|
||||||
|
return when (addressType) {
|
||||||
|
BlockScanner.AddressType.STANDARD ->
|
||||||
|
69L
|
||||||
|
|
||||||
|
BlockScanner.AddressType.MLDSA ->
|
||||||
|
1358L
|
||||||
|
|
||||||
|
BlockScanner.AddressType.SLHDSA ->
|
||||||
|
2015L
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun estimatedOutputVirtualBytes(
|
||||||
|
script: ByteArray
|
||||||
|
): Long {
|
||||||
|
val scriptLengthBytes =
|
||||||
|
when {
|
||||||
|
script.size < 253 -> 1L
|
||||||
|
script.size <= 0xffff -> 3L
|
||||||
|
else -> 5L
|
||||||
|
}
|
||||||
|
|
||||||
|
return 8L +
|
||||||
|
scriptLengthBytes +
|
||||||
|
script.size.toLong()
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun estimateFee(
|
||||||
|
inputs: List<BlockScanner.SpendableUtxo>,
|
||||||
|
outputScripts: List<ByteArray>
|
||||||
|
): Long {
|
||||||
|
return estimateFeeFromOutputVirtualBytes(
|
||||||
|
inputs = inputs,
|
||||||
|
outputVirtualBytes =
|
||||||
|
outputScripts.map {
|
||||||
|
estimatedOutputVirtualBytes(it)
|
||||||
|
}
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun estimateFeeFromOutputVirtualBytes(
|
||||||
|
inputs: List<BlockScanner.SpendableUtxo>,
|
||||||
|
outputVirtualBytes: List<Long>
|
||||||
|
): Long {
|
||||||
|
val inputVirtualBytes =
|
||||||
|
inputs.sumOf {
|
||||||
|
estimatedInputVirtualBytes(
|
||||||
|
it.addressType
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
val virtualBytes =
|
val virtualBytes =
|
||||||
11L +
|
11L +
|
||||||
inputCount * 69L +
|
inputVirtualBytes +
|
||||||
outputCount * 31L
|
outputVirtualBytes.sum()
|
||||||
|
|
||||||
return (
|
return (
|
||||||
virtualBytes * defaultFeeSatoshisPerKb + 999L
|
virtualBytes * defaultFeeSatoshisPerKb + 999L
|
||||||
|
|
@ -419,13 +1049,33 @@ object VextaTransactionSender {
|
||||||
val value = address.trim()
|
val value = address.trim()
|
||||||
|
|
||||||
if (value.lowercase().startsWith("vtx1")) {
|
if (value.lowercase().startsWith("vtx1")) {
|
||||||
val witnessProgram = decodeBech32Witness(value)
|
val (witnessVersion, witnessProgram) =
|
||||||
|
decodeBech32Witness(value)
|
||||||
|
|
||||||
|
return when (witnessVersion) {
|
||||||
|
0 -> {
|
||||||
require(witnessProgram.size == 20) {
|
require(witnessProgram.size == 20) {
|
||||||
"Only Vexta P2WPKH addresses are supported"
|
"Only Vexta P2WPKH witness v0 addresses are supported"
|
||||||
|
}
|
||||||
|
byteArrayOf(0x00, 0x14) + witnessProgram
|
||||||
}
|
}
|
||||||
|
|
||||||
return byteArrayOf(0x00, 0x14) + witnessProgram
|
2 -> {
|
||||||
|
require(witnessProgram.size == 32) {
|
||||||
|
"Invalid ML-DSA witness program"
|
||||||
|
}
|
||||||
|
byteArrayOf(0x52, 0x20) + witnessProgram
|
||||||
|
}
|
||||||
|
|
||||||
|
3 -> {
|
||||||
|
require(witnessProgram.size == 32) {
|
||||||
|
"Invalid SLH-DSA witness program"
|
||||||
|
}
|
||||||
|
byteArrayOf(0x53, 0x20) + witnessProgram
|
||||||
|
}
|
||||||
|
|
||||||
|
else -> error("Unsupported witness version")
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
val decoded = decodeBase58Check(value)
|
val decoded = decodeBase58Check(value)
|
||||||
|
|
@ -452,7 +1102,9 @@ object VextaTransactionSender {
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun decodeBech32Witness(address: String): ByteArray {
|
private fun decodeBech32Witness(
|
||||||
|
address: String
|
||||||
|
): Pair<Int, ByteArray> {
|
||||||
require(address == address.lowercase()) {
|
require(address == address.lowercase()) {
|
||||||
"Mixed-case Bech32 address"
|
"Mixed-case Bech32 address"
|
||||||
}
|
}
|
||||||
|
|
@ -477,21 +1129,38 @@ object VextaTransactionSender {
|
||||||
index
|
index
|
||||||
}
|
}
|
||||||
|
|
||||||
require(bech32Polymod(expandHrp(hrp) + values) == 1) {
|
val data = values.dropLast(6)
|
||||||
"Invalid Bech32 checksum"
|
require(data.isNotEmpty()) {
|
||||||
|
"Missing witness version"
|
||||||
}
|
}
|
||||||
|
|
||||||
val data = values.dropLast(6)
|
val witnessVersion = data[0]
|
||||||
require(data.isNotEmpty() && data[0] == 0) {
|
require(witnessVersion in 0..16) {
|
||||||
"Unsupported witness version"
|
"Unsupported witness version"
|
||||||
}
|
}
|
||||||
|
|
||||||
return convertBits(
|
val expectedChecksum =
|
||||||
|
if (witnessVersion == 0) 1 else 0x2bc830a3
|
||||||
|
|
||||||
|
require(
|
||||||
|
bech32Polymod(expandHrp(hrp) + values) ==
|
||||||
|
expectedChecksum
|
||||||
|
) {
|
||||||
|
if (witnessVersion == 0) {
|
||||||
|
"Invalid Bech32 checksum"
|
||||||
|
} else {
|
||||||
|
"Invalid Bech32m checksum"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
val witnessProgram = convertBits(
|
||||||
data.drop(1),
|
data.drop(1),
|
||||||
5,
|
5,
|
||||||
8,
|
8,
|
||||||
false
|
false
|
||||||
).map { it.toByte() }.toByteArray()
|
).map { it.toByte() }.toByteArray()
|
||||||
|
|
||||||
|
return witnessVersion to witnessProgram
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun convertBits(
|
private fun convertBits(
|
||||||
|
|
|
||||||
File diff suppressed because it is too large
Load diff
|
|
@ -37,6 +37,14 @@ class WalletMonitoringService : Service() {
|
||||||
private const val PREF_IV = "seed_iv"
|
private const val PREF_IV = "seed_iv"
|
||||||
private const val PREF_RECEIVE_ADDRESS_INDEX =
|
private const val PREF_RECEIVE_ADDRESS_INDEX =
|
||||||
"receive_address_index"
|
"receive_address_index"
|
||||||
|
private const val PREF_MLDSA_ADDRESS_INDEX =
|
||||||
|
"mldsa_address_index"
|
||||||
|
private const val PREF_SLHDSA_ADDRESS_INDEX =
|
||||||
|
"slhdsa_address_index"
|
||||||
|
private const val PREF_MLDSA_ADDRESS_CREATED =
|
||||||
|
"mldsa_address_created"
|
||||||
|
private const val PREF_SLHDSA_ADDRESS_CREATED =
|
||||||
|
"slhdsa_address_created"
|
||||||
private const val PREF_KNOWN_INCOMING_TXIDS =
|
private const val PREF_KNOWN_INCOMING_TXIDS =
|
||||||
"known_incoming_txids"
|
"known_incoming_txids"
|
||||||
private const val PREF_TX_NOTIFICATIONS_INITIALIZED =
|
private const val PREF_TX_NOTIFICATIONS_INITIALIZED =
|
||||||
|
|
@ -54,6 +62,16 @@ class WalletMonitoringService : Service() {
|
||||||
private const val BACKGROUND_TX_HISTORY_VERSION = 4
|
private const val BACKGROUND_TX_HISTORY_VERSION = 4
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private fun backgroundUtxoKey(
|
||||||
|
addressType: BlockScanner.AddressType,
|
||||||
|
addressIndex: Int
|
||||||
|
): String {
|
||||||
|
return PREF_BACKGROUND_UTXO_PREFIX +
|
||||||
|
addressType.name +
|
||||||
|
"_" +
|
||||||
|
addressIndex
|
||||||
|
}
|
||||||
|
|
||||||
private val handler = Handler(Looper.getMainLooper())
|
private val handler = Handler(Looper.getMainLooper())
|
||||||
private var scanRunning = false
|
private var scanRunning = false
|
||||||
|
|
||||||
|
|
@ -136,6 +154,16 @@ class WalletMonitoringService : Service() {
|
||||||
.getInt(PREF_RECEIVE_ADDRESS_INDEX, 0)
|
.getInt(PREF_RECEIVE_ADDRESS_INDEX, 0)
|
||||||
.coerceAtLeast(0)
|
.coerceAtLeast(0)
|
||||||
|
|
||||||
|
val highestMldsaAddressIndex =
|
||||||
|
preferences
|
||||||
|
.getInt(PREF_MLDSA_ADDRESS_INDEX, 0)
|
||||||
|
.coerceAtLeast(0)
|
||||||
|
|
||||||
|
val highestSlhdsaAddressIndex =
|
||||||
|
preferences
|
||||||
|
.getInt(PREF_SLHDSA_ADDRESS_INDEX, 0)
|
||||||
|
.coerceAtLeast(0)
|
||||||
|
|
||||||
val localHeight = chain.last().height
|
val localHeight = chain.last().height
|
||||||
|
|
||||||
preferences.edit()
|
preferences.edit()
|
||||||
|
|
@ -153,14 +181,45 @@ class WalletMonitoringService : Service() {
|
||||||
)
|
)
|
||||||
.coerceAtLeast(0)
|
.coerceAtLeast(0)
|
||||||
|
|
||||||
val backgroundCacheAvailable =
|
val standardCacheAvailable =
|
||||||
(0..highestAddressIndex).all { addressIndex ->
|
(0..highestAddressIndex).all { addressIndex ->
|
||||||
|
preferences.contains(
|
||||||
|
backgroundUtxoKey(
|
||||||
|
BlockScanner.AddressType.STANDARD,
|
||||||
|
addressIndex
|
||||||
|
)
|
||||||
|
) ||
|
||||||
preferences.contains(
|
preferences.contains(
|
||||||
PREF_BACKGROUND_UTXO_PREFIX +
|
PREF_BACKGROUND_UTXO_PREFIX +
|
||||||
addressIndex
|
addressIndex
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
val mldsaCacheAvailable =
|
||||||
|
(0..highestMldsaAddressIndex).all { addressIndex ->
|
||||||
|
preferences.contains(
|
||||||
|
backgroundUtxoKey(
|
||||||
|
BlockScanner.AddressType.MLDSA,
|
||||||
|
addressIndex
|
||||||
|
)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
val slhdsaCacheAvailable =
|
||||||
|
(0..highestSlhdsaAddressIndex).all { addressIndex ->
|
||||||
|
preferences.contains(
|
||||||
|
backgroundUtxoKey(
|
||||||
|
BlockScanner.AddressType.SLHDSA,
|
||||||
|
addressIndex
|
||||||
|
)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
val backgroundCacheAvailable =
|
||||||
|
standardCacheAvailable &&
|
||||||
|
mldsaCacheAvailable &&
|
||||||
|
slhdsaCacheAvailable
|
||||||
|
|
||||||
val transactionHistoryCurrent =
|
val transactionHistoryCurrent =
|
||||||
preferences.getInt(
|
preferences.getInt(
|
||||||
PREF_BACKGROUND_TX_HISTORY_VERSION,
|
PREF_BACKGROUND_TX_HISTORY_VERSION,
|
||||||
|
|
@ -192,12 +251,79 @@ class WalletMonitoringService : Service() {
|
||||||
mutableListOf<BlockScanner.WalletTransaction>()
|
mutableListOf<BlockScanner.WalletTransaction>()
|
||||||
|
|
||||||
val updatedUtxos =
|
val updatedUtxos =
|
||||||
linkedMapOf<Int, List<BlockScanner.SpendableUtxo>>()
|
linkedMapOf<
|
||||||
|
Pair<BlockScanner.AddressType, Int>,
|
||||||
|
List<BlockScanner.SpendableUtxo>
|
||||||
|
>()
|
||||||
|
|
||||||
|
val scanTargets =
|
||||||
|
buildList {
|
||||||
for (addressIndex in 0..highestAddressIndex) {
|
for (addressIndex in 0..highestAddressIndex) {
|
||||||
val script =
|
add(
|
||||||
deriveWitnessScript(words, addressIndex)
|
Triple(
|
||||||
|
BlockScanner.AddressType.STANDARD,
|
||||||
|
addressIndex,
|
||||||
|
deriveWitnessScript(
|
||||||
|
words,
|
||||||
|
addressIndex
|
||||||
|
)
|
||||||
|
)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
if (
|
||||||
|
preferences.getBoolean(
|
||||||
|
PREF_MLDSA_ADDRESS_CREATED,
|
||||||
|
false
|
||||||
|
)
|
||||||
|
) {
|
||||||
|
for (
|
||||||
|
addressIndex in
|
||||||
|
0..highestMldsaAddressIndex
|
||||||
|
) {
|
||||||
|
add(
|
||||||
|
Triple(
|
||||||
|
BlockScanner.AddressType.MLDSA,
|
||||||
|
addressIndex,
|
||||||
|
derivePqWitnessScript(
|
||||||
|
words,
|
||||||
|
BlockScanner.AddressType.MLDSA,
|
||||||
|
addressIndex
|
||||||
|
)
|
||||||
|
)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (
|
||||||
|
preferences.getBoolean(
|
||||||
|
PREF_SLHDSA_ADDRESS_CREATED,
|
||||||
|
false
|
||||||
|
)
|
||||||
|
) {
|
||||||
|
for (
|
||||||
|
addressIndex in
|
||||||
|
0..highestSlhdsaAddressIndex
|
||||||
|
) {
|
||||||
|
add(
|
||||||
|
Triple(
|
||||||
|
BlockScanner.AddressType.SLHDSA,
|
||||||
|
addressIndex,
|
||||||
|
derivePqWitnessScript(
|
||||||
|
words,
|
||||||
|
BlockScanner.AddressType.SLHDSA,
|
||||||
|
addressIndex
|
||||||
|
)
|
||||||
|
)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for (
|
||||||
|
(addressType, addressIndex, script)
|
||||||
|
in scanTargets
|
||||||
|
) {
|
||||||
val filters =
|
val filters =
|
||||||
CompactFilterClient.scan(
|
CompactFilterClient.scan(
|
||||||
this,
|
this,
|
||||||
|
|
@ -212,6 +338,7 @@ class WalletMonitoringService : Service() {
|
||||||
} else {
|
} else {
|
||||||
loadBackgroundUtxos(
|
loadBackgroundUtxos(
|
||||||
preferences,
|
preferences,
|
||||||
|
addressType,
|
||||||
addressIndex
|
addressIndex
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
@ -223,13 +350,15 @@ class WalletMonitoringService : Service() {
|
||||||
filters.matchingHeights,
|
filters.matchingHeights,
|
||||||
script,
|
script,
|
||||||
addressIndex,
|
addressIndex,
|
||||||
|
addressType = addressType,
|
||||||
initialUtxos = initialUtxos
|
initialUtxos = initialUtxos
|
||||||
) { _, _ -> }
|
) { _, _ -> }
|
||||||
|
|
||||||
allTransactions.addAll(blocks.transactions)
|
allTransactions.addAll(blocks.transactions)
|
||||||
|
|
||||||
updatedUtxos[addressIndex] =
|
updatedUtxos[
|
||||||
blocks.utxos
|
addressType to addressIndex
|
||||||
|
] = blocks.utxos
|
||||||
}
|
}
|
||||||
|
|
||||||
val mergedTransactions =
|
val mergedTransactions =
|
||||||
|
|
@ -297,15 +426,16 @@ class WalletMonitoringService : Service() {
|
||||||
)
|
)
|
||||||
|
|
||||||
for (
|
for (
|
||||||
addressIndex in 0..highestAddressIndex
|
(target, utxos) in updatedUtxos
|
||||||
) {
|
) {
|
||||||
|
val (addressType, addressIndex) = target
|
||||||
|
|
||||||
editor.putStringSet(
|
editor.putStringSet(
|
||||||
PREF_BACKGROUND_UTXO_PREFIX +
|
backgroundUtxoKey(
|
||||||
addressIndex,
|
addressType,
|
||||||
encodeBackgroundUtxos(
|
addressIndex
|
||||||
updatedUtxos[addressIndex]
|
),
|
||||||
?: emptyList()
|
encodeBackgroundUtxos(utxos)
|
||||||
)
|
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -536,25 +666,47 @@ class WalletMonitoringService : Service() {
|
||||||
utxo.value.toString(),
|
utxo.value.toString(),
|
||||||
utxo.height.toString(),
|
utxo.height.toString(),
|
||||||
utxo.addressIndex.toString(),
|
utxo.addressIndex.toString(),
|
||||||
utxo.isCoinbase.toString()
|
utxo.isCoinbase.toString(),
|
||||||
|
utxo.addressType.name
|
||||||
).joinToString("|")
|
).joinToString("|")
|
||||||
}.toSet()
|
}.toSet()
|
||||||
|
|
||||||
private fun loadBackgroundUtxos(
|
private fun loadBackgroundUtxos(
|
||||||
preferences: android.content.SharedPreferences,
|
preferences: android.content.SharedPreferences,
|
||||||
|
addressType: BlockScanner.AddressType,
|
||||||
addressIndex: Int
|
addressIndex: Int
|
||||||
): List<BlockScanner.SpendableUtxo> {
|
): List<BlockScanner.SpendableUtxo> {
|
||||||
|
val newKey =
|
||||||
|
backgroundUtxoKey(
|
||||||
|
addressType,
|
||||||
|
addressIndex
|
||||||
|
)
|
||||||
|
|
||||||
|
val legacyKey =
|
||||||
|
PREF_BACKGROUND_UTXO_PREFIX + addressIndex
|
||||||
|
|
||||||
val values =
|
val values =
|
||||||
|
if (preferences.contains(newKey)) {
|
||||||
preferences.getStringSet(
|
preferences.getStringSet(
|
||||||
PREF_BACKGROUND_UTXO_PREFIX + addressIndex,
|
newKey,
|
||||||
emptySet()
|
emptySet()
|
||||||
) ?: emptySet()
|
) ?: emptySet()
|
||||||
|
} else if (
|
||||||
|
addressType == BlockScanner.AddressType.STANDARD
|
||||||
|
) {
|
||||||
|
preferences.getStringSet(
|
||||||
|
legacyKey,
|
||||||
|
emptySet()
|
||||||
|
) ?: emptySet()
|
||||||
|
} else {
|
||||||
|
emptySet()
|
||||||
|
}
|
||||||
|
|
||||||
return values.mapNotNull { value ->
|
return values.mapNotNull { value ->
|
||||||
try {
|
try {
|
||||||
val parts = value.split("|")
|
val parts = value.split("|")
|
||||||
|
|
||||||
if (parts.size != 6) {
|
if (parts.size != 6 && parts.size != 7) {
|
||||||
return@mapNotNull null
|
return@mapNotNull null
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -564,7 +716,13 @@ class WalletMonitoringService : Service() {
|
||||||
value = parts[2].toLong(),
|
value = parts[2].toLong(),
|
||||||
height = parts[3].toInt(),
|
height = parts[3].toInt(),
|
||||||
addressIndex = parts[4].toInt(),
|
addressIndex = parts[4].toInt(),
|
||||||
isCoinbase = parts[5].toBooleanStrict()
|
isCoinbase = parts[5].toBooleanStrict(),
|
||||||
|
addressType =
|
||||||
|
if (parts.size == 7) {
|
||||||
|
BlockScanner.AddressType.valueOf(parts[6])
|
||||||
|
} else {
|
||||||
|
BlockScanner.AddressType.STANDARD
|
||||||
|
}
|
||||||
)
|
)
|
||||||
} catch (_: Exception) {
|
} catch (_: Exception) {
|
||||||
null
|
null
|
||||||
|
|
@ -619,6 +777,129 @@ class WalletMonitoringService : Service() {
|
||||||
.toString(Charsets.UTF_8)
|
.toString(Charsets.UTF_8)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private fun derivePqWitnessScript(
|
||||||
|
words: List<String>,
|
||||||
|
addressType: BlockScanner.AddressType,
|
||||||
|
addressIndex: Int
|
||||||
|
): ByteArray {
|
||||||
|
require(
|
||||||
|
addressType == BlockScanner.AddressType.MLDSA ||
|
||||||
|
addressType == BlockScanner.AddressType.SLHDSA
|
||||||
|
)
|
||||||
|
require(addressIndex >= 0)
|
||||||
|
|
||||||
|
MnemonicCode.INSTANCE.check(words)
|
||||||
|
|
||||||
|
val bip39Seed =
|
||||||
|
MnemonicCode.toSeed(words, "")
|
||||||
|
|
||||||
|
val masterKey =
|
||||||
|
HDKeyDerivation.createMasterPrivateKey(
|
||||||
|
bip39Seed
|
||||||
|
)
|
||||||
|
|
||||||
|
val walletSeed =
|
||||||
|
masterKey.privKeyBytes
|
||||||
|
|
||||||
|
val domain =
|
||||||
|
"Vexta-PQ-HD-v1"
|
||||||
|
.toByteArray(Charsets.US_ASCII)
|
||||||
|
|
||||||
|
val data =
|
||||||
|
ByteArray(domain.size + 1 + 4)
|
||||||
|
|
||||||
|
System.arraycopy(
|
||||||
|
domain,
|
||||||
|
0,
|
||||||
|
data,
|
||||||
|
0,
|
||||||
|
domain.size
|
||||||
|
)
|
||||||
|
|
||||||
|
var offset = domain.size
|
||||||
|
|
||||||
|
data[offset++] =
|
||||||
|
when (addressType) {
|
||||||
|
BlockScanner.AddressType.MLDSA -> 2
|
||||||
|
BlockScanner.AddressType.SLHDSA -> 3
|
||||||
|
BlockScanner.AddressType.STANDARD ->
|
||||||
|
error("Standard address is not PQ")
|
||||||
|
}.toByte()
|
||||||
|
|
||||||
|
data[offset++] =
|
||||||
|
((addressIndex ushr 24) and 0xff).toByte()
|
||||||
|
data[offset++] =
|
||||||
|
((addressIndex ushr 16) and 0xff).toByte()
|
||||||
|
data[offset++] =
|
||||||
|
((addressIndex ushr 8) and 0xff).toByte()
|
||||||
|
data[offset] =
|
||||||
|
(addressIndex and 0xff).toByte()
|
||||||
|
|
||||||
|
val mac =
|
||||||
|
javax.crypto.Mac.getInstance(
|
||||||
|
"HmacSHA512"
|
||||||
|
)
|
||||||
|
|
||||||
|
mac.init(
|
||||||
|
javax.crypto.spec.SecretKeySpec(
|
||||||
|
walletSeed,
|
||||||
|
"HmacSHA512"
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
|
val seedMaterial =
|
||||||
|
mac.doFinal(data)
|
||||||
|
|
||||||
|
val keyPair =
|
||||||
|
when (addressType) {
|
||||||
|
BlockScanner.AddressType.MLDSA ->
|
||||||
|
requireNotNull(
|
||||||
|
VextaPQ.mldsaKeypairFromSeed(
|
||||||
|
seedMaterial
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
|
BlockScanner.AddressType.SLHDSA ->
|
||||||
|
requireNotNull(
|
||||||
|
VextaPQ.sphincsKeypairFromSeed(
|
||||||
|
seedMaterial
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
|
BlockScanner.AddressType.STANDARD ->
|
||||||
|
error("Standard address is not PQ")
|
||||||
|
}
|
||||||
|
|
||||||
|
require(keyPair.size == 2)
|
||||||
|
|
||||||
|
val publicKey = keyPair[0]
|
||||||
|
val secretKey = keyPair[1]
|
||||||
|
|
||||||
|
return try {
|
||||||
|
val keyId =
|
||||||
|
java.security.MessageDigest
|
||||||
|
.getInstance("SHA-256")
|
||||||
|
.digest(publicKey)
|
||||||
|
|
||||||
|
byteArrayOf(
|
||||||
|
if (
|
||||||
|
addressType ==
|
||||||
|
BlockScanner.AddressType.MLDSA
|
||||||
|
) {
|
||||||
|
0x52.toByte()
|
||||||
|
} else {
|
||||||
|
0x53.toByte()
|
||||||
|
},
|
||||||
|
0x20
|
||||||
|
) + keyId
|
||||||
|
} finally {
|
||||||
|
secretKey.fill(0)
|
||||||
|
seedMaterial.fill(0)
|
||||||
|
walletSeed.fill(0)
|
||||||
|
bip39Seed.fill(0)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private fun deriveWitnessScript(
|
private fun deriveWitnessScript(
|
||||||
words: List<String>,
|
words: List<String>,
|
||||||
addressIndex: Int
|
addressIndex: Int
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue