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.properties
|
||||
app/build.gradle.kts.backup-*
|
||||
|
||||
app/.cxx/
|
||||
|
|
|
|||
|
|
@ -17,6 +17,7 @@ if (keystorePropertiesFile.exists()) {
|
|||
android {
|
||||
namespace = "org.vextaproject.wallet"
|
||||
compileSdk = 36
|
||||
ndkVersion = "27.3.13750724"
|
||||
|
||||
defaultConfig {
|
||||
applicationId = "org.vextaproject.wallet"
|
||||
|
|
@ -42,7 +43,19 @@ android {
|
|||
}
|
||||
}
|
||||
|
||||
externalNativeBuild {
|
||||
cmake {
|
||||
version = "3.22.1"
|
||||
path = file("src/main/cpp/CMakeLists.txt")
|
||||
}
|
||||
}
|
||||
|
||||
buildTypes {
|
||||
getByName("debug") {
|
||||
applicationIdSuffix = ".debug"
|
||||
versionNameSuffix = "-debug"
|
||||
}
|
||||
|
||||
getByName("release") {
|
||||
if (keystorePropertiesFile.exists()) {
|
||||
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 =
|
||||
"0000027626959d894ae1c8a6f9050cdcbdf58eb0037ab06345cb5502955370f1"
|
||||
|
||||
enum class AddressType {
|
||||
STANDARD,
|
||||
MLDSA,
|
||||
SLHDSA
|
||||
}
|
||||
|
||||
data class SpendableUtxo(
|
||||
val txid: String,
|
||||
val outputIndex: Long,
|
||||
val value: Long,
|
||||
val height: Int,
|
||||
val addressIndex: Int,
|
||||
val isCoinbase: Boolean
|
||||
val isCoinbase: Boolean,
|
||||
val addressType: AddressType = AddressType.STANDARD
|
||||
)
|
||||
|
||||
data class WalletTransaction(
|
||||
|
|
@ -59,7 +66,8 @@ object BlockScanner {
|
|||
val value: Long,
|
||||
val height: Int,
|
||||
val addressIndex: Int,
|
||||
val isCoinbase: Boolean
|
||||
val isCoinbase: Boolean,
|
||||
val addressType: AddressType
|
||||
)
|
||||
|
||||
private data class ParsedTransaction(
|
||||
|
|
@ -86,13 +94,17 @@ object BlockScanner {
|
|||
matchingHeights: List<Int>,
|
||||
walletScript: ByteArray,
|
||||
addressIndex: Int,
|
||||
addressType: AddressType = AddressType.STANDARD,
|
||||
initialUtxos: List<SpendableUtxo> = emptyList(),
|
||||
progress: (Int, Int) -> Unit
|
||||
): Result {
|
||||
if (matchingHeights.isEmpty()) {
|
||||
val existing =
|
||||
initialUtxos
|
||||
.filter { it.addressIndex == addressIndex }
|
||||
.filter {
|
||||
it.addressIndex == addressIndex &&
|
||||
it.addressType == addressType
|
||||
}
|
||||
.sortedWith(
|
||||
compareBy<SpendableUtxo> { it.height }
|
||||
.thenBy { it.txid }
|
||||
|
|
@ -128,7 +140,10 @@ object BlockScanner {
|
|||
val utxos = linkedMapOf<String, Utxo>()
|
||||
|
||||
initialUtxos
|
||||
.filter { it.addressIndex == addressIndex }
|
||||
.filter {
|
||||
it.addressIndex == addressIndex &&
|
||||
it.addressType == addressType
|
||||
}
|
||||
.forEach { existing ->
|
||||
val txidWire =
|
||||
existing.txid
|
||||
|
|
@ -144,7 +159,8 @@ object BlockScanner {
|
|||
value = existing.value,
|
||||
height = existing.height,
|
||||
addressIndex = existing.addressIndex,
|
||||
isCoinbase = existing.isCoinbase
|
||||
isCoinbase = existing.isCoinbase,
|
||||
addressType = existing.addressType
|
||||
)
|
||||
}
|
||||
var receivedTransactions = 0
|
||||
|
|
@ -241,7 +257,8 @@ object BlockScanner {
|
|||
value = transactionOutput.value,
|
||||
height = height,
|
||||
addressIndex = addressIndex,
|
||||
isCoinbase = transaction.isCoinbase
|
||||
isCoinbase = transaction.isCoinbase,
|
||||
addressType = addressType
|
||||
)
|
||||
|
||||
receivedWalletValue +=
|
||||
|
|
@ -290,7 +307,8 @@ object BlockScanner {
|
|||
value = utxo.value,
|
||||
height = utxo.height,
|
||||
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
|
||||
)
|
||||
|
||||
data class PqSigningKey(
|
||||
val addressType: BlockScanner.AddressType,
|
||||
val publicKey: ByteArray,
|
||||
val secretKey: ByteArray
|
||||
)
|
||||
|
||||
|
||||
private const val COINBASE_MATURITY = 100
|
||||
|
||||
private fun validateCoinbaseMaturity(
|
||||
|
|
@ -90,9 +97,9 @@ object VextaTransactionSender {
|
|||
Math.addExact(sum, utxo.value)
|
||||
}
|
||||
|
||||
val fee = estimateFee(
|
||||
inputCount = spendableUtxos.size,
|
||||
outputCount = 1
|
||||
val fee = estimateFeeFromOutputVirtualBytes(
|
||||
inputs = spendableUtxos,
|
||||
outputVirtualBytes = listOf(43L)
|
||||
)
|
||||
|
||||
val amount = total - fee
|
||||
|
|
@ -112,6 +119,8 @@ object VextaTransactionSender {
|
|||
recipientAddress: String,
|
||||
amountSatoshis: Long,
|
||||
privateKeysByIndex: Map<Int, ECKey>,
|
||||
pqSigningKeys:
|
||||
Map<Pair<BlockScanner.AddressType, Int>, PqSigningKey>,
|
||||
changePubKeyHash: ByteArray,
|
||||
chainHeight: Int
|
||||
): CreatedTransaction {
|
||||
|
|
@ -127,20 +136,33 @@ object VextaTransactionSender {
|
|||
"Invalid change public-key hash"
|
||||
}
|
||||
|
||||
require(privateKeysByIndex.isNotEmpty()) {
|
||||
"No wallet private keys supplied"
|
||||
require(
|
||||
privateKeysByIndex.isNotEmpty() ||
|
||||
pqSigningKeys.isNotEmpty()
|
||||
) {
|
||||
"No wallet signing keys supplied"
|
||||
}
|
||||
|
||||
val recipientScript = addressToScript(recipientAddress)
|
||||
val changeScript =
|
||||
byteArrayOf(0x00, 0x14) + changePubKeyHash
|
||||
|
||||
val orderedUtxos = spendableUtxos.sortedWith(
|
||||
compareBy<BlockScanner.SpendableUtxo> { it.height }
|
||||
val orderedUtxos =
|
||||
spendableUtxos
|
||||
.sortedWith(
|
||||
compareBy<BlockScanner.SpendableUtxo> {
|
||||
it.height
|
||||
}
|
||||
.thenBy { it.txid }
|
||||
.thenBy { it.outputIndex }
|
||||
)
|
||||
|
||||
if (orderedUtxos.isEmpty()) {
|
||||
throw IllegalStateException(
|
||||
"No spendable outputs available"
|
||||
)
|
||||
}
|
||||
|
||||
val maxSpend =
|
||||
calculateMaxSpend(
|
||||
orderedUtxos,
|
||||
|
|
@ -172,8 +194,11 @@ object VextaTransactionSender {
|
|||
Math.addExact(selectedValue, utxo.value)
|
||||
|
||||
val estimatedFee = estimateFee(
|
||||
inputCount = selected.size,
|
||||
outputCount = 2
|
||||
inputs = selected,
|
||||
outputScripts = listOf(
|
||||
recipientScript,
|
||||
changeScript
|
||||
)
|
||||
)
|
||||
|
||||
if (
|
||||
|
|
@ -191,8 +216,11 @@ object VextaTransactionSender {
|
|||
}
|
||||
|
||||
fee = estimateFee(
|
||||
inputCount = selected.size,
|
||||
outputCount = 2
|
||||
inputs = selected,
|
||||
outputScripts = listOf(
|
||||
recipientScript,
|
||||
changeScript
|
||||
)
|
||||
)
|
||||
|
||||
if (selectedValue < amountSatoshis + fee) {
|
||||
|
|
@ -267,6 +295,8 @@ object VextaTransactionSender {
|
|||
}
|
||||
|
||||
selected.forEachIndexed { index, utxo ->
|
||||
when (utxo.addressType) {
|
||||
BlockScanner.AddressType.STANDARD -> {
|
||||
val privateKey =
|
||||
privateKeysByIndex[utxo.addressIndex]
|
||||
?: throw IllegalStateException(
|
||||
|
|
@ -292,7 +322,9 @@ object VextaTransactionSender {
|
|||
false
|
||||
)
|
||||
|
||||
transaction.getInput(index.toLong()).setWitness(
|
||||
transaction
|
||||
.getInput(index.toLong())
|
||||
.setWitness(
|
||||
TransactionWitness.redeemP2WPKH(
|
||||
signature,
|
||||
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 =
|
||||
transaction.bitcoinSerialize()
|
||||
|
||||
|
|
@ -340,6 +458,468 @@ object VextaTransactionSender {
|
|||
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(
|
||||
selected: List<BlockScanner.SpendableUtxo>,
|
||||
outputs: List<TransactionOutputData>,
|
||||
|
|
@ -401,14 +981,64 @@ object VextaTransactionSender {
|
|||
}.toByteArray()
|
||||
}
|
||||
|
||||
private fun estimateFee(
|
||||
inputCount: Int,
|
||||
outputCount: Int
|
||||
private fun estimatedInputVirtualBytes(
|
||||
addressType: BlockScanner.AddressType
|
||||
): 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 =
|
||||
11L +
|
||||
inputCount * 69L +
|
||||
outputCount * 31L
|
||||
inputVirtualBytes +
|
||||
outputVirtualBytes.sum()
|
||||
|
||||
return (
|
||||
virtualBytes * defaultFeeSatoshisPerKb + 999L
|
||||
|
|
@ -419,13 +1049,33 @@ object VextaTransactionSender {
|
|||
val value = address.trim()
|
||||
|
||||
if (value.lowercase().startsWith("vtx1")) {
|
||||
val witnessProgram = decodeBech32Witness(value)
|
||||
val (witnessVersion, witnessProgram) =
|
||||
decodeBech32Witness(value)
|
||||
|
||||
return when (witnessVersion) {
|
||||
0 -> {
|
||||
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)
|
||||
|
|
@ -452,7 +1102,9 @@ object VextaTransactionSender {
|
|||
)
|
||||
}
|
||||
|
||||
private fun decodeBech32Witness(address: String): ByteArray {
|
||||
private fun decodeBech32Witness(
|
||||
address: String
|
||||
): Pair<Int, ByteArray> {
|
||||
require(address == address.lowercase()) {
|
||||
"Mixed-case Bech32 address"
|
||||
}
|
||||
|
|
@ -477,21 +1129,38 @@ object VextaTransactionSender {
|
|||
index
|
||||
}
|
||||
|
||||
require(bech32Polymod(expandHrp(hrp) + values) == 1) {
|
||||
"Invalid Bech32 checksum"
|
||||
val data = values.dropLast(6)
|
||||
require(data.isNotEmpty()) {
|
||||
"Missing witness version"
|
||||
}
|
||||
|
||||
val data = values.dropLast(6)
|
||||
require(data.isNotEmpty() && data[0] == 0) {
|
||||
val witnessVersion = data[0]
|
||||
require(witnessVersion in 0..16) {
|
||||
"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),
|
||||
5,
|
||||
8,
|
||||
false
|
||||
).map { it.toByte() }.toByteArray()
|
||||
|
||||
return witnessVersion to witnessProgram
|
||||
}
|
||||
|
||||
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_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 =
|
||||
"known_incoming_txids"
|
||||
private const val PREF_TX_NOTIFICATIONS_INITIALIZED =
|
||||
|
|
@ -54,6 +62,16 @@ class WalletMonitoringService : Service() {
|
|||
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 var scanRunning = false
|
||||
|
||||
|
|
@ -136,6 +154,16 @@ class WalletMonitoringService : Service() {
|
|||
.getInt(PREF_RECEIVE_ADDRESS_INDEX, 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
|
||||
|
||||
preferences.edit()
|
||||
|
|
@ -153,14 +181,45 @@ class WalletMonitoringService : Service() {
|
|||
)
|
||||
.coerceAtLeast(0)
|
||||
|
||||
val backgroundCacheAvailable =
|
||||
val standardCacheAvailable =
|
||||
(0..highestAddressIndex).all { addressIndex ->
|
||||
preferences.contains(
|
||||
backgroundUtxoKey(
|
||||
BlockScanner.AddressType.STANDARD,
|
||||
addressIndex
|
||||
)
|
||||
) ||
|
||||
preferences.contains(
|
||||
PREF_BACKGROUND_UTXO_PREFIX +
|
||||
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 =
|
||||
preferences.getInt(
|
||||
PREF_BACKGROUND_TX_HISTORY_VERSION,
|
||||
|
|
@ -192,12 +251,79 @@ class WalletMonitoringService : Service() {
|
|||
mutableListOf<BlockScanner.WalletTransaction>()
|
||||
|
||||
val updatedUtxos =
|
||||
linkedMapOf<Int, List<BlockScanner.SpendableUtxo>>()
|
||||
linkedMapOf<
|
||||
Pair<BlockScanner.AddressType, Int>,
|
||||
List<BlockScanner.SpendableUtxo>
|
||||
>()
|
||||
|
||||
val scanTargets =
|
||||
buildList {
|
||||
for (addressIndex in 0..highestAddressIndex) {
|
||||
val script =
|
||||
deriveWitnessScript(words, addressIndex)
|
||||
add(
|
||||
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 =
|
||||
CompactFilterClient.scan(
|
||||
this,
|
||||
|
|
@ -212,6 +338,7 @@ class WalletMonitoringService : Service() {
|
|||
} else {
|
||||
loadBackgroundUtxos(
|
||||
preferences,
|
||||
addressType,
|
||||
addressIndex
|
||||
)
|
||||
}
|
||||
|
|
@ -223,13 +350,15 @@ class WalletMonitoringService : Service() {
|
|||
filters.matchingHeights,
|
||||
script,
|
||||
addressIndex,
|
||||
addressType = addressType,
|
||||
initialUtxos = initialUtxos
|
||||
) { _, _ -> }
|
||||
|
||||
allTransactions.addAll(blocks.transactions)
|
||||
|
||||
updatedUtxos[addressIndex] =
|
||||
blocks.utxos
|
||||
updatedUtxos[
|
||||
addressType to addressIndex
|
||||
] = blocks.utxos
|
||||
}
|
||||
|
||||
val mergedTransactions =
|
||||
|
|
@ -297,15 +426,16 @@ class WalletMonitoringService : Service() {
|
|||
)
|
||||
|
||||
for (
|
||||
addressIndex in 0..highestAddressIndex
|
||||
(target, utxos) in updatedUtxos
|
||||
) {
|
||||
val (addressType, addressIndex) = target
|
||||
|
||||
editor.putStringSet(
|
||||
PREF_BACKGROUND_UTXO_PREFIX +
|
||||
addressIndex,
|
||||
encodeBackgroundUtxos(
|
||||
updatedUtxos[addressIndex]
|
||||
?: emptyList()
|
||||
)
|
||||
backgroundUtxoKey(
|
||||
addressType,
|
||||
addressIndex
|
||||
),
|
||||
encodeBackgroundUtxos(utxos)
|
||||
)
|
||||
}
|
||||
|
||||
|
|
@ -536,25 +666,47 @@ class WalletMonitoringService : Service() {
|
|||
utxo.value.toString(),
|
||||
utxo.height.toString(),
|
||||
utxo.addressIndex.toString(),
|
||||
utxo.isCoinbase.toString()
|
||||
utxo.isCoinbase.toString(),
|
||||
utxo.addressType.name
|
||||
).joinToString("|")
|
||||
}.toSet()
|
||||
|
||||
private fun loadBackgroundUtxos(
|
||||
preferences: android.content.SharedPreferences,
|
||||
addressType: BlockScanner.AddressType,
|
||||
addressIndex: Int
|
||||
): List<BlockScanner.SpendableUtxo> {
|
||||
val newKey =
|
||||
backgroundUtxoKey(
|
||||
addressType,
|
||||
addressIndex
|
||||
)
|
||||
|
||||
val legacyKey =
|
||||
PREF_BACKGROUND_UTXO_PREFIX + addressIndex
|
||||
|
||||
val values =
|
||||
if (preferences.contains(newKey)) {
|
||||
preferences.getStringSet(
|
||||
PREF_BACKGROUND_UTXO_PREFIX + addressIndex,
|
||||
newKey,
|
||||
emptySet()
|
||||
) ?: emptySet()
|
||||
} else if (
|
||||
addressType == BlockScanner.AddressType.STANDARD
|
||||
) {
|
||||
preferences.getStringSet(
|
||||
legacyKey,
|
||||
emptySet()
|
||||
) ?: emptySet()
|
||||
} else {
|
||||
emptySet()
|
||||
}
|
||||
|
||||
return values.mapNotNull { value ->
|
||||
try {
|
||||
val parts = value.split("|")
|
||||
|
||||
if (parts.size != 6) {
|
||||
if (parts.size != 6 && parts.size != 7) {
|
||||
return@mapNotNull null
|
||||
}
|
||||
|
||||
|
|
@ -564,7 +716,13 @@ class WalletMonitoringService : Service() {
|
|||
value = parts[2].toLong(),
|
||||
height = parts[3].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) {
|
||||
null
|
||||
|
|
@ -619,6 +777,129 @@ class WalletMonitoringService : Service() {
|
|||
.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(
|
||||
words: List<String>,
|
||||
addressIndex: Int
|
||||
|
|
|
|||
Loading…
Reference in a new issue