Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
c62cae0fd2 | ||
|
|
e68f47d8d1 | ||
|
|
fba35559f4 | ||
|
|
59ee1e0bb9 | ||
|
|
d7fa096714 | ||
|
|
e27f5ef27d | ||
|
|
7a2abc0115 | ||
|
|
ba44de3cfd | ||
|
|
45d9b111fb | ||
|
|
3e2a7150f9 | ||
|
|
e1f63b1727 | ||
|
|
6ee4b78298 |
215 changed files with 39785 additions and 1686 deletions
2
.gitignore
vendored
2
.gitignore
vendored
|
|
@ -8,3 +8,5 @@ local.properties
|
|||
*.keystore
|
||||
keystore.properties
|
||||
app/build.gradle.kts.backup-*
|
||||
|
||||
app/.cxx/
|
||||
|
|
|
|||
|
|
@ -17,13 +17,14 @@ if (keystorePropertiesFile.exists()) {
|
|||
android {
|
||||
namespace = "org.vextaproject.wallet"
|
||||
compileSdk = 36
|
||||
ndkVersion = "27.3.13750724"
|
||||
|
||||
defaultConfig {
|
||||
applicationId = "org.vextaproject.wallet"
|
||||
minSdk = 26
|
||||
targetSdk = 36
|
||||
versionCode = 34
|
||||
versionName = "0.6.4"
|
||||
versionCode = 41
|
||||
versionName = "0.7.2"
|
||||
}
|
||||
|
||||
signingConfigs {
|
||||
|
|
@ -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")
|
||||
|
|
|
|||
|
|
@ -7,6 +7,7 @@
|
|||
<uses-permission android:name="android.permission.POST_NOTIFICATIONS" />
|
||||
<uses-permission android:name="android.permission.FOREGROUND_SERVICE" />
|
||||
<uses-permission android:name="android.permission.FOREGROUND_SERVICE_SPECIAL_USE" />
|
||||
<uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED" />
|
||||
|
||||
<application
|
||||
android:allowBackup="false"
|
||||
|
|
@ -40,6 +41,17 @@
|
|||
android:value="Monitors Vexta wallet for incoming blockchain payments while the app is in the background." />
|
||||
</service>
|
||||
|
||||
|
||||
<receiver
|
||||
android:name=".BootReceiver"
|
||||
android:enabled="true"
|
||||
android:exported="true">
|
||||
<intent-filter>
|
||||
<action android:name="android.intent.action.BOOT_COMPLETED" />
|
||||
<action android:name="android.intent.action.MY_PACKAGE_REPLACED" />
|
||||
</intent-filter>
|
||||
</receiver>
|
||||
|
||||
<meta-data
|
||||
android:name="com.google.mlkit.vision.DEPENDENCIES"
|
||||
android:value="barcode_ui" />
|
||||
|
|
|
|||
62
app/src/main/cpp/CMakeLists.txt
Normal file
62
app/src/main/cpp/CMakeLists.txt
Normal file
|
|
@ -0,0 +1,62 @@
|
|||
cmake_minimum_required(VERSION 3.22.1)
|
||||
|
||||
project(vextapq LANGUAGES C CXX)
|
||||
|
||||
# RandomX 1.2.3 - same version used by Vexta Core v0.3.0
|
||||
add_subdirectory(randomx EXCLUDE_FROM_ALL)
|
||||
|
||||
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
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/randomx/src
|
||||
)
|
||||
|
||||
target_compile_definitions(
|
||||
vextapq
|
||||
PRIVATE
|
||||
DILITHIUM_MODE=3
|
||||
PARAMS=sphincs-sha2-128s
|
||||
)
|
||||
|
||||
find_library(log-lib log)
|
||||
|
||||
target_link_libraries(
|
||||
vextapq
|
||||
randomx
|
||||
${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_ */
|
||||
223
app/src/main/cpp/randomx/.github/workflows/c-cpp.yml
vendored
Normal file
223
app/src/main/cpp/randomx/.github/workflows/c-cpp.yml
vendored
Normal file
|
|
@ -0,0 +1,223 @@
|
|||
name: C/C++ CI
|
||||
|
||||
on:
|
||||
push:
|
||||
branches: [ master ]
|
||||
pull_request:
|
||||
|
||||
jobs:
|
||||
build-alpine:
|
||||
|
||||
timeout-minutes: 15
|
||||
runs-on: ubuntu-22.04
|
||||
|
||||
strategy:
|
||||
matrix:
|
||||
config:
|
||||
- {arch: x86_64, branch: latest-stable}
|
||||
- {arch: x86, branch: latest-stable}
|
||||
- {arch: aarch64, branch: latest-stable}
|
||||
- {arch: armhf, branch: latest-stable}
|
||||
- {arch: armv7, branch: latest-stable}
|
||||
- {arch: ppc64le, branch: latest-stable}
|
||||
- {arch: riscv64, branch: latest-stable}
|
||||
- {arch: s390x, branch: latest-stable}
|
||||
|
||||
steps:
|
||||
- name: Setup Alpine Linux
|
||||
uses: jirutka/setup-alpine@v1
|
||||
with:
|
||||
arch: ${{ matrix.config.arch }}
|
||||
branch: ${{ matrix.config.branch }}
|
||||
|
||||
- name: Install dependencies
|
||||
shell: alpine.sh --root {0}
|
||||
run: |
|
||||
apk add git cmake gcc g++ make
|
||||
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@v3
|
||||
with:
|
||||
submodules: true
|
||||
|
||||
- name: Build RandomX
|
||||
shell: alpine.sh {0}
|
||||
run: |
|
||||
mkdir build
|
||||
cd build
|
||||
cmake .. -DCMAKE_BUILD_TYPE=Release -DARCH=native
|
||||
make -j$(nproc)
|
||||
|
||||
- name: Run tests
|
||||
shell: alpine.sh {0}
|
||||
run: |
|
||||
build/randomx-tests
|
||||
|
||||
build-ubuntu:
|
||||
|
||||
timeout-minutes: 5
|
||||
runs-on: ${{ matrix.config.os }}
|
||||
|
||||
strategy:
|
||||
matrix:
|
||||
config:
|
||||
- {os: ubuntu-24.04, c: gcc-14, cpp: g++-14}
|
||||
- {os: ubuntu-24.04-arm, c: gcc-14, cpp: g++-14}
|
||||
|
||||
steps:
|
||||
- name: Install dependencies
|
||||
run: |
|
||||
sudo add-apt-repository -y ppa:ubuntu-toolchain-r/test
|
||||
sudo apt update
|
||||
sudo apt install -y git build-essential cmake ${{ matrix.config.c }} ${{ matrix.config.cpp }}
|
||||
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@v3
|
||||
with:
|
||||
submodules: true
|
||||
|
||||
- name: Build RandomX
|
||||
run: |
|
||||
mkdir build
|
||||
cd build
|
||||
cmake ..
|
||||
make -j$(nproc)
|
||||
|
||||
- name: Run tests
|
||||
run: |
|
||||
build/randomx-tests
|
||||
|
||||
build-windows-msys2:
|
||||
|
||||
timeout-minutes: 15
|
||||
runs-on: windows-latest
|
||||
|
||||
strategy:
|
||||
matrix:
|
||||
config:
|
||||
- {c: "gcc", cxx: "g++"}
|
||||
- {c: "clang", cxx: "clang++"}
|
||||
|
||||
defaults:
|
||||
run:
|
||||
shell: msys2 {0}
|
||||
|
||||
steps:
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@v3
|
||||
with:
|
||||
submodules: recursive
|
||||
|
||||
- name: Setup MSYS2
|
||||
uses: eine/setup-msys2@v2
|
||||
with:
|
||||
update: true
|
||||
install: mingw-w64-x86_64-toolchain mingw-w64-x86_64-clang mingw-w64-x86_64-lld mingw-w64-x86_64-cmake make
|
||||
|
||||
- name: Build RandomX
|
||||
run: |
|
||||
mkdir build
|
||||
cd build
|
||||
cmake .. -G "Unix Makefiles" -DCMAKE_C_COMPILER=${{ matrix.config.c }} -DCMAKE_CXX_COMPILER=${{ matrix.config.cxx }}
|
||||
make -j$(nproc)
|
||||
|
||||
- name: Run tests
|
||||
run: |
|
||||
build/randomx-tests.exe
|
||||
|
||||
build-windows-msbuild:
|
||||
|
||||
timeout-minutes: 5
|
||||
runs-on: windows-${{ matrix.config.os }}
|
||||
|
||||
strategy:
|
||||
matrix:
|
||||
config:
|
||||
- {arch: x64, os: 2022, vs: Visual Studio 17 2022, msbuild: "C:\\Program Files\\Microsoft Visual Studio\\2022\\Enterprise\\Msbuild\\Current\\Bin\\amd64\\"}
|
||||
- {arch: Win32, os: 2022, vs: Visual Studio 17 2022, msbuild: "C:\\Program Files\\Microsoft Visual Studio\\2022\\Enterprise\\Msbuild\\Current\\Bin\\"}
|
||||
|
||||
steps:
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@v3
|
||||
with:
|
||||
submodules: recursive
|
||||
|
||||
- name: Setup cmake
|
||||
uses: lukka/get-cmake@latest
|
||||
|
||||
- name: Build RandomX
|
||||
run: |
|
||||
mkdir build
|
||||
cd build
|
||||
cmake .. -G "${{ matrix.config.vs }}" -A ${{ matrix.config.arch }}
|
||||
& "${{ matrix.config.msbuild }}msbuild" -v:m /m /p:Configuration=Release randomx-tests.vcxproj
|
||||
|
||||
- name: Run tests
|
||||
run: |
|
||||
build/Release/randomx-tests.exe
|
||||
|
||||
build-macos:
|
||||
|
||||
timeout-minutes: 5
|
||||
runs-on: ${{ matrix.os }}
|
||||
|
||||
strategy:
|
||||
matrix:
|
||||
os: [macos-15, macos-15-intel]
|
||||
|
||||
steps:
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@v3
|
||||
with:
|
||||
submodules: recursive
|
||||
|
||||
- name: Build RandomX
|
||||
run: |
|
||||
mkdir build
|
||||
cd build
|
||||
cmake ..
|
||||
make -j4
|
||||
|
||||
- name: Run tests
|
||||
run: |
|
||||
build/randomx-tests
|
||||
|
||||
build-freebsd:
|
||||
|
||||
timeout-minutes: 15
|
||||
runs-on: ${{ matrix.os.host }}
|
||||
|
||||
strategy:
|
||||
matrix:
|
||||
os:
|
||||
- name: freebsd
|
||||
architecture: x86-64
|
||||
version: '13.3'
|
||||
host: ubuntu-latest
|
||||
|
||||
- name: freebsd
|
||||
architecture: arm64
|
||||
version: '13.3'
|
||||
host: ubuntu-latest
|
||||
|
||||
steps:
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@v3
|
||||
with:
|
||||
submodules: recursive
|
||||
|
||||
- name: Build RandomX
|
||||
uses: cross-platform-actions/action@v0.24.0
|
||||
with:
|
||||
operating_system: ${{ matrix.os.name }}
|
||||
architecture: ${{ matrix.os.architecture }}
|
||||
version: ${{ matrix.os.version }}
|
||||
memory: 12G
|
||||
cpu_count: 4
|
||||
shell: bash
|
||||
run: |
|
||||
sudo pkg install -y cmake
|
||||
mkdir build && cd build
|
||||
cmake ..
|
||||
make -j2
|
||||
./randomx-tests
|
||||
274
app/src/main/cpp/randomx/CMakeLists.txt
Normal file
274
app/src/main/cpp/randomx/CMakeLists.txt
Normal file
|
|
@ -0,0 +1,274 @@
|
|||
# Copyright (c) 2019, The Monero Project
|
||||
#
|
||||
# All rights reserved.
|
||||
#
|
||||
# Redistribution and use in source and binary forms, with or without modification, are
|
||||
# permitted provided that the following conditions are met:
|
||||
#
|
||||
# 1. Redistributions of source code must retain the above copyright notice, this list of
|
||||
# conditions and the following disclaimer.
|
||||
#
|
||||
# 2. Redistributions in binary form must reproduce the above copyright notice, this list
|
||||
# of conditions and the following disclaimer in the documentation and/or other
|
||||
# materials provided with the distribution.
|
||||
#
|
||||
# 3. Neither the name of the copyright holder nor the names of its contributors may be
|
||||
# used to endorse or promote products derived from this software without specific
|
||||
# prior written permission.
|
||||
#
|
||||
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY
|
||||
# EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
|
||||
# MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
|
||||
# THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
|
||||
# PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||
# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
|
||||
# STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF
|
||||
# THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
cmake_minimum_required(VERSION 3.10)
|
||||
|
||||
project(RandomX)
|
||||
|
||||
set(randomx_sources
|
||||
src/aes_hash.cpp
|
||||
src/argon2_ref.c
|
||||
src/argon2_ssse3.c
|
||||
src/argon2_avx2.c
|
||||
src/bytecode_machine.cpp
|
||||
src/cpu.cpp
|
||||
src/dataset.cpp
|
||||
src/soft_aes.cpp
|
||||
src/virtual_memory.c
|
||||
src/vm_interpreted.cpp
|
||||
src/allocator.cpp
|
||||
src/assembly_generator_x86.cpp
|
||||
src/instruction.cpp
|
||||
src/randomx.cpp
|
||||
src/superscalar.cpp
|
||||
src/vm_compiled.cpp
|
||||
src/vm_interpreted_light.cpp
|
||||
src/argon2_core.c
|
||||
src/blake2_generator.cpp
|
||||
src/instructions_portable.cpp
|
||||
src/reciprocal.c
|
||||
src/virtual_machine.cpp
|
||||
src/vm_compiled_light.cpp
|
||||
src/blake2/blake2b.c)
|
||||
|
||||
if(NOT ARCH_ID)
|
||||
# allow cross compiling
|
||||
if(CMAKE_SYSTEM_PROCESSOR STREQUAL "")
|
||||
set(CMAKE_SYSTEM_PROCESSOR ${CMAKE_HOST_SYSTEM_PROCESSOR})
|
||||
endif()
|
||||
string(TOLOWER "${CMAKE_SYSTEM_PROCESSOR}" ARCH_ID)
|
||||
endif()
|
||||
|
||||
if(NOT ARM_ID)
|
||||
set(ARM_ID "${ARCH_ID}")
|
||||
endif()
|
||||
|
||||
if(NOT ARCH)
|
||||
set(ARCH "default")
|
||||
endif()
|
||||
|
||||
if(NOT CMAKE_BUILD_TYPE)
|
||||
set(CMAKE_BUILD_TYPE Release)
|
||||
message(STATUS "Setting default build type: ${CMAKE_BUILD_TYPE}")
|
||||
endif()
|
||||
|
||||
include(CheckCXXCompilerFlag)
|
||||
include(CheckCCompilerFlag)
|
||||
|
||||
function(add_flag flag)
|
||||
string(REPLACE "-" "_" supported_cxx ${flag}_cxx)
|
||||
check_cxx_compiler_flag(${flag} ${supported_cxx})
|
||||
if(${${supported_cxx}})
|
||||
message(STATUS "Setting CXX flag ${flag}")
|
||||
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} ${flag}" PARENT_SCOPE)
|
||||
endif()
|
||||
string(REPLACE "-" "_" supported_c ${flag}_c)
|
||||
check_c_compiler_flag(${flag} ${supported_c})
|
||||
if(${${supported_c}})
|
||||
message(STATUS "Setting C flag ${flag}")
|
||||
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} ${flag}" PARENT_SCOPE)
|
||||
endif()
|
||||
endfunction()
|
||||
|
||||
# x86-64
|
||||
if ((CMAKE_SIZEOF_VOID_P EQUAL 8) AND (ARCH_ID STREQUAL "x86_64" OR ARCH_ID STREQUAL "x86-64" OR ARCH_ID STREQUAL "amd64"))
|
||||
list(APPEND randomx_sources
|
||||
src/jit_compiler_x86.cpp)
|
||||
|
||||
if(MSVC)
|
||||
enable_language(ASM_MASM)
|
||||
list(APPEND randomx_sources src/jit_compiler_x86_static.asm)
|
||||
|
||||
set_property(SOURCE src/jit_compiler_x86_static.asm PROPERTY LANGUAGE ASM_MASM)
|
||||
|
||||
set_source_files_properties(src/argon2_avx2.c COMPILE_FLAGS /arch:AVX2)
|
||||
|
||||
set(CMAKE_C_FLAGS_RELWITHDEBINFO "${CMAKE_C_FLAGS_RELWITHDEBINFO} /DRELWITHDEBINFO")
|
||||
set(CMAKE_CXX_FLAGS_RELWITHDEBINFO "${CMAKE_CXX_FLAGS_RELWITHDEBINFO} /DRELWITHDEBINFO")
|
||||
|
||||
add_custom_command(OUTPUT ${CMAKE_CURRENT_SOURCE_DIR}/src/asm/configuration.asm
|
||||
COMMAND powershell -ExecutionPolicy Bypass -File h2inc.ps1 ..\\src\\configuration.h > ..\\src\\asm\\configuration.asm SET ERRORLEVEL = 0
|
||||
COMMENT "Generating configuration.asm at ${CMAKE_CURRENT_SOURCE_DIR}"
|
||||
WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}/vcxproj)
|
||||
add_custom_target(generate-asm
|
||||
DEPENDS ${CMAKE_CURRENT_SOURCE_DIR}/src/asm/configuration.asm)
|
||||
else()
|
||||
list(APPEND randomx_sources src/jit_compiler_x86_static.S)
|
||||
|
||||
# cheat because cmake and ccache hate each other
|
||||
set_property(SOURCE src/jit_compiler_x86_static.S PROPERTY LANGUAGE C)
|
||||
set_property(SOURCE src/jit_compiler_x86_static.S PROPERTY XCODE_EXPLICIT_FILE_TYPE sourcecode.asm)
|
||||
|
||||
if(ARCH STREQUAL "native")
|
||||
add_flag("-march=native")
|
||||
else()
|
||||
# default build has hardware AES enabled (software AES can be selected at runtime)
|
||||
add_flag("-maes")
|
||||
check_c_compiler_flag(-mssse3 HAVE_SSSE3)
|
||||
if(HAVE_SSSE3)
|
||||
set_source_files_properties(src/argon2_ssse3.c COMPILE_FLAGS -mssse3)
|
||||
endif()
|
||||
check_c_compiler_flag(-mavx2 HAVE_AVX2)
|
||||
if(HAVE_AVX2)
|
||||
set_source_files_properties(src/argon2_avx2.c COMPILE_FLAGS -mavx2)
|
||||
endif()
|
||||
endif()
|
||||
endif()
|
||||
endif()
|
||||
|
||||
# PowerPC
|
||||
if(ARCH_ID STREQUAL "ppc64" OR ARCH_ID STREQUAL "ppc64le")
|
||||
if(ARCH STREQUAL "native")
|
||||
add_flag("-mcpu=native")
|
||||
endif()
|
||||
# PowerPC AES requires ALTIVEC (POWER7+), so it cannot be enabled in the default build
|
||||
endif()
|
||||
|
||||
# ARMv8
|
||||
if(ARM_ID STREQUAL "aarch64" OR ARM_ID STREQUAL "arm64" OR ARM_ID STREQUAL "armv8-a")
|
||||
list(APPEND randomx_sources
|
||||
src/jit_compiler_a64_static.S
|
||||
src/jit_compiler_a64.cpp)
|
||||
# cheat because cmake and ccache hate each other
|
||||
set_property(SOURCE src/jit_compiler_a64_static.S PROPERTY LANGUAGE C)
|
||||
set_property(SOURCE src/jit_compiler_a64_static.S PROPERTY XCODE_EXPLICIT_FILE_TYPE sourcecode.asm)
|
||||
|
||||
# not sure if this check is needed
|
||||
include(CheckIncludeFile)
|
||||
check_include_file(asm/hwcap.h HAVE_HWCAP)
|
||||
if(HAVE_HWCAP)
|
||||
add_definitions(-DHAVE_HWCAP)
|
||||
endif()
|
||||
|
||||
if(ARCH STREQUAL "native")
|
||||
add_flag("-march=native")
|
||||
else()
|
||||
# default build has hardware AES enabled (software AES can be selected at runtime)
|
||||
add_flag("-march=armv8-a+crypto")
|
||||
endif()
|
||||
endif()
|
||||
|
||||
# RISC-V
|
||||
if(ARCH_ID STREQUAL "riscv64")
|
||||
list(APPEND randomx_sources
|
||||
src/jit_compiler_rv64_static.S
|
||||
src/jit_compiler_rv64.cpp)
|
||||
# cheat because cmake and ccache hate each other
|
||||
set_property(SOURCE src/jit_compiler_rv64_static.S PROPERTY LANGUAGE C)
|
||||
set_property(SOURCE src/jit_compiler_rv64_static.S PROPERTY XCODE_EXPLICIT_FILE_TYPE sourcecode.asm)
|
||||
|
||||
# default build uses the RV64GC baseline
|
||||
set(RVARCH "rv64gc")
|
||||
|
||||
# for native builds, enable Zba and Zbb if supported by the CPU
|
||||
if(ARCH STREQUAL "native")
|
||||
enable_language(ASM)
|
||||
try_run(RANDOMX_ZBA_RUN_FAIL
|
||||
RANDOMX_ZBA_COMPILE_OK
|
||||
${CMAKE_CURRENT_BINARY_DIR}/
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/src/tests/riscv64_zba.s
|
||||
COMPILE_DEFINITIONS "-march=rv64gc_zba")
|
||||
if (RANDOMX_ZBA_COMPILE_OK AND NOT RANDOMX_ZBA_RUN_FAIL)
|
||||
set(RVARCH "${RVARCH}_zba")
|
||||
endif()
|
||||
try_run(RANDOMX_ZBB_RUN_FAIL
|
||||
RANDOMX_ZBB_COMPILE_OK
|
||||
${CMAKE_CURRENT_BINARY_DIR}/
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/src/tests/riscv64_zbb.s
|
||||
COMPILE_DEFINITIONS "-march=rv64gc_zbb")
|
||||
if (RANDOMX_ZBB_COMPILE_OK AND NOT RANDOMX_ZBB_RUN_FAIL)
|
||||
set(RVARCH "${RVARCH}_zbb")
|
||||
endif()
|
||||
endif()
|
||||
|
||||
add_flag("-march=${RVARCH}")
|
||||
endif()
|
||||
|
||||
set(RANDOMX_INCLUDE "${CMAKE_CURRENT_SOURCE_DIR}/src" CACHE STRING "RandomX Include path")
|
||||
|
||||
add_library(randomx ${randomx_sources})
|
||||
|
||||
if(TARGET generate-asm)
|
||||
add_dependencies(randomx generate-asm)
|
||||
endif()
|
||||
|
||||
set_property(TARGET randomx PROPERTY POSITION_INDEPENDENT_CODE ON)
|
||||
set_property(TARGET randomx PROPERTY CXX_STANDARD 11)
|
||||
set_property(TARGET randomx PROPERTY CXX_STANDARD_REQUIRED ON)
|
||||
set_property(TARGET randomx PROPERTY PUBLIC_HEADER src/randomx.h)
|
||||
|
||||
include(GNUInstallDirs)
|
||||
install(TARGETS randomx
|
||||
LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR}
|
||||
ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR}
|
||||
PUBLIC_HEADER DESTINATION ${CMAKE_INSTALL_INCLUDEDIR})
|
||||
|
||||
if(NOT ANDROID)
|
||||
add_executable(randomx-tests
|
||||
src/tests/tests.cpp)
|
||||
target_link_libraries(randomx-tests
|
||||
PRIVATE randomx)
|
||||
set_property(TARGET randomx-tests PROPERTY POSITION_INDEPENDENT_CODE ON)
|
||||
set_property(TARGET randomx-tests PROPERTY CXX_STANDARD 11)
|
||||
|
||||
add_executable(randomx-codegen
|
||||
src/tests/code-generator.cpp)
|
||||
target_link_libraries(randomx-codegen
|
||||
PRIVATE randomx)
|
||||
|
||||
set_property(TARGET randomx-codegen PROPERTY POSITION_INDEPENDENT_CODE ON)
|
||||
set_property(TARGET randomx-codegen PROPERTY CXX_STANDARD 11)
|
||||
|
||||
if(NOT Threads_FOUND AND UNIX AND NOT APPLE)
|
||||
set(THREADS_PREFER_PTHREAD_FLAG ON)
|
||||
find_package(Threads)
|
||||
endif()
|
||||
|
||||
add_executable(randomx-benchmark
|
||||
src/tests/benchmark.cpp
|
||||
src/tests/affinity.cpp)
|
||||
target_link_libraries(randomx-benchmark
|
||||
PRIVATE randomx
|
||||
PRIVATE ${CMAKE_THREAD_LIBS_INIT})
|
||||
|
||||
include(CheckCXXSourceCompiles)
|
||||
check_cxx_source_compiles("
|
||||
#include <cstdint>
|
||||
#include <atomic>
|
||||
int main() {
|
||||
std::atomic<uint64_t> a;
|
||||
a.is_lock_free();
|
||||
}" HAVE_CXX_ATOMICS)
|
||||
|
||||
if(NOT HAVE_CXX_ATOMICS)
|
||||
target_link_libraries(randomx-benchmark
|
||||
PRIVATE "atomic")
|
||||
endif()
|
||||
set_property(TARGET randomx-benchmark PROPERTY POSITION_INDEPENDENT_CODE ON)
|
||||
set_property(TARGET randomx-benchmark PROPERTY CXX_STANDARD 11)
|
||||
endif()
|
||||
27
app/src/main/cpp/randomx/LICENSE
Normal file
27
app/src/main/cpp/randomx/LICENSE
Normal file
|
|
@ -0,0 +1,27 @@
|
|||
Copyright (c) 2018-2019, tevador <tevador@gmail.com>
|
||||
|
||||
Copyright (c) 2014-2019, The Monero Project
|
||||
|
||||
All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted provided that the following conditions are met:
|
||||
* Redistributions of source code must retain the above copyright
|
||||
notice, this list of conditions and the following disclaimer.
|
||||
* Redistributions in binary form must reproduce the above copyright
|
||||
notice, this list of conditions and the following disclaimer in the
|
||||
documentation and/or other materials provided with the distribution.
|
||||
* Neither the name of the copyright holder nor the
|
||||
names of its contributors may be used to endorse or promote products
|
||||
derived from this software without specific prior written permission.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
|
||||
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
|
||||
FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
||||
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
|
||||
CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
|
||||
OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
159
app/src/main/cpp/randomx/README.md
Normal file
159
app/src/main/cpp/randomx/README.md
Normal file
|
|
@ -0,0 +1,159 @@
|
|||
# RandomX
|
||||
RandomX is a proof-of-work (PoW) algorithm that is optimized for general-purpose CPUs. RandomX uses random code execution (hence the name) together with several memory-hard techniques to minimize the efficiency advantage of specialized hardware.
|
||||
|
||||
## Overview
|
||||
|
||||
RandomX utilizes a virtual machine that executes programs in a special instruction set that consists of integer math, floating point math and branches. These programs can be translated into the CPU's native machine code on the fly (example: [program.asm](doc/program.asm)). At the end, the outputs of the executed programs are consolidated into a 256-bit result using a cryptographic hashing function ([Blake2b](https://blake2.net/)).
|
||||
|
||||
RandomX can operate in two main modes with different memory requirements:
|
||||
|
||||
* **Fast mode** - requires 2080 MiB of shared memory.
|
||||
* **Light mode** - requires only 256 MiB of shared memory, but runs significantly slower
|
||||
|
||||
Both modes are interchangeable as they give the same results. The fast mode is suitable for "mining", while the light mode is expected to be used only for proof verification.
|
||||
|
||||
## Documentation
|
||||
|
||||
Full specification is available in [specs.md](doc/specs.md).
|
||||
|
||||
Design description and analysis is available in [design.md](doc/design.md).
|
||||
|
||||
## Audits
|
||||
|
||||
Between May and August 2019, RandomX was audited by 4 independent security research teams:
|
||||
|
||||
* [Trail of Bits](https://www.trailofbits.com/) (28 000 USD)
|
||||
* [X41 D-SEC](https://www.x41-dsec.de/) (42 000 EUR)
|
||||
* [Kudelski Security](https://www.kudelskisecurity.com/) (18 250 CHF)
|
||||
* [QuarksLab](https://quarkslab.com/en/) (52 800 USD)
|
||||
|
||||
The first audit was generously funded by [Arweave](https://www.arweave.org/), one of the early adopters of RandomX. The remaining three audits were funded by donations from the [Monero community](https://ccs.getmonero.org/proposals/RandomX-audit.html). All four audits were coordinated by [OSTIF](https://ostif.org/).
|
||||
|
||||
Final reports from all four audits are available in the [audits](audits/) directory. None of the audits found any critical vulnerabilities, but several changes in the algorithm and the code were made as a direct result of the audits. More details can be found in the [final report by OSTIF](https://ostif.org/four-audits-of-randomx-for-monero-and-arweave-have-been-completed-results/).
|
||||
|
||||
## Build
|
||||
|
||||
RandomX is written in C++11 and builds a static library with a C API provided by header file [randomx.h](src/randomx.h). Minimal API usage example is provided in [api-example1.c](src/tests/api-example1.c). The reference code includes a `randomx-benchmark` and `randomx-tests` executables for testing.
|
||||
|
||||
### Linux
|
||||
|
||||
Build dependencies: `cmake` (minimum 3.5) and `gcc` (minimum version 4.8, but version 7+ is recommended).
|
||||
|
||||
To build optimized binaries for your machine, run:
|
||||
```
|
||||
git clone https://github.com/tevador/RandomX.git
|
||||
cd RandomX
|
||||
mkdir build && cd build
|
||||
cmake -DARCH=native ..
|
||||
make
|
||||
```
|
||||
|
||||
To build portable binaries, omit the `ARCH` option when executing cmake.
|
||||
|
||||
### Windows
|
||||
|
||||
On Windows, it is possible to build using MinGW (same procedure as on Linux) or using Visual Studio (solution file is provided).
|
||||
|
||||
### Precompiled binaries
|
||||
|
||||
Precompiled `randomx-benchmark` binaries are available on the [Releases page](https://github.com/tevador/RandomX/releases).
|
||||
|
||||
## Proof of work
|
||||
|
||||
RandomX was primarily designed as a PoW algorithm for [Monero](https://www.getmonero.org/). The recommended usage is following:
|
||||
|
||||
* The key `K` is selected to be the hash of a block in the blockchain - this block is called the 'key block'. For optimal mining and verification performance, the key should change every 2048 blocks (~2.8 days) and there should be a delay of 64 blocks (~2 hours) between the key block and the change of the key `K`. This can be achieved by changing the key when `blockHeight % 2048 == 64` and selecting key block such that `keyBlockHeight % 2048 == 0`.
|
||||
* The input `H` is the standard hashing blob with a selected nonce value.
|
||||
|
||||
RandomX was successfully activated on the Monero network on the 30th November 2019.
|
||||
|
||||
If you wish to use RandomX as a PoW algorithm for your cryptocurrency, please follow the [configuration guidelines](doc/configuration.md).
|
||||
|
||||
**Note**: To achieve ASIC resistance, the key `K` must change and must not be miner-selectable. We recommend to use blockchain data as the key in a similar way to the Monero example above. If blockchain data cannot be used for some reason, use a predefined sequence of keys.
|
||||
|
||||
### CPU performance
|
||||
The table below lists the performance of selected CPUs using the optimal number of threads (T) and large pages (if possible), in hashes per second (H/s). "CNv4" refers to the CryptoNight variant 4 (CN/R) hashrate measured using [XMRig](https://github.com/xmrig/xmrig) v2.14.1. "Fast mode" and "Light mode" are the two modes of RandomX.
|
||||
|
||||
|CPU|RAM|OS|AES|CNv4|Fast mode|Light mode|
|
||||
|---|---|--|---|-----|------|--------------|
|
||||
Intel Core i9-9900K|32G DDR4-3200|Windows 10|hw|660 (8T)|5770 (8T)|1160 (16T)|
|
||||
AMD Ryzen 7 1700|16G DDR4-2666|Ubuntu 16.04|hw|520 (8T)|4100 (8T)|620 (16T)|
|
||||
Intel Core i7-8550U|16G DDR4-2400|Windows 10|hw|200 (4T)|1700 (4T)|350 (8T)|
|
||||
Intel Core i3-3220|4G DDR3-1333|Ubuntu 16.04|soft|42 (4T)|510 (4T)|150 (4T)|
|
||||
Raspberry Pi 3|1G LPDDR2|Ubuntu 16.04|soft|3.5 (4T)|-|20 (4T)|
|
||||
|
||||
Note that RandomX currently includes a JIT compiler for x86-64, ARM64 and RISCV64. Other architectures have to use the portable interpreter, which is much slower.
|
||||
|
||||
### GPU performance
|
||||
|
||||
SChernykh is developing GPU mining code for RandomX. Benchmarks are included in the following repositories:
|
||||
|
||||
* [CUDA miner](https://github.com/SChernykh/RandomX_CUDA) - NVIDIA GPUs.
|
||||
* [OpenCL miner](https://github.com/SChernykh/RandomX_OpenCL) - only for AMD Vega and AMD Polaris GPUs (uses GCN machine code).
|
||||
|
||||
The code from the above repositories is included in the open source miner [XMRig](https://github.com/xmrig/xmrig).
|
||||
|
||||
Note that GPUs are at a disadvantage when running RandomX since the algorithm was designed to be efficient on CPUs.
|
||||
|
||||
# FAQ
|
||||
|
||||
### Which CPU is best for mining RandomX?
|
||||
|
||||
Most Intel and AMD CPUs made since 2011 should be fairly efficient at RandomX. More specifically, efficient mining requires:
|
||||
|
||||
* 64-bit architecture
|
||||
* IEEE 754 compliant floating point unit
|
||||
* Hardware AES support ([AES-NI](https://en.wikipedia.org/wiki/AES_instruction_set) extension for x86, Cryptography extensions for ARMv8)
|
||||
* 16 KiB of L1 cache, 256 KiB of L2 cache and 2 MiB of L3 cache per mining thread
|
||||
* Support for large memory pages
|
||||
* At least 2.5 GiB of free RAM per NUMA node
|
||||
* Multiple memory channels may be required:
|
||||
* DDR3 memory is limited to about 1500-2000 H/s per channel (depending on frequency and timings)
|
||||
* DDR4 memory is limited to about 4000-6000 H/s per channel (depending on frequency and timings)
|
||||
|
||||
### Does RandomX facilitate botnets/malware mining or web mining?
|
||||
|
||||
Due to the way the algorithm works, mining malware is much easier to detect. [RandomX Sniffer](https://github.com/tevador/randomx-sniffer) is a proof of concept tool that can detect illicit mining activity on Windows.
|
||||
|
||||
Efficient mining requires more than 2 GiB of memory, which also disqualifies many low-end machines such as IoT devices, which are often parts of large botnets.
|
||||
|
||||
Web mining is infeasible due to the large memory requirement and the lack of directed rounding support for floating point operations in both Javascript and WebAssembly.
|
||||
|
||||
### Since RandomX uses floating point math, does it give reproducible results on different platforms?
|
||||
|
||||
RandomX uses only operations that are guaranteed to give correctly rounded results by the [IEEE 754](https://en.wikipedia.org/wiki/IEEE_754) standard: addition, subtraction, multiplication, division and square root. Special care is taken to avoid corner cases such as NaN values or denormals.
|
||||
|
||||
The reference implementation has been validated on the following platforms:
|
||||
* x86 (32-bit, little-endian)
|
||||
* x86-64 (64-bit, little-endian)
|
||||
* ARMv7+VFPv3 (32-bit, little-endian)
|
||||
* ARMv8 (64-bit, little-endian)
|
||||
* PPC64 (64-bit, big-endian)
|
||||
* RISCV64 (64-bit, little-endian)
|
||||
|
||||
### Can FPGAs mine RandomX?
|
||||
|
||||
RandomX generates multiple unique programs for every hash, so FPGAs cannot dynamically reconfigure their circuitry because typical FPGA takes tens of seconds to load a bitstream. It is also not possible to generate bitstreams for RandomX programs in advance due to the sheer number of combinations (there are 2<sup>512</sup> unique programs).
|
||||
|
||||
Sufficiently large FPGAs can mine RandomX in a [soft microprocessor](https://en.wikipedia.org/wiki/Soft_microprocessor) configuration by emulating a CPU. Under these circumstances, an FPGA will be much less efficient than a CPU or a specialized chip (ASIC).
|
||||
|
||||
## Acknowledgements
|
||||
* [tevador](https://github.com/tevador) - author
|
||||
* [SChernykh](https://github.com/SChernykh) - contributed significantly to the design of RandomX
|
||||
* [hyc](https://github.com/hyc) - original idea of using random code execution for PoW
|
||||
* [Other contributors](https://github.com/tevador/RandomX/graphs/contributors)
|
||||
|
||||
RandomX uses some source code from the following 3rd party repositories:
|
||||
* Argon2d, Blake2b hashing functions: https://github.com/P-H-C/phc-winner-argon2
|
||||
|
||||
The author of RandomX declares no competing financial interest.
|
||||
|
||||
## Donations
|
||||
|
||||
If you'd like to use RandomX, please consider donating to help cover the development cost of the algorithm.
|
||||
|
||||
Author's XMR address:
|
||||
```
|
||||
845xHUh5GvfHwc2R8DVJCE7BT2sd4YEcmjG8GNSdmeNsP5DTEjXd1CNgxTcjHjiFuthRHAoVEJjM7GyKzQKLJtbd56xbh7V
|
||||
```
|
||||
Total donations received: ~3.86 XMR (as of 30th August 2019). Thanks to all contributors.
|
||||
287
app/src/main/cpp/randomx/doc/configuration.md
Normal file
287
app/src/main/cpp/randomx/doc/configuration.md
Normal file
|
|
@ -0,0 +1,287 @@
|
|||
# RandomX configuration
|
||||
|
||||
RandomX has 45 customizable parameters (see table below). We recommend each project using RandomX to select a unique configuration to prevent network attacks from hashpower rental services.
|
||||
|
||||
These parameters can be modified in source file [configuration.h](../src/configuration.h).
|
||||
|
||||
|parameter|description|default value|
|
||||
|---------|-----|-------|
|
||||
|`RANDOMX_ARGON_MEMORY`|The number of 1 KiB Argon2 blocks in the Cache| `262144`|
|
||||
|`RANDOMX_ARGON_ITERATIONS`|The number of Argon2d iterations for Cache initialization|`3`|
|
||||
|`RANDOMX_ARGON_LANES`|The number of parallel lanes for Cache initialization|`1`|
|
||||
|`RANDOMX_ARGON_SALT`|Argon2 salt|`"RandomX\x03"`|
|
||||
|`RANDOMX_CACHE_ACCESSES`|The number of random Cache accesses per Dataset item|`8`|
|
||||
|`RANDOMX_SUPERSCALAR_LATENCY`|Target latency for SuperscalarHash (in cycles of the reference CPU)|`170`|
|
||||
|`RANDOMX_DATASET_BASE_SIZE`|Dataset base size in bytes|`2147483648`|
|
||||
|`RANDOMX_DATASET_EXTRA_SIZE`|Dataset extra size in bytes|`33554368`|
|
||||
|`RANDOMX_PROGRAM_SIZE`|The number of instructions in a RandomX program|`256`|
|
||||
|`RANDOMX_PROGRAM_ITERATIONS`|The number of iterations per program|`2048`|
|
||||
|`RANDOMX_PROGRAM_COUNT`|The number of programs per hash|`8`|
|
||||
|`RANDOMX_JUMP_BITS`|Jump condition mask size in bits|`8`|
|
||||
|`RANDOMX_JUMP_OFFSET`|Jump condition mask offset in bits|`8`|
|
||||
|`RANDOMX_SCRATCHPAD_L3`|Scratchpad size in bytes|`2097152`|
|
||||
|`RANDOMX_SCRATCHPAD_L2`|Scratchpad L2 size in bytes|`262144`|
|
||||
|`RANDOMX_SCRATCHPAD_L1`|Scratchpad L1 size in bytes|`16384`|
|
||||
|`RANDOMX_FREQ_*` (29x)|Instruction frequencies|multiple values|
|
||||
|
||||
Not all of the parameters can be changed safely and most parameters have some contraints on what values can be selected (checked at compile-time).
|
||||
|
||||
**Disclaimer: The compile-time checks only prevent obviously broken configurations. Passing the checks does not imply that the configuration is safe and will not cause crashes or other issues. We recommend that each non-standard configuration is thoroughly tested before being deployed.**
|
||||
|
||||
### RANDOMX_ARGON_MEMORY
|
||||
|
||||
This parameter determines the amount of memory needed in the light mode. Memory is specified in KiB (1 KiB = 1024 bytes).
|
||||
|
||||
#### Permitted values
|
||||
Integer powers of 2 in the range 8 - 2097152.
|
||||
|
||||
#### Notes
|
||||
Lower sizes will reduce the memory-hardness of the algorithm.
|
||||
|
||||
### RANDOMX_ARGON_ITERATIONS
|
||||
|
||||
Determines the number of passes of Argon2 that are used to generate the Cache.
|
||||
|
||||
#### Permitted values
|
||||
Any positive 32-bit integer.
|
||||
|
||||
#### Notes
|
||||
The time needed to initialize the Cache is proportional to the value of this constant.
|
||||
|
||||
### RANDOMX_ARGON_LANES
|
||||
|
||||
The number of parallel lanes for Cache initialization.
|
||||
|
||||
#### Permitted values
|
||||
Integers in the range 1 - 16777215.
|
||||
|
||||
#### Notes
|
||||
This parameter determines how many threads can be used for Cache initialization.
|
||||
|
||||
### RANDOMX_ARGON_SALT
|
||||
|
||||
Salt value for Cache initialization.
|
||||
|
||||
#### Permitted values
|
||||
A string of at least 8 characters.
|
||||
|
||||
#### Note
|
||||
Every implementation should choose a unique salt value.
|
||||
|
||||
### RANDOMX_CACHE_ACCESSES
|
||||
|
||||
The number of random Cache access per Dataset item.
|
||||
|
||||
#### Permitted values
|
||||
Any integer greater than 1.
|
||||
|
||||
#### Notes
|
||||
This value directly determines the performance ratio between the 'fast' and 'light' modes.
|
||||
|
||||
### RANDOMX_SUPERSCALAR_LATENCY
|
||||
Target latency for SuperscalarHash, in cycles of the reference CPU.
|
||||
|
||||
#### Permitted values
|
||||
Integers in the range 1 - 10000.
|
||||
|
||||
#### Notes
|
||||
The default value was tuned so that a high-performance superscalar CPU running at 2-4 GHz will execute SuperscalarHash in similar time it takes to load data from RAM (40-80 ns). Using a lower value will make Dataset generation (and light mode) more memory bound, while increasing this value will make Dataset generation (and light mode) more compute bound.
|
||||
|
||||
### RANDOMX_DATASET_BASE_SIZE
|
||||
|
||||
Dataset base size in bytes.
|
||||
|
||||
#### Permitted values
|
||||
Integer powers of 2 in the range 64 - 4294967296 (inclusive).
|
||||
|
||||
#### Note
|
||||
This constant affects the memory requirements in fast mode. Some values are unsafe depending on other parameters. See [Unsafe configurations](#unsafe-configurations).
|
||||
|
||||
### RANDOMX_DATASET_EXTRA_SIZE
|
||||
|
||||
Dataset extra size in bytes.
|
||||
|
||||
#### Permitted values
|
||||
Non-negative integer divisible by 64.
|
||||
|
||||
#### Note
|
||||
This constant affects the memory requirements in fast mode. Some values are unsafe depending on other parameters. See [Unsafe configurations](#unsafe-configurations).
|
||||
|
||||
### RANDOMX_PROGRAM_SIZE
|
||||
|
||||
The number of instructions in a RandomX program.
|
||||
|
||||
#### Permitted values
|
||||
Positive integers divisible by 8 in the range 8 - 32768 (inclusive).
|
||||
|
||||
#### Notes
|
||||
Smaller values will make RandomX more DRAM-latency bound, while higher values will make RandomX more compute-bound. Some values are unsafe. See [Unsafe configurations](#unsafe-configurations).
|
||||
|
||||
### RANDOMX_PROGRAM_ITERATIONS
|
||||
|
||||
The number of iterations per program.
|
||||
|
||||
#### Permitted values
|
||||
Any positive integer.
|
||||
|
||||
#### Notes
|
||||
Time per hash increases linearly with this constant. Smaller values will increase the overhead of program compilation, while larger values may allow more time for optimizations. Some values are unsafe. See [Unsafe configurations](#unsafe-configurations).
|
||||
|
||||
### RANDOMX_PROGRAM_COUNT
|
||||
|
||||
The number of programs per hash.
|
||||
|
||||
#### Permitted values
|
||||
Any positive integer.
|
||||
|
||||
#### Notes
|
||||
Time per hash increases linearly with this constant. Some values are unsafe. See [Unsafe configurations](#unsafe-configurations).
|
||||
|
||||
### RANDOMX_JUMP_BITS
|
||||
Jump condition mask size in bits.
|
||||
|
||||
#### Permitted values
|
||||
Positive integers. The sum of `RANDOMX_JUMP_BITS` and `RANDOMX_JUMP_OFFSET` must not exceed 16.
|
||||
|
||||
#### Notes
|
||||
This determines the jump probability of the CBRANCH instruction. The default value of 8 results in jump probability of <code>1/2<sup>8</sup> = 1/256</code>. Increasing this constant will decrease the rate of jumps (and vice versa).
|
||||
|
||||
### RANDOMX_JUMP_OFFSET
|
||||
Jump condition mask offset in bits.
|
||||
|
||||
#### Permitted values
|
||||
Non-negative integers. The sum of `RANDOMX_JUMP_BITS` and `RANDOMX_JUMP_OFFSET` must not exceed 16.
|
||||
|
||||
#### Notes
|
||||
Since the low-order bits of RandomX registers are slightly biased, this offset moves the condition mask to higher bits, which are less biased. Using values smaller than the default may result in a slightly lower jump probability than the theoretical value calculated from `RANDOMX_JUMP_BITS`.
|
||||
|
||||
### RANDOMX_SCRATCHPAD_L3
|
||||
RandomX Scratchpad size in bytes.
|
||||
|
||||
#### Permitted values
|
||||
Any integer power of 2. Must be larger than or equal to `RANDOMX_SCRATCHPAD_L2`.
|
||||
|
||||
#### Notes
|
||||
|
||||
The default value of 2 MiB was selected to match the typical cache/core ratio of desktop processors. Using a lower value will make RandomX more core-bound, while using larger values will make the algorithm more latency-bound. Some values are unsafe depending on other parameters. See [Unsafe configurations](#unsafe-configurations).
|
||||
|
||||
### RANDOMX_SCRATCHPAD_L2
|
||||
|
||||
Scratchpad L2 size in bytes.
|
||||
|
||||
#### Permitted values
|
||||
Any integer power of 2. Must be larger than or equal to `RANDOMX_SCRATCHPAD_L1`.
|
||||
|
||||
#### Notes
|
||||
The default value of 256 KiB was selected to match the typical per-core L2 cache size of desktop processors. Using a lower value will make RandomX more core-bound, while using larger values will make the algorithm more latency-bound.
|
||||
|
||||
### RANDOMX_SCRATCHPAD_L1
|
||||
|
||||
Scratchpad L1 size in bytes.
|
||||
|
||||
#### Permitted values
|
||||
Any integer power of 2. The minimum is 64 bytes.
|
||||
|
||||
#### Notes
|
||||
The default value of 16 KiB was selected to be about half of the per-core L1 cache size of desktop processors. Using a lower value will make RandomX more core-bound, while using larger values will make the algorithm more latency-bound.
|
||||
|
||||
### RANDOMX_FREQ_*
|
||||
|
||||
Instruction frequencies (per 256 instructions).
|
||||
|
||||
#### Permitted values
|
||||
There is a total of 29 different instructions. The sum of frequencies must be equal to 256.
|
||||
|
||||
#### Notes
|
||||
|
||||
Making changes to the default values is not recommended. The only exceptions are the instruction pairs IROR_R/IROL_R, FADD_R/FSUB_R and FADD_M/FSUB_M, which are functionally equivalent. Example of a safe custom configuration:
|
||||
|
||||
||default|custom|
|
||||
|-|------|------|-|
|
||||
|`RANDOMX_FREQ_IROR_R`|8|5|
|
||||
|`RANDOMX_FREQ_IROL_R`|2|5|
|
||||
|
||||
||default|custom|
|
||||
|-|------|------|
|
||||
|`RANDOMX_FREQ_FADD_R`|16|17|
|
||||
|`RANDOMX_FREQ_FSUB_R`|16|15|
|
||||
|
||||
||default|custom|
|
||||
|-|------|------|
|
||||
|`RANDOMX_FREQ_FADD_M`|5|4|
|
||||
|`RANDOMX_FREQ_FSUB_M`|5|6|
|
||||
|
||||
## Unsafe configurations
|
||||
|
||||
There are some configurations that are considered 'unsafe' because they affect the security of the algorithm against attacks. If the conditions listed below are not satisfied, the configuration is unsafe and a compilation error is emitted when building the RandomX library.
|
||||
|
||||
These checks can be disabled by definining `RANDOMX_UNSAFE` when building RandomX, e.g. by using `-DRANDOMX_UNSAFE` command line switch in GCC or MSVC. It is not recommended to disable these checks except for testing purposes.
|
||||
|
||||
|
||||
### 1. Memory-time tradeoffs
|
||||
|
||||
#### Condition
|
||||
````
|
||||
RANDOMX_CACHE_ACCESSES * RANDOMX_ARGON_MEMORY * 1024 + 33554432 >= RANDOMX_DATASET_BASE_SIZE + RANDOMX_DATASET_EXTRA_SIZE
|
||||
````
|
||||
|
||||
Configurations not satisfying this condition are vulnerable to memory-time tradeoffs, which enables efficient mining in light mode.
|
||||
|
||||
#### Solutions
|
||||
|
||||
* Increase `RANDOMX_CACHE_ACCESSES` or `RANDOMX_ARGON_MEMORY`.
|
||||
* Decrease `RANDOMX_DATASET_BASE_SIZE` or `RANDOMX_DATASET_EXTRA_SIZE`.
|
||||
|
||||
### 2. Insufficient Scratchpad writes
|
||||
|
||||
#### Condition
|
||||
````
|
||||
(128 + RANDOMX_PROGRAM_SIZE * RANDOMX_FREQ_ISTORE / 256) * (RANDOMX_PROGRAM_COUNT * RANDOMX_PROGRAM_ITERATIONS) >= RANDOMX_SCRATCHPAD_L3
|
||||
````
|
||||
|
||||
Configurations not satisfying this condition are vulnerable to Scratchpad size optimizations due to low amount of writes.
|
||||
|
||||
#### Solutions
|
||||
|
||||
* Increase `RANDOMX_PROGRAM_SIZE`, `RANDOMX_FREQ_ISTORE`, `RANDOMX_PROGRAM_COUNT` or `RANDOMX_PROGRAM_ITERATIONS`.
|
||||
* Decrease `RANDOMX_SCRATCHPAD_L3`.
|
||||
|
||||
### 3. Program filtering strategies
|
||||
|
||||
#### Condition
|
||||
```
|
||||
RANDOMX_PROGRAM_COUNT > 1
|
||||
```
|
||||
|
||||
Configurations not satisfying this condition are vulnerable to program filtering strategies.
|
||||
|
||||
#### Solution
|
||||
|
||||
* Increase `RANDOMX_PROGRAM_COUNT` to at least 2.
|
||||
|
||||
### 4. Low program entropy
|
||||
|
||||
#### Condition
|
||||
```
|
||||
RANDOMX_PROGRAM_SIZE >= 64
|
||||
```
|
||||
|
||||
Configurations not satisfying this condition do not have a sufficient number of instruction combinations.
|
||||
|
||||
#### Solution
|
||||
|
||||
* Increase `RANDOMX_PROGRAM_SIZE` to at least 64.
|
||||
|
||||
### 5. High compilation overhead
|
||||
|
||||
#### Condition
|
||||
```
|
||||
RANDOMX_PROGRAM_ITERATIONS >= 400
|
||||
```
|
||||
|
||||
Configurations not satisfying this condition have a program compilation overhead exceeding 10%.
|
||||
|
||||
#### Solution
|
||||
|
||||
* Increase `RANDOMX_PROGRAM_ITERATIONS` to at least 400.
|
||||
|
||||
650
app/src/main/cpp/randomx/doc/design.md
Normal file
650
app/src/main/cpp/randomx/doc/design.md
Normal file
|
|
@ -0,0 +1,650 @@
|
|||
# RandomX design
|
||||
To minimize the performance advantage of specialized hardware, a proof of work (PoW) algorithm must achieve *device binding* by targeting specific features of existing general-purpose hardware. This is a complex task because we have to target a large class of devices with different architectures from different manufacturers.
|
||||
|
||||
There are two distinct classes of general processing devices: central processing units (CPUs) and graphics processing units (GPUs). RandomX targets CPUs for the following reasons:
|
||||
|
||||
* CPUs, being less specialized devices, are more prevalent and widely accessible. A CPU-bound algorithm is more egalitarian and allows more participants to join the network. This is one of the goals stated in the original CryptoNote whitepaper [[1](https://cryptonote.org/whitepaper.pdf)].
|
||||
* A large common subset of native hardware instructions exists among different CPU architectures. The same cannot be said about GPUs. For example, there is no common integer multiplication instruction for NVIDIA and AMD GPUs [[2](https://github.com/ifdefelse/ProgPOW/issues/16)].
|
||||
* All major CPU instruction sets are well documented with multiple open source compilers available. In comparison, GPU instruction sets are usually proprietary and may require vendor specific closed-source drivers for maximum performance.
|
||||
|
||||
## 1. Design considerations
|
||||
|
||||
The most basic idea of a CPU-bound proof of work is that the "work" must be dynamic. This takes advantage of the fact that CPUs accept two kinds of inputs: *data* (the main input) and *code* (which specifies what to perform with the data).
|
||||
|
||||
Conversely, typical cryptographic hashing functions [[3](https://en.wikipedia.org/wiki/Cryptographic_hash_function)] do not represent suitable work for the CPU because their only input is *data*, while the sequence of operations is fixed and can be performed more efficiently by a specialized integrated circuit.
|
||||
|
||||
### 1.1 Dynamic proof of work
|
||||
|
||||
A dynamic proof of work algorithm can generally consist of the following 4 steps:
|
||||
|
||||
1) Generate a random program.
|
||||
2) Translate it into the native machine code of the CPU.
|
||||
3) Execute the program.
|
||||
4) Transform the output of the program into a cryptographically secure value.
|
||||
|
||||
The actual 'useful' CPU-bound work is performed in step 3, so the algorithm must be tuned to minimize the overhead of the remaining steps.
|
||||
|
||||
#### 1.1.1 Generating a random program
|
||||
|
||||
Early attempts at a dynamic proof of work design were based on generating a program in a high-level language, such as C or Javascript [[4](https://github.com/hyc/randprog), [5](https://github.com/tevador/RandomJS)]. However, this is very inefficient for two main reasons:
|
||||
|
||||
* High level languages have a complex syntax, so generating a valid program is relatively slow since it requires the creation of an abstract syntax tree (ASL).
|
||||
* Once the source code of the program is generated, the compiler will generally parse the textual representation back into the ASL, which makes the whole process of generating source code redundant.
|
||||
|
||||
The fastest way to generate a random program is to use a *logic-less* generator - simply filling a buffer with random data. This of course requires designing a syntaxless programming language (or instruction set) in which all random bit strings represent valid programs.
|
||||
|
||||
#### 1.1.2 Translating the program into machine code
|
||||
|
||||
This step is inevitable because we don't want to limit the algorithm to a specific CPU architecture. In order to generate machine code as fast as possible, we need our instruction set to be as close to native hardware as possible, while still generic enough to support different architectures. There is not enough time for expensive optimizations during code compilation.
|
||||
|
||||
#### 1.1.3 Executing the program
|
||||
|
||||
The actual program execution should utilize as many CPU components as possible. Some of the features that should be utilized in the program are:
|
||||
|
||||
* multi-level caches (L1, L2, L3)
|
||||
* μop cache [[6](https://en.wikipedia.org/wiki/CPU_cache#Micro-operation_(%CE%BCop_or_uop)_cache)]
|
||||
* arithmetic logic unit (ALU)
|
||||
* floating point unit (FPU)
|
||||
* memory controller
|
||||
* instruction level parallelism [[7](https://en.wikipedia.org/wiki/Instruction-level_parallelism)]
|
||||
* superscalar execution [[8](https://en.wikipedia.org/wiki/Superscalar_processor)]
|
||||
* out-of-order execution [[9](https://en.wikipedia.org/wiki/Out-of-order_execution)]
|
||||
* speculative execution [[10](https://en.wikipedia.org/wiki/Speculative_execution)]
|
||||
* register renaming [[11](https://en.wikipedia.org/wiki/Register_renaming)]
|
||||
|
||||
Chapter 2 describes how the RandomX VM takes advantages of these features.
|
||||
|
||||
#### 1.1.4 Calculating the final result
|
||||
|
||||
Blake2b [[12](https://blake2.net/)] is a cryptographically secure hashing function that was specifically designed to be fast in software, especially on modern 64-bit processors, where it's around three times faster than SHA-3 and can run at a speed of around 3 clock cycles per byte of input. This function is an ideal candidate to be used in a CPU-friendly proof of work.
|
||||
|
||||
For processing larger amounts of data in a cryptographically secure way, the Advanced Encryption Standard (AES) [[13](https://en.wikipedia.org/wiki/Advanced_Encryption_Standard)] can provide the fastest processing speed because many modern CPUs support hardware acceleration of these operations. See chapter 3 for more details about the use of AES in RandomX.
|
||||
|
||||
### 1.2 The "Easy program problem"
|
||||
|
||||
When a random program is generated, one may choose to execute it only when it's favorable. This strategy is viable for two main reasons:
|
||||
|
||||
1. The runtime of randomly generated programs typically follows a log-normal distribution [[14](https://en.wikipedia.org/wiki/Log-normal_distribution)] (also see Appendix C). A generated program may be quickly analyzed and if it's likely to have above-average runtime, program execution may be skipped and a new program may be generated instead. This can significantly boost performance especially in case the runtime distribution has a heavy tail (many long-running outliers) and if program generation is cheap.
|
||||
2. An implementation may choose to optimize for a subset of the features required for program execution. For example, the support for some operations (such as division) may be dropped or some instruction sequences may be implemented more efficiently. Generated programs would then be analyzed and be executed only if they match the specific requirements of the optimized implementation.
|
||||
|
||||
These strategies of searching for programs of particular properties deviate from the objectives of this proof of work, so they must be eliminated. This can be achieved by requiring a sequence of *N* random programs to be executed such that each program is generated from the output of the previous one. The output of the final program is then used as the result.
|
||||
|
||||
```
|
||||
+---------------+ +---------------+ +---------------+ +---------------+
|
||||
| | | | | | | |
|
||||
input --> | program 1 | --> | program 2 | --> ... --> | program (N-1) | --> | program N | --> result
|
||||
| | | | | | | |
|
||||
+---------------+ +---------------+ +---------------+ +---------------+
|
||||
```
|
||||
|
||||
The principle is that after the first program is executed, a miner has to either commit to finishing the whole chain (which may include unfavorable programs) or start over and waste the effort expended on the unfinished chain. Examples of how this affects the hashrate of different mining strategies are given in Appendix A.
|
||||
|
||||
Additionally, this chained program execution has the benefit of equalizing the runtime for the whole chain since the relative deviation of a sum of identically distributed runtimes is decreased.
|
||||
|
||||
### 1.3 Verification time
|
||||
|
||||
Since the purpose of the proof of work is to be used in a trustless peer-to-peer network, network participants must be able to quickly verify if a proof is valid or not. This puts an upper bound on the complexity of the proof of work algorithm. In particular, we set a goal for RandomX to be at least as fast to verify as the CryptoNight hash function [[15](https://cryptonote.org/cns/cns008.txt)], which it aims to replace.
|
||||
|
||||
### 1.4 Memory-hardness
|
||||
|
||||
Besides pure computational resources, such as ALUs and FPUs, CPUs usually have access to a large amount of memory in the form of DRAM [[16](https://en.wikipedia.org/wiki/Dynamic_random-access_memory)]. The performance of the memory subsystem is typically tuned to match the compute capabilities, for example [[17](https://en.wikipedia.org/wiki/Multi-channel_memory_architecture)]:
|
||||
|
||||
* single channel memory for embedded and low power CPUs
|
||||
* dual channel memory for desktop CPUs
|
||||
* triple or quad channel memory for workstation CPUs
|
||||
* six or eight channel memory for high-end server CPUs
|
||||
|
||||
In order to utilize the external memory as well as the on-chip memory controllers, the proof of work algorithm should access a large memory buffer (called the "Dataset"). The Dataset must be:
|
||||
|
||||
1. larger than what can be stored on-chip (to require external memory)
|
||||
2. dynamic (to require writable memory)
|
||||
|
||||
The maximum amount of SRAM that can be put on a single chip is more than 512 MiB for a 16 nm process and more than 2 GiB for a 7 nm process [[18](https://www.grin-forum.org/t/obelisk-grn1-chip-details/4571)]. Ideally, the size of the Dataset should be at least 4 GiB. However, due to constraints on the verification time (see below), the size used by RandomX was selected to be 2080 MiB. While a single chip can theoretically be made with this amount of SRAM using current technology (7 nm in 2019), the feasibility of such solution is questionable, at least in the near future.
|
||||
|
||||
#### 1.4.1 Light-client verification
|
||||
|
||||
While it's reasonable to require >2 GiB for dedicated mining systems that solve the proof of work, an option must be provided for light clients to verify the proof using a much lower amount of memory.
|
||||
|
||||
The ratio of memory required for the 'fast' and 'light' modes must be chosen carefully not to make the light mode viable for mining. In particular, the area-time (AT) product of the light mode should not be smaller than the AT product of the fast mode. Reduction of the AT product is a common way of measuring tradeoff attacks [[19](https://eprint.iacr.org/2015/227.pdf)].
|
||||
|
||||
Given the constraints described in the previous chapters, the maximum possible performance ratio between the fast and the light verification modes was empirically determined to be 8. This is because:
|
||||
|
||||
1. Further increase of the light verification time would violate the constraints set out in chapter 1.3.
|
||||
2. Further decrease of the fast mode runtime would violate the constraints set out in chapter 1.1, in particular the overhead time of program generation and result calculation would become too high.
|
||||
|
||||
Additionally, 256 MiB was selected as the maximum amount of memory that can be required in the light-client mode. This amount is acceptable even for small single-board computers such as the Raspberry Pi.
|
||||
|
||||
To keep a constant memory-time product, the maximum fast-mode memory requirement is:
|
||||
```
|
||||
8 * 256 MiB = 2048 MiB
|
||||
```
|
||||
This can be further increased since the light mode requires additional chip area for the SuperscalarHash function (see chapter 3.4 and chapter 6 of the Specification). Assuming a conservative estimate of 0.2 mm<sup>2</sup> per SuperscalarHash core and DRAM density of 0.149 Gb/mm<sup>2</sup> [[20](http://en.thelec.kr/news/articleView.html?idxno=20)], the additional memory is:
|
||||
|
||||
```
|
||||
8 * 0.2 * 0.149 * 1024 / 8 = 30.5 MiB
|
||||
```
|
||||
or 32 MiB when rounded to the nearest power of 2. The total memory requirement of the fast mode can be 2080 MiB with a roughly constant AT product.
|
||||
|
||||
## 2. Virtual machine architecture
|
||||
|
||||
This section describes the design of the RandomX virtual machine (VM).
|
||||
|
||||
### 2.1 Instruction set
|
||||
|
||||
RandomX uses a fixed-length instruction encoding with 8 bytes per instruction. This allows a 32-bit immediate value to be included in the instruction word. The interpretation of the instruction word bits was chosen so that any 8-byte word is a valid instruction. This allows for very efficient random program generation (see chapter 1.1.1).
|
||||
|
||||
#### 2.1.1 Instruction complexity
|
||||
|
||||
The VM is a complex instruction set machine that allows both register and memory addressed operands. However, each RandomX instructions translates to only 1-7 x86 instructions (1.8 on average). It is important to keep the instruction complexity relatively low to minimize the efficiency advantage of specialized hardware with a tailored instruction set.
|
||||
|
||||
### 2.2 Program
|
||||
|
||||
The program executed by the VM has the form of a loop consisting of 256 random instructions.
|
||||
|
||||
* 256 instructions is long enough to provide a large number of possible programs and enough space for branches. The number of different programs that can be generated is limited to 2<sup>512</sup> = 1.3e+154, which is the number of possible seed values of the random generator.
|
||||
* 256 instructions is short enough so that high-performance CPUs can execute one iteration in similar time it takes to fetch data from DRAM. This is advantageous because it allows Dataset accesses to be synchronized and fully prefetchable (see chapter 2.9).
|
||||
* Since the program is a loop, it can take advantage of the μop cache [[6](https://en.wikipedia.org/wiki/CPU_cache#Micro-operation_(%CE%BCop_or_uop)_cache)] that is present in some x86 CPUs. Running a loop from the μop cache allows the CPU to power down the x86 instruction decoders, which should help to equalize the power efficiency between x86 and architectures with simple instruction decoding.
|
||||
|
||||
### 2.3 Registers
|
||||
|
||||
The VM uses 8 integer registers and 12 floating point registers. This is the maximum that can be allocated as physical registers in x86-64, which has the fewest architectural registers among common 64-bit CPU architectures. Using more registers would put x86 CPUs at a disadvantage since they would have to use memory to store VM register contents.
|
||||
|
||||
### 2.4 Integer operations
|
||||
|
||||
RandomX uses all primitive integer operations that have high output entropy: addition (IADD_RS, IADD_M), subtraction (ISUB_R, ISUB_M, INEG_R), multiplication (IMUL_R, IMUL_M, IMULH_R, IMULH_M, ISMULH_R, ISMULH_M, IMUL_RCP), exclusive or (IXOR_R, IXOR_M) and rotation (IROR_R, IROL_R).
|
||||
|
||||
#### 2.4.1 IADD_RS
|
||||
|
||||
The IADD_RS instruction utilizes the address calculation logic of CPUs and can be performed in a single hardware instruction by most CPUs (x86 `lea`, arm `add`).
|
||||
|
||||
#### 2.4.2 IMUL_RCP
|
||||
|
||||
Because integer division is not fully pipelined in CPUs and can be made faster in ASICs, the IMUL_RCP instruction requires only one division per program to calculate the reciprocal. This forces an ASIC to include a hardware divider without giving them a performance advantage during program execution.
|
||||
|
||||
#### 2.4.3 IROR_R/IROL_R
|
||||
|
||||
Rotation instructions are split between rotate right and rotate left with a 4:1 ratio. Rotate right has a higher frequency because some architecures (like ARM) don't support rotate left natively (it must be emulated using rotate right).
|
||||
|
||||
#### 2.4.4 ISWAP_R
|
||||
|
||||
This instruction can be executed efficiently by CPUs that support register renaming/move elimination.
|
||||
|
||||
### 2.5 Floating point operations
|
||||
|
||||
RandomX uses double precision floating point operations, which are supported by the majority of CPUs and require more complex hardware than single precision. All operations are performed as 128-bit vector operations, which is also supported by all major CPU architectures.
|
||||
|
||||
RandomX uses five operations that are guaranteed by the IEEE 754 standard to give correctly rounded results: addition, subtraction, multiplication, division and square root. All 4 rounding modes defined by the standard are used.
|
||||
|
||||
#### 2.5.1 Floating point register groups
|
||||
|
||||
The domains of floating point operations are separated into "additive" operations, which use register group F and "multiplicative" operations, which use register group E. This is done to prevent addition/subtraction from becoming no-op when a small number is added to a large number. Since the range of the F group registers is limited to around `±3.0e+14`, adding or subtracting a floating point number with absolute value larger than 1 always changes at least 5 fraction bits.
|
||||
|
||||
Because the limited range of group F registers would allow the use of a more efficient fixed-point representation (with 80-bit numbers), the FSCAL instruction manipulates the binary representation of the floating point format to make this optimization more difficult.
|
||||
|
||||
Group E registers are restricted to positive values, which avoids `NaN` results (such as square root of a negative number or `0 * ∞`). Division uses only memory source operand to avoid being optimized into multiplication by constant reciprocal. The exponent of group E memory operands is set to a value between -255 and 0 to avoid division and multiplication by 0 and to increase the range of numbers that can be obtained. The approximate range of possible group E register values is `1.7E-77` to `infinity`.
|
||||
|
||||
Approximate distribution of floating point register values at the end of each program loop is shown in these figures (left - group F, right - group E):
|
||||
|
||||

|
||||
|
||||
*(Note: bins are marked by the left-side value of the interval, e.g. bin marked `1e-40` contains values from `1e-40` to `1e-20`.)*
|
||||
|
||||
The small number of F register values at `1e+14` is caused by the FSCAL instruction, which significantly increases the range of the register values.
|
||||
|
||||
Group E registers cover a very large range of values. About 2% of programs produce at least one `infinity` value.
|
||||
|
||||
To maximize entropy and also to fit into one 64-byte cache line, floating point registers are combined using the XOR operation at the end of each iteration before being stored into the Scratchpad.
|
||||
|
||||
### 2.6 Branches
|
||||
|
||||
Modern CPUs invest a lot of die area and energy to handle branches. This includes:
|
||||
|
||||
* Branch predictor unit [[21](https://en.wikipedia.org/wiki/Branch_predictor)]
|
||||
* Checkpoint/rollback states that allow the CPU to recover in case of a branch misprediction.
|
||||
|
||||
To take advantage of speculative designs, the random programs should contain branches. However, if branch prediction fails, the speculatively executed instructions are thrown away, which results in a certain amount of wasted energy with each misprediction. Therefore we should aim to minimize the number of mispredictions.
|
||||
|
||||
Additionally, branches in the code are essential because they significantly reduce the amount of static optimizations that can be made. For example, consider the following x86 instruction sequence:
|
||||
```asm
|
||||
...
|
||||
branch_target_00:
|
||||
...
|
||||
xor r8, r9
|
||||
test r10, 2088960
|
||||
je branch_target_00
|
||||
xor r8, r9
|
||||
...
|
||||
```
|
||||
The XOR operations would normally cancel out, but cannot be optimized away due to the branch because the result will be different if the branch is taken. Similarly, the ISWAP_R instruction could be always statically optimized out if it wasn't for branches.
|
||||
|
||||
In general, random branches must be designed in such way that:
|
||||
|
||||
1. Infinite loops are not possible.
|
||||
1. The number of mispredicted branches is small.
|
||||
1. Branch condition depends on a runtime value to disable static branch optimizations.
|
||||
|
||||
#### 2.6.1 Branch prediction
|
||||
|
||||
Unfortunately, we haven't found a way how to utilize branch prediction in RandomX. Because RandomX is a consensus protocol, all the rules must be set out in advance, which includes the rules for branches. Fully predictable branches cannot depend on the runtime value of any VM register (since register values are pseudorandom and unpredictable), so they would have to be static and therefore easily optimizable by specialized hardware.
|
||||
|
||||
#### 2.6.2 CBRANCH instruction
|
||||
|
||||
RandomX therefore uses random branches with a jump probability of 1/256 and branch condition that depends on an integer register value. These branches will be predicted as "not taken" by the CPU. Such branches are "free" in most CPU designs unless they are taken. While this doesn't take advantage of the branch predictors, speculative designs will see a significant performance boost compared to non-speculative branch handling - see Appendix B for more information.
|
||||
|
||||
The branching conditions and jump targets are chosen in such way that infinite loops in RandomX code are impossible because the register controlling the branch will never be modified in the repeated code block. Each CBRANCH instruction can jump up to twice in a row. Handling CBRANCH using predicated execution [[22](https://en.wikipedia.org/wiki/Predication_(computer_architecture))] is impractical because the branch is not taken most of the time.
|
||||
|
||||
### 2.7 Instruction-level parallelism
|
||||
|
||||
CPUs improve their performance using several techniques that utilize instruction-level parallelism of the executed code. These techniques include:
|
||||
|
||||
* Having multiple execution units that can execute operations in parallel (*superscalar execution*).
|
||||
* Executing instruction not in program order, but in the order of operand availability (*out-of-order execution*).
|
||||
* Predicting which way branches will go to enhance the benefits of both superscalar and out-of-order execution.
|
||||
|
||||
RandomX benefits from all these optimizations. See Appendix B for a detailed analysis.
|
||||
|
||||
### 2.8 Scratchpad
|
||||
|
||||
The Scratchpad is used as read-write memory. Its size was selected to fit entirely into CPU cache.
|
||||
|
||||
#### 2.8.1 Scratchpad levels
|
||||
|
||||
The Scratchpad is split into 3 levels to mimic the typical CPU cache hierarchy [[23](https://en.wikipedia.org/wiki/CPU_cache)]. Most VM instructions access "L1" and "L2" Scratchpad because L1 and L2 CPU caches are located close to the CPU execution units and provide the best random access latency. The ratio of reads from L1 and L2 is 3:1, which matches the inverse ratio of typical latencies (see table below).
|
||||
|
||||
|CPU μ-architecture|L1 latency|L2 latency|L3 latency|source|
|
||||
|----------------|----------|----------|----------|------|
|
||||
ARM Cortex A55|2|6|-|[[24](https://www.anandtech.com/show/11441/dynamiq-and-arms-new-cpus-cortex-a75-a55/4)]
|
||||
|AMD Zen+|4|12|40|[[25](https://en.wikichip.org/wiki/amd/microarchitectures/zen%2B#Memory_Hierarchy)]|
|
||||
|Intel Skylake|4|12|42|[[26](https://en.wikichip.org/wiki/intel/microarchitectures/skylake_(client)#Memory_Hierarchy)]
|
||||
|
||||
The L3 cache is much larger and located further from the CPU core. As a result, its access latencies are much higher and can cause stalls in program execution.
|
||||
|
||||
RandomX therefore performs only 2 random accesses into "L3" Scratchpad per program iteration (steps 2 and 3 in chapter 4.6.2 of the Specification). Register values from a given iteration are written into the same locations they were loaded from, which guarantees that the required cache lines have been moved into the faster L1 or L2 caches.
|
||||
|
||||
Additionally, integer instructions that read from a fixed address also use the whole "L3" Scratchpad (Table 5.1.4 of the Specification) because repetitive accesses will ensure that the cache line will be placed in the L1 cache of the CPU. This shows that the Scratchpad level doesn't always directly correspond to the same CPU cache level.
|
||||
|
||||
#### 2.8.2 Scratchpad writes
|
||||
|
||||
There are two ways the Scratchpad is modified during VM execution:
|
||||
|
||||
1. At the end of each program iteration, all register values are written into "L3" Scratchpad (see Specification chapter 4.6.2, steps 9 and 11). This writes a total of 128 bytes per iteration in two 64-byte blocks.
|
||||
2. The ISTORE instruction does explicit stores. On average, there are 16 stores per program, out of which 2 stores are into the "L3" level. Each ISTORE instruction writes 8 bytes.
|
||||
|
||||
The image below shows an example of the distribution of writes to the Scratchpad. Each pixel in the image represents 8 bytes of the Scratchpad. Red pixels represent portions of the Scratchpad that have been overwritten at least once during hash calculation. The "L1" and "L2" levels are on the left side (almost completely overwritten). The right side of the scratchpad represents the bottom 1792 KiB. Only about 66% of it are overwritten, but the writes are spread uniformly and randomly.
|
||||
|
||||

|
||||
|
||||
See Appendix D for the analysis of Scratchpad entropy.
|
||||
|
||||
#### 2.8.3 Read-write ratio
|
||||
|
||||
Programs make, on average, 39 reads (instructions IADD_M, ISUB_M, IMUL_M, IMULH_M, ISMULH_M, IXOR_M, FADD_M, FSUB_M, FDIV_M) and 16 writes (instruction ISTORE) to the Scratchpad per program iteration. Additional 128 bytes are read and written implicitly to initialize and store register values. 64 bytes of data is read from the Dataset per iteration. In total:
|
||||
|
||||
* The average amount of data read from memory per program iteration is: 39 * 8 + 128 + 64 = **504 bytes**.
|
||||
* The average mount of data written to memory per program iteration is: 16 * 8 + 128 = **256 bytes**.
|
||||
|
||||
This is close to a 2:1 read/write ratio, which CPUs are optimized for.
|
||||
|
||||
### 2.9 Dataset
|
||||
|
||||
Since the Scratchpad is usually stored in the CPU cache, only Dataset accesses utilize the memory controllers.
|
||||
|
||||
RandomX randomly reads from the Dataset once per program iteration (16384 times per hash result). Since the Dataset must be stored in DRAM, it provides a natural parallelization limit, because DRAM cannot do more than about 25 million random accesses per second per bank group. Each separately addressable bank group allows a throughput of around 1500 H/s.
|
||||
|
||||
All Dataset accesses read one CPU cache line (64 bytes) and are fully prefetched. The time to execute one program iteration described in chapter 4.6.2 of the Specification is about the same as typical DRAM access latency (50-100 ns).
|
||||
|
||||
#### 2.9.1 Cache
|
||||
|
||||
The Cache, which is used for light verification and Dataset construction, is about 8 times smaller than the Dataset. To keep a constant area-time product, each Dataset item is constructed from 8 random Cache accesses.
|
||||
|
||||
Because 256 MiB is small enough to be included on-chip, RandomX uses a custom high-latency, high-power mixing function ("SuperscalarHash") which defeats the benefits of using low-latency memory and the energy required to calculate SuperscalarHash makes light mode very inefficient for mining (see chapter 3.4).
|
||||
|
||||
Using less than 256 MiB of memory is not possible due to the use of tradeoff-resistant Argon2d with 3 iterations. When using 3 iterations (passes), halving the memory usage increases computational cost 3423 times for the best tradeoff attack [[27](https://eprint.iacr.org/2015/430.pdf)].
|
||||
|
||||
## 3. Custom functions
|
||||
|
||||
### 3.1 AesGenerator1R
|
||||
|
||||
AesGenerator1R was designed for the fastest possible generation of pseudorandom data to fill the Scratchpad. It takes advantage of hardware accelerated AES in modern CPUs. Only one AES round is performed per 16 bytes of output, which results in throughput exceeding 20 GB/s in most modern CPUs.
|
||||
|
||||
AesGenerator1R gives a good output distribution provided that it's initialized with a sufficiently 'random' initial state (see Appendix F).
|
||||
|
||||
### 3.2 AesGenerator4R
|
||||
|
||||
AesGenerator4R uses 4 AES rounds to generate pseudorandom data for Program Buffer initialization. Since 2 AES rounds are sufficient for full avalanche of all input bits [[28](https://csrc.nist.gov/csrc/media/projects/cryptographic-standards-and-guidelines/documents/aes-development/rijndael-ammended.pdf)], AesGenerator4R has excellent statistical properties (see Appendix F) while maintaining very good performance.
|
||||
|
||||
The reversible nature of this generator is not an issue since the generator state is always initialized using the output of a non-reversible hashing function (Blake2b).
|
||||
|
||||
### 3.3 AesHash1R
|
||||
|
||||
AesHash was designed for the fastest possible calculation of the Scratchpad fingerprint. It interprets the Scratchpad as a set of AES round keys, so it's equivalent to AES encryption with 32768 rounds. Two extra rounds are performed at the end to ensure avalanche of all Scratchpad bits in each lane.
|
||||
|
||||
The reversible nature of AesHash1R is not a problem for two main reasons:
|
||||
|
||||
* It is not possible to directly control the input of AesHash1R.
|
||||
* The output of AesHash1R is passed into the Blake2b hashing function, which is not reversible.
|
||||
|
||||
### 3.4 SuperscalarHash
|
||||
|
||||
SuperscalarHash was designed to burn as much power as possible while the CPU is waiting for data to be loaded from DRAM. The target latency of 170 cycles corresponds to the usual DRAM latency of 40-80 ns and clock frequency of 2-4 GHz. ASIC devices designed for light-mode mining with low-latency memory will be bottlenecked by SuperscalarHash when calculating Dataset items and their efficiency will be destroyed by the high power usage of SuperscalarHash.
|
||||
|
||||
The average SuperscalarHash function contains a total of 450 instructions, out of which 155 are 64-bit multiplications. On average, the longest dependency chain is 95 instructions long. An ASIC design for light-mode mining, with 256 MiB of on-die memory and 1-cycle latency for all operations, will need on average 95 * 8 = 760 cycles to construct a Dataset item, assuming unlimited parallelization. It will have to execute 155 * 8 = 1240 64-bit multiplications per item, which will consume energy comparable to loading 64 bytes from DRAM.
|
||||
|
||||
## Appendix
|
||||
|
||||
### A. The effect of chaining VM executions
|
||||
|
||||
Chapter 1.2 describes why `N` random programs are chained to prevent mining strategies that search for 'easy' programs. RandomX uses a value of `N = 8`.
|
||||
|
||||
Let's define `Q` as the ratio of acceptable programs in a strategy that uses filtering. For example `Q = 0.75` means that 25% of programs are rejected.
|
||||
|
||||
For `N = 1`, there are no wasted program executions and the only cost is program generation and the filtering itself. The calculations below assume that these costs are zero and the only real cost is program execution. However, this is a simplification because program generation in RandomX is not free (the first program generation requires full Scratchpad initialization), but it describes a best-case scenario for an attacker.
|
||||
|
||||
|
||||
For `N > 1`, the first program can be filtered as usual, but after the program is executed, there is a chance of `1-Q` that the next program should be rejected and we have wasted one program execution.
|
||||
|
||||
For `N` chained executions, the chance is only <code>Q<sup>N</sup></code> that all programs in the chain are acceptable. However, during each attempt to find such chain, we will waste the execution of some programs. For `N = 8`, the number of wasted programs per attempt is equal to <code>(1-Q)*(1+2\*Q+3\*Q<sup>2</sup>+4\*Q<sup>3</sup>+5\*Q<sup>4</sup>+6\*Q<sup>5</sup>+7\*Q<sup>6</sup>)</code> (approximately 2.5 for `Q = 0.75`).
|
||||
|
||||
Let's consider 3 mining strategies:
|
||||
|
||||
#### Strategy I
|
||||
|
||||
Honest miner that doesn't reject any programs (`Q = 1`).
|
||||
|
||||
#### Strategy II
|
||||
|
||||
Miner that uses optimized custom hardware that cannot execute 25% of programs (`Q = 0.75`), but supported programs can be executed 50% faster.
|
||||
|
||||
#### Strategy III
|
||||
|
||||
Miner that can execute all programs, but rejects 25% of the slowest programs for the first program in the chain. This gives a 5% performance boost for the first program in the chain (this matches the runtime distribution from Appendix C).
|
||||
|
||||
#### Results
|
||||
|
||||
The table below lists the results for the above 3 strategies and different values of `N`. The columns **N(I)**, **N(II)** and **N(III)** list the number of programs that each strategy has to execute on average to get one valid hash result (this includes programs wasted in rejected chains). Columns **Speed(I)**, **Speed(II)** and **Speed(III)** list the average mining performance relative to strategy I.
|
||||
|
||||
|N|N(I)|N(II)|N(III)|Speed(I)|Speed(II)|Speed(III)|
|
||||
|---|----|----|----|---------|---------|---------|
|
||||
|1|1|1|1|1.00|1.50|1.05|
|
||||
|2|2|2.3|2|1.00|1.28|1.02|
|
||||
|4|4|6.5|4|1.00|0.92|1.01|
|
||||
|8|8|27.0|8|1.00|0.44|1.00|
|
||||
|
||||
For `N = 8`, strategy II will perform at less than half the speed of the honest miner despite having a 50% performance advantage for selected programs. The small statistical advantage of strategy III is negligible with `N = 8`.
|
||||
|
||||
### B. Performance simulation
|
||||
|
||||
As discussed in chapter 2.7, RandomX aims to take advantage of the complex design of modern high-performance CPUs. To evaluate the impact of superscalar, out-of-order and speculative execution, we performed a simplified CPU simulation. Source code is available in [perf-simulation.cpp](../src/tests/perf-simulation.cpp).
|
||||
|
||||
#### CPU model
|
||||
|
||||
The model CPU uses a 3-stage pipeline to achieve an ideal throughput of 1 instruction per cycle:
|
||||
```
|
||||
(1) (2) (3)
|
||||
+------------------+ +----------------+ +----------------+
|
||||
| Instruction | | | | |
|
||||
| fetch | ---> | Memory access | ---> | Execute |
|
||||
| + decode | | | | |
|
||||
+------------------+ +----------------+ +----------------+
|
||||
```
|
||||
The 3 stages are:
|
||||
|
||||
1. Instruction fetch and decode. This stage loads the instruction from the Program Buffer and decodes the instruction operation and operands.
|
||||
2. Memory access. If this instruction uses a memory operand, it is loaded from the Scratchpad in this stage. This includes the calculation of the memory address. Stores are also performed in this stage. The value of the address register must be available in this stage.
|
||||
3. Execute. This stage executes the instruction using the operands retrieved in the previous stages and writes the results into the register file.
|
||||
|
||||
Note that this is an optimistically short pipeline that would not allow very high clock speeds. Designs using a longer pipeline would significantly increase the benefits of speculative execution.
|
||||
|
||||
#### Superscalar execution
|
||||
|
||||
Our model CPU contains two kinds of components:
|
||||
|
||||
* Execution unit (EXU) - it is used to perform the actual integer or floating point operation. All RandomX instructions except ISTORE must use an execution unit in the 3rd pipeline stage. All operations are considered to take only 1 clock cycle.
|
||||
* Memory unit (MEM) - it is used for loads and stores into Scratchpad. All memory instructions (including ISTORE) use a memory unit in the 2nd pipeline stage.
|
||||
|
||||
A superscalar design will contain multiple execution or memory units to improve performance.
|
||||
|
||||
#### Out-of-order execution
|
||||
|
||||
The simulation model supports two designs:
|
||||
|
||||
1. **In-order** - all instructions are executed in the order they appear in the Program Buffer. This design will stall if a dependency is encountered or the required EXU/MEM unit is not available.
|
||||
2. **Out-of-order** - doesn't execute instructions in program order, but an instruction can be executed when its operands are ready and the required EXU/MEM units are available.
|
||||
|
||||
#### Branch handling
|
||||
|
||||
The simulation model supports two types of branch handling:
|
||||
|
||||
1. **Non-speculative** - when a branch is encountered, the pipeline is stalled. This typically adds a 3-cycle penalty for each branch.
|
||||
2. **Speculative** - all branches are predicted not taken and the pipeline is flushed if a misprediction occurs (probability of 1/256).
|
||||
|
||||
#### Results
|
||||
|
||||
The following 10 designs were simulated and the average number of clock cycles to execute a RandomX program (256 instructions) was measured.
|
||||
|
||||
|design|superscalar config.|reordering|branch handling|execution time [cycles]|IPC|
|
||||
|-------|-----------|----------|---------------|-----------------------|---|
|
||||
|#1|1 EXU + 1 MEM|in-order|non-speculative|293|0.87|
|
||||
|#2|1 EXU + 1 MEM|in-order|speculative|262|0.98|
|
||||
|#3|2 EXU + 1 MEM|in-order|non-speculative|197|1.3|
|
||||
|#4|2 EXU + 1 MEM|in-order|speculative|161|1.6|
|
||||
|#5|2 EXU + 1 MEM|out-of-order|non-speculative|144|1.8|
|
||||
|#6|2 EXU + 1 MEM|out-of-order|speculative|122|2.1|
|
||||
|#7|4 EXU + 2 MEM|in-order|non-speculative|135|1.9|
|
||||
|#8|4 EXU + 2 MEM|in-order|speculative|99|2.6|
|
||||
|#9|4 EXU + 2 MEM|out-of-order|non-speculative|89|2.9|
|
||||
|#10|4 EXU + 2 MEM|out-of-order|speculative|64|4.0|
|
||||
|
||||
The benefits of superscalar, out-of-order and speculative designs are clearly demonstrated.
|
||||
|
||||
### C. RandomX runtime distribution
|
||||
|
||||
Runtime numbers were measured on AMD Ryzen 7 1700 running at 3.0 GHz using 1 core. Source code to measure program execution and verification times is available in [runtime-distr.cpp](../src/tests/runtime-distr.cpp). Source code to measure the performance of the x86 JIT compiler is available in [jit-performance.cpp](../src/tests/jit-performance.cpp).
|
||||
|
||||
#### Fast mode - program execution
|
||||
|
||||
The following figure shows the distribution of the runtimes of a single VM program (in fast mode). This includes: program generation, JIT compilation, VM execution and Blake2b hash of the register file. Program generation and JIT compilation was measured to take 3.6 μs per program.
|
||||
|
||||

|
||||
|
||||
AMD Ryzen 7 1700 can calculate 625 hashes per second in fast mode (using 1 thread), which means a single hash result takes 1600 μs (1.6 ms). This consists of (approximately):
|
||||
|
||||
* 1480 μs for VM execution (8 programs)
|
||||
* 45 μs for initial Scratchpad fill (AesGenerator1R).
|
||||
* 45 μs for final Scratchpad hash (AesHash1R).
|
||||
* 30 μs for program generation and JIT compilation (8 programs)
|
||||
|
||||
This gives a total overhead of 7.5% (time per hash spent not executing VM).
|
||||
|
||||
#### Light mode - verification time
|
||||
|
||||
The following figure shows the distribution of times to calculate 1 hash result using the light mode. Most of the time is spent executing SuperscalarHash to calculate Dataset items (13.2 ms out of 14.8 ms). The average verification time exactly matches the performance of the CryptoNight algorithm.
|
||||
|
||||

|
||||
|
||||
### D. Scratchpad entropy analysis
|
||||
|
||||
The average entropy of the Scratchpad after 8 program executions was approximated using the LZMA compression algorithm:
|
||||
|
||||
1. Hash resuls were calculated and the final scratchpads were written to disk as files with '.spad' extension (source code: [scratchpad-entropy.cpp](../src/tests/scratchpad-entropy.cpp))
|
||||
2. The files were compressed using 7-Zip [[29](https://www.7-zip.org/)] in Ultra compression mode: `7z.exe a -t7z -m0=lzma2 -mx=9 scratchpads.7z *.spad`
|
||||
|
||||
The size of the resulting archive is approximately 99.98% of the uncompressed size of the scratchpad files. This shows that the Scratchpad retains high entropy during VM execution.
|
||||
|
||||
### E. SuperscalarHash analysis
|
||||
|
||||
SuperscalarHash is a custom function used by RandomX to generate Dataset items. It operates on 8 integer registers and uses a random sequence of instructions. About 1/3 of the instructions are multiplications.
|
||||
|
||||
The following figure shows the sensitivity of SuperscalarHash to changing a single bit of an input register:
|
||||
|
||||

|
||||
|
||||
This shows that SuperscalaHash has quite low sensitivity to high-order bits and somewhat decreased sensitivity to the lowest-order bits. Sensitivity is highest for bits 3-53 (inclusive).
|
||||
|
||||
When calculating a Dataset item, the input of the first SuperscalarHash depends only on the item number. To ensure a good distribution of results, the constants described in section 7.3 of the Specification were chosen to provide unique values of bits 3-53 for *all* item numbers in the range 0-34078718 (the Dataset contains 34078719 items). All initial register values for all Dataset item numbers were checked to make sure bits 3-53 of each register are unique and there are no collisions (source code: [superscalar-init.cpp](../src/tests/superscalar-init.cpp)). While this is not strictly necessary to get unique output from SuperscalarHash, it's a security precaution that mitigates the non-perfect avalanche properties of the randomly generated SuperscalarHash instances.
|
||||
|
||||
### F. Statistical tests of RNG
|
||||
|
||||
Both AesGenerator1R and AesGenerator4R were tested using the TestU01 library [[30](http://simul.iro.umontreal.ca/testu01/tu01.html)] intended for empirical testing of random number generators. The source code is available in [rng-tests.cpp](../src/tests/rng-tests.cpp).
|
||||
|
||||
The tests sample about 200 MB ("SmallCrush" test), 500 GB ("Crush" test) or 4 TB ("BigCrush" test) of output from each generator. This is considerably more than the amounts generated in RandomX (2176 bytes for AesGenerator4R and 2 MiB for AesGenerator1R), so failures in the tests don't necessarily imply that the generators are not suitable for their use case.
|
||||
|
||||
|
||||
#### AesGenerator4R
|
||||
The generator passes all tests in the "BigCrush" suite when initialized using the Blake2b hash function:
|
||||
|
||||
```
|
||||
$ bin/rng-tests 1
|
||||
state0 = 67e8bbe567a1c18c91a316faf19fab73
|
||||
state1 = 39f7c0e0a8d96512c525852124fdc9fe
|
||||
state2 = 7abb07b2c90e04f098261e323eee8159
|
||||
state3 = 3df534c34cdfbb4e70f8c0e1826f4cf7
|
||||
|
||||
...
|
||||
|
||||
========= Summary results of BigCrush =========
|
||||
|
||||
Version: TestU01 1.2.3
|
||||
Generator: AesGenerator4R
|
||||
Number of statistics: 160
|
||||
Total CPU time: 02:50:18.34
|
||||
|
||||
All tests were passed
|
||||
```
|
||||
|
||||
|
||||
The generator passes all tests in the "Crush" suite even with an initial state set to all zeroes.
|
||||
```
|
||||
$ bin/rng-tests 0
|
||||
state0 = 00000000000000000000000000000000
|
||||
state1 = 00000000000000000000000000000000
|
||||
state2 = 00000000000000000000000000000000
|
||||
state3 = 00000000000000000000000000000000
|
||||
|
||||
...
|
||||
|
||||
========= Summary results of Crush =========
|
||||
|
||||
Version: TestU01 1.2.3
|
||||
Generator: AesGenerator4R
|
||||
Number of statistics: 144
|
||||
Total CPU time: 00:25:17.95
|
||||
|
||||
All tests were passed
|
||||
```
|
||||
|
||||
#### AesGenerator1R
|
||||
|
||||
The generator passes all tests in the "Crush" suite when initialized using the Blake2b hash function.
|
||||
|
||||
```
|
||||
$ bin/rng-tests 1
|
||||
state0 = 67e8bbe567a1c18c91a316faf19fab73
|
||||
state1 = 39f7c0e0a8d96512c525852124fdc9fe
|
||||
state2 = 7abb07b2c90e04f098261e323eee8159
|
||||
state3 = 3df534c34cdfbb4e70f8c0e1826f4cf7
|
||||
|
||||
...
|
||||
|
||||
========= Summary results of Crush =========
|
||||
|
||||
Version: TestU01 1.2.3
|
||||
Generator: AesGenerator1R
|
||||
Number of statistics: 144
|
||||
Total CPU time: 00:25:06.07
|
||||
|
||||
All tests were passed
|
||||
|
||||
```
|
||||
|
||||
When the initial state is initialized to all zeroes, the generator fails 1 test out of 144 tests in the "Crush" suite:
|
||||
|
||||
```
|
||||
$ bin/rng-tests 0
|
||||
state0 = 00000000000000000000000000000000
|
||||
state1 = 00000000000000000000000000000000
|
||||
state2 = 00000000000000000000000000000000
|
||||
state3 = 00000000000000000000000000000000
|
||||
|
||||
...
|
||||
|
||||
========= Summary results of Crush =========
|
||||
|
||||
Version: TestU01 1.2.3
|
||||
Generator: AesGenerator1R
|
||||
Number of statistics: 144
|
||||
Total CPU time: 00:26:12.75
|
||||
The following tests gave p-values outside [0.001, 0.9990]:
|
||||
(eps means a value < 1.0e-300):
|
||||
(eps1 means a value < 1.0e-15):
|
||||
|
||||
Test p-value
|
||||
----------------------------------------------
|
||||
12 BirthdaySpacings, t = 3 1 - 4.4e-5
|
||||
----------------------------------------------
|
||||
All other tests were passed
|
||||
|
||||
```
|
||||
|
||||
## References
|
||||
|
||||
[1] CryptoNote whitepaper - https://cryptonote.org/whitepaper.pdf
|
||||
|
||||
[2] ProgPoW: Inefficient integer multiplications - https://github.com/ifdefelse/ProgPOW/issues/16
|
||||
|
||||
[3] Cryptographic Hashing function - https://en.wikipedia.org/wiki/Cryptographic_hash_function
|
||||
|
||||
[4] randprog - https://github.com/hyc/randprog
|
||||
|
||||
[5] RandomJS - https://github.com/tevador/RandomJS
|
||||
|
||||
[6] μop cache - https://en.wikipedia.org/wiki/CPU_cache#Micro-operation_(%CE%BCop_or_uop)_cache
|
||||
|
||||
[7] Instruction-level parallelism - https://en.wikipedia.org/wiki/Instruction-level_parallelism
|
||||
|
||||
[8] Superscalar processor - https://en.wikipedia.org/wiki/Superscalar_processor
|
||||
|
||||
[9] Out-of-order execution - https://en.wikipedia.org/wiki/Out-of-order_execution
|
||||
|
||||
[10] Speculative execution - https://en.wikipedia.org/wiki/Speculative_execution
|
||||
|
||||
[11] Register renaming - https://en.wikipedia.org/wiki/Register_renaming
|
||||
|
||||
[12] Blake2 hashing function - https://blake2.net/
|
||||
|
||||
[13] Advanced Encryption Standard - https://en.wikipedia.org/wiki/Advanced_Encryption_Standard
|
||||
|
||||
[14] Log-normal distribution - https://en.wikipedia.org/wiki/Log-normal_distribution
|
||||
|
||||
[15] CryptoNight hash function - https://cryptonote.org/cns/cns008.txt
|
||||
|
||||
[16] Dynamic random-access memory - https://en.wikipedia.org/wiki/Dynamic_random-access_memory
|
||||
|
||||
[17] Multi-channel memory architecture - https://en.wikipedia.org/wiki/Multi-channel_memory_architecture
|
||||
|
||||
[18] Obelisk GRN1 chip details - https://www.grin-forum.org/t/obelisk-grn1-chip-details/4571
|
||||
|
||||
[19] Biryukov et al.: Tradeoff Cryptanalysis of Memory-Hard Functions - https://eprint.iacr.org/2015/227.pdf
|
||||
|
||||
[20] SK Hynix 20nm DRAM density - http://en.thelec.kr/news/articleView.html?idxno=20
|
||||
|
||||
[21] Branch predictor - https://en.wikipedia.org/wiki/Branch_predictor
|
||||
|
||||
[22] Predication - https://en.wikipedia.org/wiki/Predication_(computer_architecture)
|
||||
|
||||
[23] CPU cache - https://en.wikipedia.org/wiki/CPU_cache
|
||||
|
||||
[24] Cortex-A55 Microarchitecture - https://www.anandtech.com/show/11441/dynamiq-and-arms-new-cpus-cortex-a75-a55/4
|
||||
|
||||
[25] AMD Zen+ Microarchitecture - https://en.wikichip.org/wiki/amd/microarchitectures/zen%2B#Memory_Hierarchy
|
||||
|
||||
[26] Intel Skylake Microarchitecture - https://en.wikichip.org/wiki/intel/microarchitectures/skylake_(client)#Memory_Hierarchy
|
||||
|
||||
[27] Biryukov et al.: Fast and Tradeoff-Resilient Memory-Hard Functions for
|
||||
Cryptocurrencies and Password Hashing - https://eprint.iacr.org/2015/430.pdf Table 2, page 8
|
||||
|
||||
[28] J. Daemen, V. Rijmen: AES Proposal: Rijndael - https://csrc.nist.gov/csrc/media/projects/cryptographic-standards-and-guidelines/documents/aes-development/rijndael-ammended.pdf page 28
|
||||
|
||||
[29] 7-Zip File archiver - https://www.7-zip.org/
|
||||
|
||||
[30] TestU01 library - http://simul.iro.umontreal.ca/testu01/tu01.html
|
||||
985
app/src/main/cpp/randomx/doc/program.asm
Normal file
985
app/src/main/cpp/randomx/doc/program.asm
Normal file
|
|
@ -0,0 +1,985 @@
|
|||
randomx_isn_0:
|
||||
; ISMULH_R r0, r3
|
||||
mov rax, r8
|
||||
imul r11
|
||||
mov r8, rdx
|
||||
randomx_isn_1:
|
||||
; IROR_R r0, r6
|
||||
mov ecx, r14d
|
||||
ror r8, cl
|
||||
randomx_isn_2:
|
||||
; FADD_R f1, a2
|
||||
addpd xmm1, xmm10
|
||||
randomx_isn_3:
|
||||
; IXOR_M r1, L1[r5+1954652011]
|
||||
lea eax, [r13d+1954652011]
|
||||
and eax, 16376
|
||||
xor r9, qword ptr [rsi+rax]
|
||||
randomx_isn_4:
|
||||
; FMUL_R e2, a3
|
||||
mulpd xmm6, xmm11
|
||||
randomx_isn_5:
|
||||
; FADD_M f0, L2[r0-772804104]
|
||||
lea eax, [r8d-772804104]
|
||||
and eax, 262136
|
||||
cvtdq2pd xmm12, qword ptr [rsi+rax]
|
||||
addpd xmm0, xmm12
|
||||
randomx_isn_6:
|
||||
; IMUL_R r6, r4
|
||||
imul r14, r12
|
||||
randomx_isn_7:
|
||||
; CBRANCH r5, 1674196118, COND 2
|
||||
add r13, 1674196118
|
||||
test r13, 261120
|
||||
jz randomx_isn_0
|
||||
randomx_isn_8:
|
||||
; ISWAP_R r7, r6
|
||||
xchg r15, r14
|
||||
randomx_isn_9:
|
||||
; ISTORE L1[r1-439821682], r3
|
||||
lea eax, [r9d-439821682]
|
||||
and eax, 16376
|
||||
mov qword ptr [rsi+rax], r11
|
||||
randomx_isn_10:
|
||||
; IXOR_R r2, r4
|
||||
xor r10, r12
|
||||
randomx_isn_11:
|
||||
; FADD_R f2, a1
|
||||
addpd xmm2, xmm9
|
||||
randomx_isn_12:
|
||||
; IXOR_M r0, L1[r1+952699079]
|
||||
lea eax, [r9d+952699079]
|
||||
and eax, 16376
|
||||
xor r8, qword ptr [rsi+rax]
|
||||
randomx_isn_13:
|
||||
; ISMULH_R r5, r2
|
||||
mov rax, r13
|
||||
imul r10
|
||||
mov r13, rdx
|
||||
randomx_isn_14:
|
||||
; INEG_R r4
|
||||
neg r12
|
||||
randomx_isn_15:
|
||||
; INEG_R r1
|
||||
neg r9
|
||||
randomx_isn_16:
|
||||
; IMUL_M r3, L1[r2+620091535]
|
||||
lea eax, [r10d+620091535]
|
||||
and eax, 16376
|
||||
imul r11, qword ptr [rsi+rax]
|
||||
randomx_isn_17:
|
||||
; FADD_R f1, a0
|
||||
addpd xmm1, xmm8
|
||||
randomx_isn_18:
|
||||
; IMUL_RCP r5, 2611385784
|
||||
mov rax, 15169754503470242065
|
||||
imul r13, rax
|
||||
randomx_isn_19:
|
||||
; IXOR_R r2, 922368940
|
||||
xor r10, 922368940
|
||||
randomx_isn_20:
|
||||
; FADD_R f3, a1
|
||||
addpd xmm3, xmm9
|
||||
randomx_isn_21:
|
||||
; IXOR_R r3, r6
|
||||
xor r11, r14
|
||||
randomx_isn_22:
|
||||
; FSWAP_R e1
|
||||
shufpd xmm5, xmm5, 1
|
||||
randomx_isn_23:
|
||||
; ISUB_R r0, r5
|
||||
sub r8, r13
|
||||
randomx_isn_24:
|
||||
; ISTORE L1[r6-1574415460], r7
|
||||
lea eax, [r14d-1574415460]
|
||||
and eax, 16376
|
||||
mov qword ptr [rsi+rax], r15
|
||||
randomx_isn_25:
|
||||
; FADD_M f3, L1[r3+1766115210]
|
||||
lea eax, [r11d+1766115210]
|
||||
and eax, 16376
|
||||
cvtdq2pd xmm12, qword ptr [rsi+rax]
|
||||
addpd xmm3, xmm12
|
||||
randomx_isn_26:
|
||||
; FSCAL_R f1
|
||||
xorps xmm1, xmm15
|
||||
randomx_isn_27:
|
||||
; CBRANCH r2, 1731738265, COND 6
|
||||
add r10, 1731746457
|
||||
test r10, 4177920
|
||||
jz randomx_isn_20
|
||||
randomx_isn_28:
|
||||
; IXOR_R r4, r1
|
||||
xor r12, r9
|
||||
randomx_isn_29:
|
||||
; CBRANCH r4, 1937048537, COND 3
|
||||
add r12, 1937050585
|
||||
test r12, 522240
|
||||
jz randomx_isn_29
|
||||
randomx_isn_30:
|
||||
; ISWAP_R r3, r5
|
||||
xchg r11, r13
|
||||
randomx_isn_31:
|
||||
; ISMULH_R r7, r5
|
||||
mov rax, r15
|
||||
imul r13
|
||||
mov r15, rdx
|
||||
randomx_isn_32:
|
||||
; IMULH_M r6, L1[r2+1879111790]
|
||||
lea ecx, [r10d+1879111790]
|
||||
and ecx, 16376
|
||||
mov rax, r14
|
||||
mul qword ptr [rsi+rcx]
|
||||
mov r14, rdx
|
||||
randomx_isn_33:
|
||||
; IMUL_R r5, r0
|
||||
imul r13, r8
|
||||
randomx_isn_34:
|
||||
; ISWAP_R r5, r0
|
||||
xchg r13, r8
|
||||
randomx_isn_35:
|
||||
; CBRANCH r4, 1174490916, COND 5
|
||||
add r12, 1174499108
|
||||
test r12, 2088960
|
||||
jz randomx_isn_30
|
||||
randomx_isn_36:
|
||||
; CBRANCH r6, -1852457840, COND 8
|
||||
add r14, -1852490608
|
||||
test r14, 16711680
|
||||
jz randomx_isn_36
|
||||
randomx_isn_37:
|
||||
; ISMULH_R r2, r0
|
||||
mov rax, r10
|
||||
imul r8
|
||||
mov r10, rdx
|
||||
randomx_isn_38:
|
||||
; ISUB_R r2, r0
|
||||
sub r10, r8
|
||||
randomx_isn_39:
|
||||
; ISTORE L1[r0-38118463], r5
|
||||
lea eax, [r8d-38118463]
|
||||
and eax, 16376
|
||||
mov qword ptr [rsi+rax], r13
|
||||
randomx_isn_40:
|
||||
; IXOR_R r0, r1
|
||||
xor r8, r9
|
||||
randomx_isn_41:
|
||||
; IMUL_R r6, r4
|
||||
imul r14, r12
|
||||
randomx_isn_42:
|
||||
; ISUB_R r7, r5
|
||||
sub r15, r13
|
||||
randomx_isn_43:
|
||||
; FDIV_M e0, L1[r2+1052956160]
|
||||
lea eax, [r10d+1052956160]
|
||||
and eax, 16376
|
||||
cvtdq2pd xmm12, qword ptr [rsi+rax]
|
||||
andps xmm12, xmm13
|
||||
orps xmm12, xmm14
|
||||
divpd xmm4, xmm12
|
||||
randomx_isn_44:
|
||||
; CBRANCH r1, 1870241002, COND 11
|
||||
add r9, 1870241002
|
||||
test r9, 133693440
|
||||
jz randomx_isn_37
|
||||
randomx_isn_45:
|
||||
; IXOR_R r1, r4
|
||||
xor r9, r12
|
||||
randomx_isn_46:
|
||||
; FMUL_R e3, a1
|
||||
mulpd xmm7, xmm9
|
||||
randomx_isn_47:
|
||||
; IXOR_M r0, L1[r2+839895331]
|
||||
lea eax, [r10d+839895331]
|
||||
and eax, 16376
|
||||
xor r8, qword ptr [rsi+rax]
|
||||
randomx_isn_48:
|
||||
; CBRANCH r2, -2128896196, COND 6
|
||||
add r10, -2128879812
|
||||
test r10, 4177920
|
||||
jz randomx_isn_45
|
||||
randomx_isn_49:
|
||||
; CFROUND r1, 13
|
||||
mov rax, r9
|
||||
and eax, 24576
|
||||
or eax, 40896
|
||||
push rax
|
||||
ldmxcsr dword ptr [rsp]
|
||||
pop rax
|
||||
randomx_isn_50:
|
||||
; ISWAP_R r3, r1
|
||||
xchg r11, r9
|
||||
randomx_isn_51:
|
||||
; IMUL_RCP r1, 4205062916
|
||||
mov rax, 9420568026795290117
|
||||
imul r9, rax
|
||||
randomx_isn_52:
|
||||
; FSUB_R f0, a0
|
||||
subpd xmm0, xmm8
|
||||
randomx_isn_53:
|
||||
; IMUL_R r7, r6
|
||||
imul r15, r14
|
||||
randomx_isn_54:
|
||||
; IADD_RS r1, r2, SHFT 3
|
||||
lea r9, [r9+r10*8]
|
||||
randomx_isn_55:
|
||||
; FSQRT_R e3
|
||||
sqrtpd xmm7, xmm7
|
||||
randomx_isn_56:
|
||||
; FMUL_R e1, a0
|
||||
mulpd xmm5, xmm8
|
||||
randomx_isn_57:
|
||||
; IMUL_RCP r3, 303101651
|
||||
mov rax, 16336962008634921950
|
||||
imul r11, rax
|
||||
randomx_isn_58:
|
||||
; IMUL_RCP r1, 3375482677
|
||||
mov rax, 11735827153567160432
|
||||
imul r9, rax
|
||||
randomx_isn_59:
|
||||
; CBRANCH r6, 2116776661, COND 12
|
||||
add r14, 2117300949
|
||||
test r14, 267386880
|
||||
jz randomx_isn_49
|
||||
randomx_isn_60:
|
||||
; IMUL_R r3, r4
|
||||
imul r11, r12
|
||||
randomx_isn_61:
|
||||
; FMUL_R e3, a0
|
||||
mulpd xmm7, xmm8
|
||||
randomx_isn_62:
|
||||
; ISUB_R r3, 1514378938
|
||||
sub r11, 1514378938
|
||||
randomx_isn_63:
|
||||
; FMUL_R e2, a0
|
||||
mulpd xmm6, xmm8
|
||||
randomx_isn_64:
|
||||
; ISUB_R r4, r6
|
||||
sub r12, r14
|
||||
randomx_isn_65:
|
||||
; FDIV_M e2, L1[r0+1496571595]
|
||||
lea eax, [r8d+1496571595]
|
||||
and eax, 16376
|
||||
cvtdq2pd xmm12, qword ptr [rsi+rax]
|
||||
andps xmm12, xmm13
|
||||
orps xmm12, xmm14
|
||||
divpd xmm6, xmm12
|
||||
randomx_isn_66:
|
||||
; FSUB_R f0, a2
|
||||
subpd xmm0, xmm10
|
||||
randomx_isn_67:
|
||||
; FDIV_M e3, L2[r7-2139079025]
|
||||
lea eax, [r15d-2139079025]
|
||||
and eax, 262136
|
||||
cvtdq2pd xmm12, qword ptr [rsi+rax]
|
||||
andps xmm12, xmm13
|
||||
orps xmm12, xmm14
|
||||
divpd xmm7, xmm12
|
||||
randomx_isn_68:
|
||||
; FSUB_R f2, a2
|
||||
subpd xmm2, xmm10
|
||||
randomx_isn_69:
|
||||
; CBRANCH r3, -1165095866, COND 7
|
||||
add r11, -1165063098
|
||||
test r11, 8355840
|
||||
jz randomx_isn_63
|
||||
randomx_isn_70:
|
||||
; IMULH_R r0, r7
|
||||
mov rax, r8
|
||||
mul r15
|
||||
mov r8, rdx
|
||||
randomx_isn_71:
|
||||
; FMUL_R e2, a0
|
||||
mulpd xmm6, xmm8
|
||||
randomx_isn_72:
|
||||
; FMUL_R e0, a3
|
||||
mulpd xmm4, xmm11
|
||||
randomx_isn_73:
|
||||
; IMUL_RCP r6, 1636610180
|
||||
mov rax, 12102479179596746977
|
||||
imul r14, rax
|
||||
randomx_isn_74:
|
||||
; FMUL_R e2, a2
|
||||
mulpd xmm6, xmm10
|
||||
randomx_isn_75:
|
||||
; ISTORE L2[r2+473418592], r3
|
||||
lea eax, [r10d+473418592]
|
||||
and eax, 262136
|
||||
mov qword ptr [rsi+rax], r11
|
||||
randomx_isn_76:
|
||||
; IADD_M r1, L1[r3-989917936]
|
||||
lea eax, [r11d-989917936]
|
||||
and eax, 16376
|
||||
add r9, qword ptr [rsi+rax]
|
||||
randomx_isn_77:
|
||||
; CBRANCH r2, 1519854177, COND 7
|
||||
add r10, 1519886945
|
||||
test r10, 8355840
|
||||
jz randomx_isn_70
|
||||
randomx_isn_78:
|
||||
; IMUL_R r2, r6
|
||||
imul r10, r14
|
||||
randomx_isn_79:
|
||||
; IMUL_R r4, r1
|
||||
imul r12, r9
|
||||
randomx_isn_80:
|
||||
; FMUL_R e2, a1
|
||||
mulpd xmm6, xmm9
|
||||
randomx_isn_81:
|
||||
; FSCAL_R f2
|
||||
xorps xmm2, xmm15
|
||||
randomx_isn_82:
|
||||
; IXOR_M r2, L1[r1+192323103]
|
||||
lea eax, [r9d+192323103]
|
||||
and eax, 16376
|
||||
xor r10, qword ptr [rsi+rax]
|
||||
randomx_isn_83:
|
||||
; IMUL_R r7, r4
|
||||
imul r15, r12
|
||||
randomx_isn_84:
|
||||
; FADD_R f2, a0
|
||||
addpd xmm2, xmm8
|
||||
randomx_isn_85:
|
||||
; FSUB_M f1, L2[r6-1549504487]
|
||||
lea eax, [r14d-1549504487]
|
||||
and eax, 262136
|
||||
cvtdq2pd xmm12, qword ptr [rsi+rax]
|
||||
subpd xmm1, xmm12
|
||||
randomx_isn_86:
|
||||
; FSUB_R f0, a3
|
||||
subpd xmm0, xmm11
|
||||
randomx_isn_87:
|
||||
; CFROUND r3, 31
|
||||
mov rax, r11
|
||||
rol rax, 46
|
||||
and eax, 24576
|
||||
or eax, 40896
|
||||
push rax
|
||||
ldmxcsr dword ptr [rsp]
|
||||
pop rax
|
||||
randomx_isn_88:
|
||||
; IXOR_R r5, r6
|
||||
xor r13, r14
|
||||
randomx_isn_89:
|
||||
; FADD_R f3, a2
|
||||
addpd xmm3, xmm10
|
||||
randomx_isn_90:
|
||||
; FADD_R f3, a0
|
||||
addpd xmm3, xmm8
|
||||
randomx_isn_91:
|
||||
; FSQRT_R e1
|
||||
sqrtpd xmm5, xmm5
|
||||
randomx_isn_92:
|
||||
; ISUB_R r6, r2
|
||||
sub r14, r10
|
||||
randomx_isn_93:
|
||||
; ISUB_R r0, r4
|
||||
sub r8, r12
|
||||
randomx_isn_94:
|
||||
; FADD_R f1, a2
|
||||
addpd xmm1, xmm10
|
||||
randomx_isn_95:
|
||||
; IMUL_R r1, r2
|
||||
imul r9, r10
|
||||
randomx_isn_96:
|
||||
; FSCAL_R f1
|
||||
xorps xmm1, xmm15
|
||||
randomx_isn_97:
|
||||
; ISTORE L1[r7-1901001017], r7
|
||||
lea eax, [r15d-1901001017]
|
||||
and eax, 16376
|
||||
mov qword ptr [rsi+rax], r15
|
||||
randomx_isn_98:
|
||||
; FADD_R f1, a3
|
||||
addpd xmm1, xmm11
|
||||
randomx_isn_99:
|
||||
; CBRANCH r2, -425599201, COND 9
|
||||
add r10, -425533665
|
||||
test r10, 33423360
|
||||
jz randomx_isn_83
|
||||
randomx_isn_100:
|
||||
; IXOR_R r4, r6
|
||||
xor r12, r14
|
||||
randomx_isn_101:
|
||||
; FMUL_R e0, a3
|
||||
mulpd xmm4, xmm11
|
||||
randomx_isn_102:
|
||||
; FADD_M f0, L1[r0+1590646897]
|
||||
lea eax, [r8d+1590646897]
|
||||
and eax, 16376
|
||||
cvtdq2pd xmm12, qword ptr [rsi+rax]
|
||||
addpd xmm0, xmm12
|
||||
randomx_isn_103:
|
||||
; FMUL_R e0, a1
|
||||
mulpd xmm4, xmm9
|
||||
randomx_isn_104:
|
||||
; IMUL_R r4, r7
|
||||
imul r12, r15
|
||||
randomx_isn_105:
|
||||
; ISUB_R r1, r0
|
||||
sub r9, r8
|
||||
randomx_isn_106:
|
||||
; FSUB_R f1, a2
|
||||
subpd xmm1, xmm10
|
||||
randomx_isn_107:
|
||||
; FMUL_R e1, a1
|
||||
mulpd xmm5, xmm9
|
||||
randomx_isn_108:
|
||||
; FMUL_R e1, a2
|
||||
mulpd xmm5, xmm10
|
||||
randomx_isn_109:
|
||||
; FADD_R f3, a2
|
||||
addpd xmm3, xmm10
|
||||
randomx_isn_110:
|
||||
; IXOR_R r0, r3
|
||||
xor r8, r11
|
||||
randomx_isn_111:
|
||||
; IMUL_R r0, 1421329412
|
||||
imul r8, 1421329412
|
||||
randomx_isn_112:
|
||||
; FSUB_R f0, a2
|
||||
subpd xmm0, xmm10
|
||||
randomx_isn_113:
|
||||
; IMUL_R r5, r4
|
||||
imul r13, r12
|
||||
randomx_isn_114:
|
||||
; IADD_RS r7, r3, SHFT 2
|
||||
lea r15, [r15+r11*4]
|
||||
randomx_isn_115:
|
||||
; FADD_R f3, a3
|
||||
addpd xmm3, xmm11
|
||||
randomx_isn_116:
|
||||
; ISTORE L1[r3-160363922], r0
|
||||
lea eax, [r11d-160363922]
|
||||
and eax, 16376
|
||||
mov qword ptr [rsi+rax], r8
|
||||
randomx_isn_117:
|
||||
; IMULH_R r0, r6
|
||||
mov rax, r8
|
||||
mul r14
|
||||
mov r8, rdx
|
||||
randomx_isn_118:
|
||||
; FSWAP_R f2
|
||||
shufpd xmm2, xmm2, 1
|
||||
randomx_isn_119:
|
||||
; FMUL_R e1, a0
|
||||
mulpd xmm5, xmm8
|
||||
randomx_isn_120:
|
||||
; IROR_R r0, 12
|
||||
ror r8, 12
|
||||
randomx_isn_121:
|
||||
; FADD_M f0, L1[r0+282806289]
|
||||
lea eax, [r8d+282806289]
|
||||
and eax, 16376
|
||||
cvtdq2pd xmm12, qword ptr [rsi+rax]
|
||||
addpd xmm0, xmm12
|
||||
randomx_isn_122:
|
||||
; FADD_M f3, L1[r7+1601529113]
|
||||
lea eax, [r15d+1601529113]
|
||||
and eax, 16376
|
||||
cvtdq2pd xmm12, qword ptr [rsi+rax]
|
||||
addpd xmm3, xmm12
|
||||
randomx_isn_123:
|
||||
; IMUL_RCP r2, 2522040806
|
||||
mov rax, 15707153176462985744
|
||||
imul r10, rax
|
||||
randomx_isn_124:
|
||||
; ISUB_M r0, L1[r3+974906597]
|
||||
lea eax, [r11d+974906597]
|
||||
and eax, 16376
|
||||
sub r8, qword ptr [rsi+rax]
|
||||
randomx_isn_125:
|
||||
; CBRANCH r2, 1508706439, COND 14
|
||||
add r10, 1506609287
|
||||
test r10, 1069547520
|
||||
jz randomx_isn_124
|
||||
randomx_isn_126:
|
||||
; IXOR_R r4, r5
|
||||
xor r12, r13
|
||||
randomx_isn_127:
|
||||
; IMUL_R r7, r2
|
||||
imul r15, r10
|
||||
randomx_isn_128:
|
||||
; IROR_R r4, r0
|
||||
mov ecx, r8d
|
||||
ror r12, cl
|
||||
randomx_isn_129:
|
||||
; CBRANCH r0, -497803311, COND 3
|
||||
add r8, -497804335
|
||||
test r8, 522240
|
||||
jz randomx_isn_126
|
||||
randomx_isn_130:
|
||||
; FSUB_M f0, L1[r3+1789853646]
|
||||
lea eax, [r11d+1789853646]
|
||||
and eax, 16376
|
||||
cvtdq2pd xmm12, qword ptr [rsi+rax]
|
||||
subpd xmm0, xmm12
|
||||
randomx_isn_131:
|
||||
; ISMULH_R r6, r3
|
||||
mov rax, r14
|
||||
imul r11
|
||||
mov r14, rdx
|
||||
randomx_isn_132:
|
||||
; FMUL_R e0, a3
|
||||
mulpd xmm4, xmm11
|
||||
randomx_isn_133:
|
||||
; FSUB_R f2, a1
|
||||
subpd xmm2, xmm9
|
||||
randomx_isn_134:
|
||||
; CBRANCH r3, -1567551204, COND 11
|
||||
add r11, -1567026916
|
||||
test r11, 133693440
|
||||
jz randomx_isn_130
|
||||
randomx_isn_135:
|
||||
; FSUB_M f2, L2[r5+1167508659]
|
||||
lea eax, [r13d+1167508659]
|
||||
and eax, 262136
|
||||
cvtdq2pd xmm12, qword ptr [rsi+rax]
|
||||
subpd xmm2, xmm12
|
||||
randomx_isn_136:
|
||||
; IMUL_R r4, r0
|
||||
imul r12, r8
|
||||
randomx_isn_137:
|
||||
; IMULH_R r7, r6
|
||||
mov rax, r15
|
||||
mul r14
|
||||
mov r15, rdx
|
||||
randomx_isn_138:
|
||||
; FMUL_R e3, a2
|
||||
mulpd xmm7, xmm10
|
||||
randomx_isn_139:
|
||||
; IMUL_R r2, r6
|
||||
imul r10, r14
|
||||
randomx_isn_140:
|
||||
; ISTORE L1[r0+1277653290], r3
|
||||
lea eax, [r8d+1277653290]
|
||||
and eax, 16376
|
||||
mov qword ptr [rsi+rax], r11
|
||||
randomx_isn_141:
|
||||
; IXOR_M r0, L1[r6-2131931958]
|
||||
lea eax, [r14d-2131931958]
|
||||
and eax, 16376
|
||||
xor r8, qword ptr [rsi+rax]
|
||||
randomx_isn_142:
|
||||
; FSUB_R f3, a3
|
||||
subpd xmm3, xmm11
|
||||
randomx_isn_143:
|
||||
; IROL_R r6, r1
|
||||
mov ecx, r9d
|
||||
rol r14, cl
|
||||
randomx_isn_144:
|
||||
; FADD_R f1, a3
|
||||
addpd xmm1, xmm11
|
||||
randomx_isn_145:
|
||||
; FMUL_R e0, a3
|
||||
mulpd xmm4, xmm11
|
||||
randomx_isn_146:
|
||||
; FSQRT_R e0
|
||||
sqrtpd xmm4, xmm4
|
||||
randomx_isn_147:
|
||||
; IADD_RS r7, r4, SHFT 0
|
||||
lea r15, [r15+r12*1]
|
||||
randomx_isn_148:
|
||||
; FSUB_R f3, a1
|
||||
subpd xmm3, xmm9
|
||||
randomx_isn_149:
|
||||
; ISTORE L2[r1-1073333533], r3
|
||||
lea eax, [r9d-1073333533]
|
||||
and eax, 262136
|
||||
mov qword ptr [rsi+rax], r11
|
||||
randomx_isn_150:
|
||||
; FMUL_R e3, a3
|
||||
mulpd xmm7, xmm11
|
||||
randomx_isn_151:
|
||||
; ISUB_R r6, r3
|
||||
sub r14, r11
|
||||
randomx_isn_152:
|
||||
; IMULH_M r7, L2[r1+1647843648]
|
||||
lea ecx, [r9d+1647843648]
|
||||
and ecx, 262136
|
||||
mov rax, r15
|
||||
mul qword ptr [rsi+rcx]
|
||||
mov r15, rdx
|
||||
randomx_isn_153:
|
||||
; FMUL_R e0, a0
|
||||
mulpd xmm4, xmm8
|
||||
randomx_isn_154:
|
||||
; IROR_R r3, r0
|
||||
mov ecx, r8d
|
||||
ror r11, cl
|
||||
randomx_isn_155:
|
||||
; IADD_M r3, L1[r7-1322060518]
|
||||
lea eax, [r15d-1322060518]
|
||||
and eax, 16376
|
||||
add r11, qword ptr [rsi+rax]
|
||||
randomx_isn_156:
|
||||
; CBRANCH r3, 608981196, COND 1
|
||||
add r11, 608981708
|
||||
test r11, 130560
|
||||
jz randomx_isn_156
|
||||
randomx_isn_157:
|
||||
; FSUB_M f0, L2[r7-252644586]
|
||||
lea eax, [r15d-252644586]
|
||||
and eax, 262136
|
||||
cvtdq2pd xmm12, qword ptr [rsi+rax]
|
||||
subpd xmm0, xmm12
|
||||
randomx_isn_158:
|
||||
; CBRANCH r2, 868397474, COND 15
|
||||
add r10, 864203170
|
||||
test r10, 2139095040
|
||||
jz randomx_isn_157
|
||||
randomx_isn_159:
|
||||
; ISUB_R r5, r3
|
||||
sub r13, r11
|
||||
randomx_isn_160:
|
||||
; FMUL_R e0, a0
|
||||
mulpd xmm4, xmm8
|
||||
randomx_isn_161:
|
||||
; FMUL_R e2, a1
|
||||
mulpd xmm6, xmm9
|
||||
randomx_isn_162:
|
||||
; CBRANCH r0, 887338591, COND 6
|
||||
add r8, 887346783
|
||||
test r8, 4177920
|
||||
jz randomx_isn_159
|
||||
randomx_isn_163:
|
||||
; IADD_RS r3, r3, SHFT 3
|
||||
lea r11, [r11+r11*8]
|
||||
randomx_isn_164:
|
||||
; IMUL_RCP r7, 3593878304
|
||||
mov rax, 11022655166993703745
|
||||
imul r15, rax
|
||||
randomx_isn_165:
|
||||
; CBRANCH r0, 1452880957, COND 13
|
||||
add r8, 1453929533
|
||||
test r8, 534773760
|
||||
jz randomx_isn_163
|
||||
randomx_isn_166:
|
||||
; ISUB_M r6, L2[r3+1539038396]
|
||||
lea eax, [r11d+1539038396]
|
||||
and eax, 262136
|
||||
sub r14, qword ptr [rsi+rax]
|
||||
randomx_isn_167:
|
||||
; IMUL_RCP r3, 1202036339
|
||||
mov rax, 16477905023274079568
|
||||
imul r11, rax
|
||||
randomx_isn_168:
|
||||
; CBRANCH r1, -1295757940, COND 13
|
||||
add r9, -1293660788
|
||||
test r9, 534773760
|
||||
jz randomx_isn_166
|
||||
randomx_isn_169:
|
||||
; FADD_M f2, L1[r2+876697387]
|
||||
lea eax, [r10d+876697387]
|
||||
and eax, 16376
|
||||
cvtdq2pd xmm12, qword ptr [rsi+rax]
|
||||
addpd xmm2, xmm12
|
||||
randomx_isn_170:
|
||||
; IMUL_R r0, r6
|
||||
imul r8, r14
|
||||
randomx_isn_171:
|
||||
; FMUL_R e1, a3
|
||||
mulpd xmm5, xmm11
|
||||
randomx_isn_172:
|
||||
; FMUL_R e0, a2
|
||||
mulpd xmm4, xmm10
|
||||
randomx_isn_173:
|
||||
; FSUB_M f3, L1[r2-1083472792]
|
||||
lea eax, [r10d-1083472792]
|
||||
and eax, 16376
|
||||
cvtdq2pd xmm12, qword ptr [rsi+rax]
|
||||
subpd xmm3, xmm12
|
||||
randomx_isn_174:
|
||||
; CBRANCH r1, -1476890738, COND 14
|
||||
add r9, -1478987890
|
||||
test r9, 1069547520
|
||||
jz randomx_isn_169
|
||||
randomx_isn_175:
|
||||
; ISUB_R r4, r7
|
||||
sub r12, r15
|
||||
randomx_isn_176:
|
||||
; ISUB_R r0, 1685118604
|
||||
sub r8, 1685118604
|
||||
randomx_isn_177:
|
||||
; FMUL_R e0, a1
|
||||
mulpd xmm4, xmm9
|
||||
randomx_isn_178:
|
||||
; ISUB_M r0, L1[r7-1897974312]
|
||||
lea eax, [r15d-1897974312]
|
||||
and eax, 16376
|
||||
sub r8, qword ptr [rsi+rax]
|
||||
randomx_isn_179:
|
||||
; IXOR_R r4, r0
|
||||
xor r12, r8
|
||||
randomx_isn_180:
|
||||
; IXOR_R r7, r2
|
||||
xor r15, r10
|
||||
randomx_isn_181:
|
||||
; FSCAL_R f1
|
||||
xorps xmm1, xmm15
|
||||
randomx_isn_182:
|
||||
; ISWAP_R r6, r2
|
||||
xchg r14, r10
|
||||
randomx_isn_183:
|
||||
; IADD_RS r3, r1, SHFT 3
|
||||
lea r11, [r11+r9*8]
|
||||
randomx_isn_184:
|
||||
; ISTORE L1[r6-1997634426], r7
|
||||
lea eax, [r14d-1997634426]
|
||||
and eax, 16376
|
||||
mov qword ptr [rsi+rax], r15
|
||||
randomx_isn_185:
|
||||
; IXOR_R r2, r7
|
||||
xor r10, r15
|
||||
randomx_isn_186:
|
||||
; IMUL_R r4, r3
|
||||
imul r12, r11
|
||||
randomx_isn_187:
|
||||
; IMUL_RCP r7, 1830833174
|
||||
mov rax, 10818593911149047378
|
||||
imul r15, rax
|
||||
randomx_isn_188:
|
||||
; FMUL_R e0, a2
|
||||
mulpd xmm4, xmm10
|
||||
randomx_isn_189:
|
||||
; FADD_R f1, a3
|
||||
addpd xmm1, xmm11
|
||||
randomx_isn_190:
|
||||
; CBRANCH r7, 121030040, COND 15
|
||||
add r15, 129418648
|
||||
test r15, 2139095040
|
||||
jz randomx_isn_188
|
||||
randomx_isn_191:
|
||||
; IADD_RS r6, r1, SHFT 0
|
||||
lea r14, [r14+r9*1]
|
||||
randomx_isn_192:
|
||||
; FSUB_R f3, a2
|
||||
subpd xmm3, xmm10
|
||||
randomx_isn_193:
|
||||
; CBRANCH r5, 1139434462, COND 11
|
||||
add r13, 1139434462
|
||||
test r13, 133693440
|
||||
jz randomx_isn_191
|
||||
randomx_isn_194:
|
||||
; FMUL_R e2, a0
|
||||
mulpd xmm6, xmm8
|
||||
randomx_isn_195:
|
||||
; FMUL_R e2, a3
|
||||
mulpd xmm6, xmm11
|
||||
randomx_isn_196:
|
||||
; CBRANCH r4, 429294077, COND 2
|
||||
add r12, 429295101
|
||||
test r12, 261120
|
||||
jz randomx_isn_194
|
||||
randomx_isn_197:
|
||||
; IMUL_R r1, r2
|
||||
imul r9, r10
|
||||
randomx_isn_198:
|
||||
; FMUL_R e3, a0
|
||||
mulpd xmm7, xmm8
|
||||
randomx_isn_199:
|
||||
; IMUL_R r2, r3
|
||||
imul r10, r11
|
||||
randomx_isn_200:
|
||||
; IMUL_RCP r1, 193535702
|
||||
mov rax, 12792885514067893012
|
||||
imul r9, rax
|
||||
randomx_isn_201:
|
||||
; IMUL_R r0, r5
|
||||
imul r8, r13
|
||||
randomx_isn_202:
|
||||
; ISUB_R r1, r2
|
||||
sub r9, r10
|
||||
randomx_isn_203:
|
||||
; FSUB_R f0, a3
|
||||
subpd xmm0, xmm11
|
||||
randomx_isn_204:
|
||||
; FSQRT_R e3
|
||||
sqrtpd xmm7, xmm7
|
||||
randomx_isn_205:
|
||||
; FMUL_R e0, a3
|
||||
mulpd xmm4, xmm11
|
||||
randomx_isn_206:
|
||||
; IMUL_R r2, r1
|
||||
imul r10, r9
|
||||
randomx_isn_207:
|
||||
; IADD_RS r1, r1, SHFT 3
|
||||
lea r9, [r9+r9*8]
|
||||
randomx_isn_208:
|
||||
; ISUB_R r6, r4
|
||||
sub r14, r12
|
||||
randomx_isn_209:
|
||||
; ISUB_R r0, r7
|
||||
sub r8, r15
|
||||
randomx_isn_210:
|
||||
; IADD_M r6, L1[r1+313140284]
|
||||
lea eax, [r9d+313140284]
|
||||
and eax, 16376
|
||||
add r14, qword ptr [rsi+rax]
|
||||
randomx_isn_211:
|
||||
; CBRANCH r4, 1358359929, COND 11
|
||||
add r12, 1358622073
|
||||
test r12, 133693440
|
||||
jz randomx_isn_197
|
||||
randomx_isn_212:
|
||||
; FSQRT_R e0
|
||||
sqrtpd xmm4, xmm4
|
||||
randomx_isn_213:
|
||||
; ISTORE L1[r3+18641493], r5
|
||||
lea eax, [r11d+18641493]
|
||||
and eax, 16376
|
||||
mov qword ptr [rsi+rax], r13
|
||||
randomx_isn_214:
|
||||
; CBRANCH r2, 1232471888, COND 7
|
||||
add r10, 1232504656
|
||||
test r10, 8355840
|
||||
jz randomx_isn_212
|
||||
randomx_isn_215:
|
||||
; IADD_M r1, L1[r3+1138069575]
|
||||
lea eax, [r11d+1138069575]
|
||||
and eax, 16376
|
||||
add r9, qword ptr [rsi+rax]
|
||||
randomx_isn_216:
|
||||
; FSQRT_R e0
|
||||
sqrtpd xmm4, xmm4
|
||||
randomx_isn_217:
|
||||
; IMUL_R r3, r4
|
||||
imul r11, r12
|
||||
randomx_isn_218:
|
||||
; FMUL_R e3, a3
|
||||
mulpd xmm7, xmm11
|
||||
randomx_isn_219:
|
||||
; IROL_R r7, r1
|
||||
mov ecx, r9d
|
||||
rol r15, cl
|
||||
randomx_isn_220:
|
||||
; FMUL_R e2, a1
|
||||
mulpd xmm6, xmm9
|
||||
randomx_isn_221:
|
||||
; IXOR_M r2, L3[697832]
|
||||
xor r10, qword ptr [rsi+697832]
|
||||
randomx_isn_222:
|
||||
; IADD_RS r1, r6, SHFT 2
|
||||
lea r9, [r9+r14*4]
|
||||
randomx_isn_223:
|
||||
; ISWAP_R r6, r2
|
||||
xchg r14, r10
|
||||
randomx_isn_224:
|
||||
; ISUB_R r0, r1
|
||||
sub r8, r9
|
||||
randomx_isn_225:
|
||||
; FSQRT_R e3
|
||||
sqrtpd xmm7, xmm7
|
||||
randomx_isn_226:
|
||||
; ISUB_R r5, r1
|
||||
sub r13, r9
|
||||
randomx_isn_227:
|
||||
; ISTORE L1[r0+238217802], r2
|
||||
lea eax, [r8d+238217802]
|
||||
and eax, 16376
|
||||
mov qword ptr [rsi+rax], r10
|
||||
randomx_isn_228:
|
||||
; IMUL_RCP r5, 324261767
|
||||
mov rax, 15270872674734795667
|
||||
imul r13, rax
|
||||
randomx_isn_229:
|
||||
; FSCAL_R f0
|
||||
xorps xmm0, xmm15
|
||||
randomx_isn_230:
|
||||
; FSQRT_R e3
|
||||
sqrtpd xmm7, xmm7
|
||||
randomx_isn_231:
|
||||
; IROL_R r1, r5
|
||||
mov ecx, r13d
|
||||
rol r9, cl
|
||||
randomx_isn_232:
|
||||
; ISUB_R r6, r1
|
||||
sub r14, r9
|
||||
randomx_isn_233:
|
||||
; FADD_R f2, a0
|
||||
addpd xmm2, xmm8
|
||||
randomx_isn_234:
|
||||
; FADD_R f1, a3
|
||||
addpd xmm1, xmm11
|
||||
randomx_isn_235:
|
||||
; IXOR_R r3, 1240450588
|
||||
xor r11, 1240450588
|
||||
randomx_isn_236:
|
||||
; FSUB_R f1, a2
|
||||
subpd xmm1, xmm10
|
||||
randomx_isn_237:
|
||||
; IMULH_R r6, r3
|
||||
mov rax, r14
|
||||
mul r11
|
||||
mov r14, rdx
|
||||
randomx_isn_238:
|
||||
; FSUB_R f1, a3
|
||||
subpd xmm1, xmm11
|
||||
randomx_isn_239:
|
||||
; FSUB_R f1, a2
|
||||
subpd xmm1, xmm10
|
||||
randomx_isn_240:
|
||||
; FSUB_M f1, L1[r7+1330184615]
|
||||
lea eax, [r15d+1330184615]
|
||||
and eax, 16376
|
||||
cvtdq2pd xmm12, qword ptr [rsi+rax]
|
||||
subpd xmm1, xmm12
|
||||
randomx_isn_241:
|
||||
; FMUL_R e2, a3
|
||||
mulpd xmm6, xmm11
|
||||
randomx_isn_242:
|
||||
; CBRANCH r3, -427325404, COND 11
|
||||
add r11, -427063260
|
||||
test r11, 133693440
|
||||
jz randomx_isn_236
|
||||
randomx_isn_243:
|
||||
; IMUL_R r5, r7
|
||||
imul r13, r15
|
||||
randomx_isn_244:
|
||||
; FMUL_R e3, a3
|
||||
mulpd xmm7, xmm11
|
||||
randomx_isn_245:
|
||||
; ISMULH_M r7, L1[r0-84959236]
|
||||
lea ecx, [r8d-84959236]
|
||||
and ecx, 16376
|
||||
mov rax, r15
|
||||
imul qword ptr [rsi+rcx]
|
||||
mov r15, rdx
|
||||
randomx_isn_246:
|
||||
; IMUL_R r6, r1
|
||||
imul r14, r9
|
||||
randomx_isn_247:
|
||||
; FMUL_R e2, a1
|
||||
mulpd xmm6, xmm9
|
||||
randomx_isn_248:
|
||||
; IADD_M r1, L2[r3+1223504721]
|
||||
lea eax, [r11d+1223504721]
|
||||
and eax, 262136
|
||||
add r9, qword ptr [rsi+rax]
|
||||
randomx_isn_249:
|
||||
; FADD_R f1, a2
|
||||
addpd xmm1, xmm10
|
||||
randomx_isn_250:
|
||||
; IXOR_M r4, L1[r2-1447740505]
|
||||
lea eax, [r10d-1447740505]
|
||||
and eax, 16376
|
||||
xor r12, qword ptr [rsi+rax]
|
||||
randomx_isn_251:
|
||||
; IXOR_R r0, r5
|
||||
xor r8, r13
|
||||
randomx_isn_252:
|
||||
; CBRANCH r4, -1337905977, COND 4
|
||||
add r12, -1337903929
|
||||
test r12, 1044480
|
||||
jz randomx_isn_251
|
||||
randomx_isn_253:
|
||||
; FSUB_R f1, a1
|
||||
subpd xmm1, xmm9
|
||||
randomx_isn_254:
|
||||
; FMUL_R e0, a0
|
||||
mulpd xmm4, xmm8
|
||||
randomx_isn_255:
|
||||
; CBRANCH r5, 437071043, COND 11
|
||||
add r13, 436808899
|
||||
test r13, 133693440
|
||||
jz randomx_isn_253
|
||||
943
app/src/main/cpp/randomx/doc/specs.md
Normal file
943
app/src/main/cpp/randomx/doc/specs.md
Normal file
|
|
@ -0,0 +1,943 @@
|
|||
# RandomX
|
||||
|
||||
RandomX is a proof of work (PoW) algorithm which was designed to close the gap between general-purpose CPUs and specialized hardware. The core of the algorithm is a simulation of a virtual CPU.
|
||||
|
||||
#### Table of contents
|
||||
|
||||
1. [Definitions](#1-definitions)
|
||||
1. [Algorithm description](#2-algorithm-description)
|
||||
1. [Custom functions](#3-custom-functions)
|
||||
1. [Virtual Machine](#4-virtual-machine)
|
||||
1. [Instruction set](#5-instruction-set)
|
||||
1. [SuperscalarHash](#6-superscalarhash)
|
||||
1. [Dataset](#7-dataset)
|
||||
|
||||
|
||||
## 1. Definitions
|
||||
|
||||
### 1.1 General definitions
|
||||
|
||||
**Hash256** and **Hash512** refer to the [Blake2b](https://blake2.net/blake2_20130129.pdf) hashing function with a 256-bit and 512-bit output size, respectively.
|
||||
|
||||
**Floating point format** refers to the [IEEE-754 double precision floating point format](https://en.wikipedia.org/wiki/Double-precision_floating-point_format) with a sign bit, 11-bit exponent and 52-bit fraction.
|
||||
|
||||
**Argon2d** is a tradeoff-resistant variant of [Argon2](https://github.com/P-H-C/phc-winner-argon2/blob/master/argon2-specs.pdf), a memory-hard password derivation function.
|
||||
|
||||
**AesGenerator1R** refers to an AES-based pseudo-random number generator described in chapter 3.2. It's initialized with a 512-bit seed value and is capable of producing more than 10 bytes per clock cycle.
|
||||
|
||||
**AesGenerator4R** is a slower but more secure AES-based pseudo-random number generator described in chapter 3.3. It's initialized with a 512-bit seed value.
|
||||
|
||||
**AesHash1R** refers to an AES-based fingerprinting function described in chapter 3.4. It's capable of processing more than 10 bytes per clock cycle and produces a 512-bit output.
|
||||
|
||||
**BlakeGenerator** refers to a custom pseudo-random number generator described in chapter 3.5. It's based on the Blake2b hashing function.
|
||||
|
||||
**SuperscalarHash** refers to a custom diffusion function designed to run efficiently on superscalar CPUs (see chapter 7). It transforms a 64-byte input value into a 64-byte output value.
|
||||
|
||||
**Virtual Machine** or **VM** refers to the RandomX virtual machine as described in chapter 4.
|
||||
|
||||
**Programming the VM** refers to the act of loading a program and configuration into the VM. This is described in chapter 4.5.
|
||||
|
||||
**Executing the VM** refers to the act of running the program loop as described in chapter 4.6.
|
||||
|
||||
**Scratchpad** refers to the workspace memory of the VM. The whole scratchpad is structured into 3 levels: L3 -> L2 -> L1 with each lower level being a subset of the higher levels.
|
||||
|
||||
**Register File** refers to a 256-byte sequence formed by concatenating VM registers in little-endian format in the following order: `r0`-`r7`, `f0`-`f3`, `e0`-`e3` and `a0`-`a3`.
|
||||
|
||||
**Program Buffer** refers to the buffer from which the VM reads instructions.
|
||||
|
||||
**Cache** refers to a read-only buffer initialized by Argon2d as described in chapter 7.1.
|
||||
|
||||
**Dataset** refers to a large read-only buffer described in chapter 7. It is constructed from the Cache using the SuperscalarHash function.
|
||||
|
||||
### 1.2 Configurable parameters
|
||||
RandomX has several configurable parameters that are listed in Table 1.2.1 with their default values.
|
||||
|
||||
*Table 1.2.1 - Configurable parameters*
|
||||
|
||||
|parameter|description|default value|
|
||||
|---------|-----|-------|
|
||||
|`RANDOMX_ARGON_MEMORY`|The number of 1 KiB Argon2 blocks in the Cache| `262144`|
|
||||
|`RANDOMX_ARGON_ITERATIONS`|The number of Argon2d iterations for Cache initialization|`3`|
|
||||
|`RANDOMX_ARGON_LANES`|The number of parallel lanes for Cache initialization|`1`|
|
||||
|`RANDOMX_ARGON_SALT`|Argon2 salt|`"RandomX\x03"`|
|
||||
|`RANDOMX_CACHE_ACCESSES`|The number of random Cache accesses per Dataset item|`8`|
|
||||
|`RANDOMX_SUPERSCALAR_LATENCY`|Target latency for SuperscalarHash (in cycles of the reference CPU)|`170`|
|
||||
|`RANDOMX_DATASET_BASE_SIZE`|Dataset base size in bytes|`2147483648`|
|
||||
|`RANDOMX_DATASET_EXTRA_SIZE`|Dataset extra size in bytes|`33554368`|
|
||||
|`RANDOMX_PROGRAM_SIZE`|The number of instructions in a RandomX program|`256`|
|
||||
|`RANDOMX_PROGRAM_ITERATIONS`|The number of iterations per program|`2048`|
|
||||
|`RANDOMX_PROGRAM_COUNT`|The number of programs per hash|`8`|
|
||||
|`RANDOMX_JUMP_BITS`|Jump condition mask size in bits|`8`|
|
||||
|`RANDOMX_JUMP_OFFSET`|Jump condition mask offset in bits|`8`|
|
||||
|`RANDOMX_SCRATCHPAD_L3`|Scratchpad L3 size in bytes|`2097152`|
|
||||
|`RANDOMX_SCRATCHPAD_L2`|Scratchpad L2 size in bytes|`262144`|
|
||||
|`RANDOMX_SCRATCHPAD_L1`|Scratchpad L1 size in bytes|`16384`|
|
||||
|
||||
Instruction frequencies listed in Tables 5.2.1, 5.3.1, 5.4.1 and 5.5.1 are also configurable.
|
||||
|
||||
|
||||
## 2. Algorithm description
|
||||
|
||||
The RandomX algorithm accepts two input values:
|
||||
|
||||
* String `K` with a size of 0-60 bytes (key)
|
||||
* String `H` of arbitrary length (the value to be hashed)
|
||||
|
||||
and outputs a 256-bit result `R`.
|
||||
|
||||
The algorithm consists of the following steps:
|
||||
|
||||
1. The Dataset is initialized using the key value `K` (described in chapter 7).
|
||||
1. 64-byte seed `S` is calculated as `S = Hash512(H)`.
|
||||
1. Let `gen1 = AesGenerator1R(S)`.
|
||||
1. The Scratchpad is filled with `RANDOMX_SCRATCHPAD_L3` random bytes using generator `gen1`.
|
||||
1. Let `gen4 = AesGenerator4R(gen1.state)` (use the final state of `gen1`).
|
||||
1. The value of the VM register `fprc` is set to 0 (default rounding mode - chapter 4.3).
|
||||
1. The VM is programmed using `128 + 8 * RANDOMX_PROGRAM_SIZE` random bytes using generator `gen4` (chapter 4.5).
|
||||
1. The VM is executed (chapter 4.6).
|
||||
1. A new 64-byte seed is calculated as `S = Hash512(RegisterFile)`.
|
||||
1. Set `gen4.state = S` (modify the state of the generator).
|
||||
1. Steps 7-10 are performed a total of `RANDOMX_PROGRAM_COUNT` times. The last iteration skips steps 9 and 10.
|
||||
1. Scratchpad fingerprint is calculated as `A = AesHash1R(Scratchpad)`.
|
||||
1. Bytes 192-255 of the Register File are set to the value of `A`.
|
||||
1. Result is calculated as `R = Hash256(RegisterFile)`.
|
||||
|
||||
The input of the `Hash512` function in step 9 is the following 256 bytes:
|
||||
```
|
||||
+---------------------------------+
|
||||
| registers r0-r7 | (64 bytes)
|
||||
+---------------------------------+
|
||||
| registers f0-f3 | (64 bytes)
|
||||
+---------------------------------+
|
||||
| registers e0-e3 | (64 bytes)
|
||||
+---------------------------------+
|
||||
| registers a0-a3 | (64 bytes)
|
||||
+---------------------------------+
|
||||
```
|
||||
|
||||
The input of the `Hash256` function in step 14 is the following 256 bytes:
|
||||
```
|
||||
+---------------------------------+
|
||||
| registers r0-r7 | (64 bytes)
|
||||
+---------------------------------+
|
||||
| registers f0-f3 | (64 bytes)
|
||||
+---------------------------------+
|
||||
| registers e0-e3 | (64 bytes)
|
||||
+---------------------------------+
|
||||
| AesHash1R(Scratchpad) | (64 bytes)
|
||||
+---------------------------------+
|
||||
```
|
||||
|
||||
## 3 Custom functions
|
||||
|
||||
### 3.1 Definitions
|
||||
|
||||
Two of the custom functions are based on the [Advanced Encryption Standard](https://en.wikipedia.org/wiki/Advanced_Encryption_Standard) (AES).
|
||||
|
||||
**AES encryption round** refers to the application of the ShiftRows, SubBytes and MixColumns transformations followed by a XOR with the round key.
|
||||
|
||||
**AES decryption round** refers to the application of inverse ShiftRows, inverse SubBytes and inverse MixColumns transformations followed by a XOR with the round key.
|
||||
|
||||
### 3.2 AesGenerator1R
|
||||
|
||||
AesGenerator1R produces a sequence of pseudo-random bytes.
|
||||
|
||||
The internal state of the generator consists of 64 bytes arranged into four columns of 16 bytes each. During each output iteration, every column is decrypted (columns 0, 2) or encrypted (columns 1, 3) with one AES round using the following round keys (one key per column):
|
||||
|
||||
```
|
||||
key0 = 53 a5 ac 6d 09 66 71 62 2b 55 b5 db 17 49 f4 b4
|
||||
key1 = 07 af 7c 6d 0d 71 6a 84 78 d3 25 17 4e dc a1 0d
|
||||
key2 = f1 62 12 3f c6 7e 94 9f 4f 79 c0 f4 45 e3 20 3e
|
||||
key3 = 35 81 ef 6a 7c 31 ba b1 88 4c 31 16 54 91 16 49
|
||||
```
|
||||
These keys were generated as:
|
||||
```
|
||||
key0, key1, key2, key3 = Hash512("RandomX AesGenerator1R keys")
|
||||
```
|
||||
|
||||
|
||||
Single iteration produces 64 bytes of output which also become the new generator state.
|
||||
```
|
||||
state0 (16 B) state1 (16 B) state2 (16 B) state3 (16 B)
|
||||
| | | |
|
||||
AES decrypt AES encrypt AES decrypt AES encrypt
|
||||
(key0) (key1) (key2) (key3)
|
||||
| | | |
|
||||
v v v v
|
||||
state0' state1' state2' state3'
|
||||
```
|
||||
|
||||
### 3.3 AesGenerator4R
|
||||
|
||||
AesGenerator4R works similar way as AesGenerator1R, except it uses 4 rounds per column. Columns 0 and 1 use a different set of keys than columns 2 and 3.
|
||||
|
||||
```
|
||||
state0 (16 B) state1 (16 B) state2 (16 B) state3 (16 B)
|
||||
| | | |
|
||||
AES decrypt AES encrypt AES decrypt AES encrypt
|
||||
(key0) (key0) (key4) (key4)
|
||||
| | | |
|
||||
v v v v
|
||||
AES decrypt AES encrypt AES decrypt AES encrypt
|
||||
(key1) (key1) (key5) (key5)
|
||||
| | | |
|
||||
v v v v
|
||||
AES decrypt AES encrypt AES decrypt AES encrypt
|
||||
(key2) (key2) (key6) (key6)
|
||||
| | | |
|
||||
v v v v
|
||||
AES decrypt AES encrypt AES decrypt AES encrypt
|
||||
(key3) (key3) (key7) (key7)
|
||||
| | | |
|
||||
v v v v
|
||||
state0' state1' state2' state3'
|
||||
```
|
||||
|
||||
AesGenerator4R uses the following 8 round keys:
|
||||
|
||||
```
|
||||
key0 = dd aa 21 64 db 3d 83 d1 2b 6d 54 2f 3f d2 e5 99
|
||||
key1 = 50 34 0e b2 55 3f 91 b6 53 9d f7 06 e5 cd df a5
|
||||
key2 = 04 d9 3e 5c af 7b 5e 51 9f 67 a4 0a bf 02 1c 17
|
||||
key3 = 63 37 62 85 08 5d 8f e7 85 37 67 cd 91 d2 de d8
|
||||
key4 = 73 6f 82 b5 a6 a7 d6 e3 6d 8b 51 3d b4 ff 9e 22
|
||||
key5 = f3 6b 56 c7 d9 b3 10 9c 4e 4d 02 e9 d2 b7 72 b2
|
||||
key6 = e7 c9 73 f2 8b a3 65 f7 0a 66 a9 2b a7 ef 3b f6
|
||||
key7 = 09 d6 7c 7a de 39 58 91 fd d1 06 0c 2d 76 b0 c0
|
||||
```
|
||||
These keys were generated as:
|
||||
```
|
||||
key0, key1, key2, key3 = Hash512("RandomX AesGenerator4R keys 0-3")
|
||||
key4, key5, key6, key7 = Hash512("RandomX AesGenerator4R keys 4-7")
|
||||
```
|
||||
|
||||
### 3.4 AesHash1R
|
||||
|
||||
AesHash1R calculates a 512-bit fingerprint of its input.
|
||||
|
||||
AesHash1R has a 64-byte internal state, which is arranged into four columns of 16 bytes each. The initial state is:
|
||||
|
||||
```
|
||||
state0 = 0d 2c b5 92 de 56 a8 9f 47 db 82 cc ad 3a 98 d7
|
||||
state1 = 6e 99 8d 33 98 b7 c7 15 5a 12 9e f5 57 80 e7 ac
|
||||
state2 = 17 00 77 6a d0 c7 62 ae 6b 50 79 50 e4 7c a0 e8
|
||||
state3 = 0c 24 0a 63 8d 82 ad 07 05 00 a1 79 48 49 99 7e
|
||||
```
|
||||
|
||||
The initial state vectors were generated as:
|
||||
```
|
||||
state0, state1, state2, state3 = Hash512("RandomX AesHash1R state")
|
||||
```
|
||||
|
||||
The input is processed in 64-byte blocks. Each input block is considered to be a set of four AES round keys `key0`, `key1`, `key2`, `key3`. Each state column is encrypted (columns 0, 2) or decrypted (columns 1, 3) with one AES round using the corresponding round key:
|
||||
|
||||
```
|
||||
state0 (16 B) state1 (16 B) state2 (16 B) state3 (16 B)
|
||||
| | | |
|
||||
AES encrypt AES decrypt AES encrypt AES decrypt
|
||||
(key0) (key1) (key2) (key3)
|
||||
| | | |
|
||||
v v v v
|
||||
state0' state1' state2' state3'
|
||||
```
|
||||
|
||||
When all input bytes have been processed, the state is processed with two additional AES rounds with the following extra keys (one key per round, same pair of keys for all columns):
|
||||
|
||||
```
|
||||
xkey0 = 89 83 fa f6 9f 94 24 8b bf 56 dc 90 01 02 89 06
|
||||
xkey1 = d1 63 b2 61 3c e0 f4 51 c6 43 10 ee 9b f9 18 ed
|
||||
```
|
||||
|
||||
The extra keys were generated as:
|
||||
```
|
||||
xkey0, xkey1 = Hash256("RandomX AesHash1R xkeys")
|
||||
```
|
||||
|
||||
```
|
||||
state0 (16 B) state1 (16 B) state2 (16 B) state3 (16 B)
|
||||
| | | |
|
||||
AES encrypt AES decrypt AES encrypt AES decrypt
|
||||
(xkey0) (xkey0) (xkey0) (xkey0)
|
||||
| | | |
|
||||
v v v v
|
||||
AES encrypt AES decrypt AES encrypt AES decrypt
|
||||
(xkey1) (xkey1) (xkey1) (xkey1)
|
||||
| | | |
|
||||
v v v v
|
||||
finalState0 finalState1 finalState2 finalState3
|
||||
```
|
||||
|
||||
The final state is the output of the function.
|
||||
|
||||
### 3.5 BlakeGenerator
|
||||
|
||||
BlakeGenerator is a simple pseudo-random number generator based on the Blake2b hashing function. It has a 64-byte internal state `S`.
|
||||
|
||||
#### 3.5.1 Initialization
|
||||
|
||||
The internal state is initialized from a seed value `K` (0-60 bytes long). The seed value is written into the internal state and padded with zeroes. Then the internal state is initialized as `S = Hash512(S)`.
|
||||
|
||||
#### 3.5.2 Random number generation
|
||||
|
||||
The generator can generate 1 byte or 4 bytes at a time by supplying data from its internal state `S`. If there are not enough unused bytes left, the internal state is reinitialized as `S = Hash512(S)`.
|
||||
|
||||
## 4. Virtual Machine
|
||||
|
||||
The components of the RandomX virtual machine are summarized in Fig. 4.1.
|
||||
|
||||
*Figure 4.1 - Virtual Machine*
|
||||
|
||||

|
||||
|
||||
The VM is a complex instruction set computer ([CISC](https://en.wikipedia.org/wiki/Complex_instruction_set_computer)). All data are loaded and stored in little-endian byte order. Signed integer numbers are represented using [two's complement](https://en.wikipedia.org/wiki/Two%27s_complement).
|
||||
|
||||
### 4.1 Dataset
|
||||
|
||||
Dataset is described in detail in chapter 7. It's a large read-only buffer. Its size is equal to `RANDOMX_DATASET_BASE_SIZE + RANDOMX_DATASET_EXTRA_SIZE` bytes. Each program uses only a random subset of the Dataset of size `RANDOMX_DATASET_BASE_SIZE`. All Dataset accesses read an aligned 64-byte item.
|
||||
|
||||
### 4.2 Scratchpad
|
||||
|
||||
Scratchpad represents the workspace memory of the VM. Its size is `RANDOMX_SCRATCHPAD_L3` bytes and it's divided into 3 "levels":
|
||||
|
||||
* The whole scratchpad is the third level "L3".
|
||||
* The first `RANDOMX_SCRATCHPAD_L2` bytes of the scratchpad is the second level "L2".
|
||||
* The first `RANDOMX_SCRATCHPAD_L1` bytes of the scratchpad is the first level "L1".
|
||||
|
||||
The scratchpad levels are inclusive, i.e. L3 contains both L2 and L1 and L2 contains L1.
|
||||
|
||||
To access a particular scratchpad level, bitwise AND with a mask according to table 4.2.1 is applied to the memory address.
|
||||
|
||||
*Table 4.2.1: Scratchpad access masks*
|
||||
|
||||
|Level|8-byte aligned mask|64-byte aligned mask|
|
||||
|---------|-|-|
|
||||
|L1|`(RANDOMX_SCRATCHPAD_L1 - 1) & ~7`|-|
|
||||
|L2|`(RANDOMX_SCRATCHPAD_L2 - 1) & ~7`|-|
|
||||
|L3|`(RANDOMX_SCRATCHPAD_L3 - 1) & ~7`|`(RANDOMX_SCRATCHPAD_L3 - 1) & ~63`|
|
||||
|
||||
### 4.3 Registers
|
||||
|
||||
The VM has 8 integer registers `r0`-`r7` (group R) and a total of 12 floating point registers split into 3 groups: `f0`-`f3` (group F), `e0`-`e3` (group E) and `a0`-`a3` (group A). Integer registers are 64 bits wide, while floating point registers are 128 bits wide and contain a pair of numbers in floating point format. The lower and upper half of floating point registers are not separately addressable.
|
||||
|
||||
Additionally, there are 3 internal registers `ma`, `mx` and `fprc`.
|
||||
|
||||
Integer registers `r0`-`r7` can be the source or the destination operands of integer instructions or may be used as address registers for accessing the Scratchpad.
|
||||
|
||||
Floating point registers `a0`-`a3` are read-only and their value is fixed for a given VM program. They can be the source operand of any floating point instruction. The value of these registers is restricted to the interval `[1, 4294967296)`.
|
||||
|
||||
Floating point registers `f0`-`f3` are the "additive" registers, which can be the destination of floating point addition and subtraction instructions. The absolute value of these registers will not exceed about `3.0e+14`.
|
||||
|
||||
Floating point registers `e0`-`e3` are the "multiplicative" registers, which can be the destination of floating point multiplication, division and square root instructions. Their value is always positive.
|
||||
|
||||
`ma` and `mx` are the memory registers. Both are 32 bits wide. `ma` contains the memory address of the next Dataset read and `mx` contains the address of the next Dataset prefetch. The values of `ma` and `mx` registers are always aligned to be a multiple of 64.
|
||||
|
||||
The 2-bit `fprc` register determines the rounding mode of all floating point operations according to Table 4.3.1. The four rounding modes are defined by the IEEE 754 standard.
|
||||
|
||||
*Table 4.3.1: Rounding modes*
|
||||
|
||||
|`fprc`|rounding mode|
|
||||
|-------|------------|
|
||||
|0|roundTiesToEven|
|
||||
|1|roundTowardNegative|
|
||||
|2|roundTowardPositive|
|
||||
|3|roundTowardZero|
|
||||
|
||||
#### 4.3.1 Group F register conversion
|
||||
|
||||
When an 8-byte value read from the memory is to be converted to an F group register value or operand, it is interpreted as a pair of 32-bit signed integers (in little endian, two's complement format) and converted to floating point format. This conversion is exact and doesn't need rounding because only 30 bits of the fraction significand are needed to represent the integer value.
|
||||
|
||||
#### 4.3.2 Group E register conversion
|
||||
|
||||
When an 8-byte value read from the memory is to be converted to an E group register value or operand, the same conversion procedure is applied as for F group registers (see 4.3.1) with additional post-processing steps for each of the two floating point values:
|
||||
|
||||
1. The sign bit is set to `0`.
|
||||
2. Bits 0-2 of the exponent are set to the constant value of <code>011<sub>2</sub></code>.
|
||||
3. Bits 3-6 of the exponent are set to the value of the exponent mask described in chapter 4.5.6. This value is fixed for a given VM program.
|
||||
4. The bottom 22 bits of the fraction significand are set to the value of the fraction mask described in chapter 4.5.6. This value is fixed for a given VM program.
|
||||
|
||||
### 4.4 Program buffer
|
||||
|
||||
The Program buffer stores the program to be executed by the VM. The program consists of `RANDOMX_PROGRAM_SIZE` instructions. Each instruction is encoded by an 8-byte word. The instruction set is described in chapter 5.
|
||||
|
||||
### 4.5 VM programming
|
||||
|
||||
The VM requires `128 + 8 * RANDOMX_PROGRAM_SIZE` bytes to be programmed. This is split into two parts:
|
||||
|
||||
* `128` bytes of configuration data = 16 quadwords (16×8 bytes), used according to Table 4.5.1
|
||||
* `8 * RANDOMX_PROGRAM_SIZE` bytes of program data, copied directly into the Program Buffer
|
||||
|
||||
*Table 4.5.1 - Configuration data*
|
||||
|
||||
|quadword|description|
|
||||
|-----|-----------|
|
||||
|0|initialize low half of register `a0`|
|
||||
|1|initialize high half of register `a0`|
|
||||
|2|initialize low half of register `a1`|
|
||||
|3|initialize high half of register `a1`|
|
||||
|4|initialize low half of register `a2`|
|
||||
|5|initialize high half of register `a2`|
|
||||
|6|initialize low half of register `a3`|
|
||||
|7|initialize high half of register `a3`|
|
||||
|8|initialize register `ma`|
|
||||
|9|(reserved)|
|
||||
|10|initialize register `mx`|
|
||||
|11|(reserved)|
|
||||
|12|select address registers|
|
||||
|13|select Dataset offset|
|
||||
|14|initialize register masks for low half of group E registers|
|
||||
|15|initialize register masks for high half of group E registers|
|
||||
|
||||
#### 4.5.2 Group A register initialization
|
||||
|
||||
The values of the floating point registers `a0`-`a3` are initialized using configuration quadwords 0-7 to have the following value:
|
||||
|
||||
<code>+1.fraction x 2<sup>exponent</sup></code>
|
||||
|
||||
The fraction has full 52 bits of precision and the exponent value ranges from 0 to 31. These values are obtained from the initialization quadword (in little endian format) according to Table 4.5.2.
|
||||
|
||||
*Table 4.5.2 - Group A register initialization*
|
||||
|
||||
|bits|description|
|
||||
|----|-----------|
|
||||
|0-51|fraction|
|
||||
|52-58|(reserved)|
|
||||
|59-63|exponent|
|
||||
|
||||
#### 4.5.3 Memory registers
|
||||
|
||||
Registers `ma` and `mx` are initialized using the low 32 bits of quadwords 8 and 10 in little endian format.
|
||||
|
||||
#### 4.5.4 Address registers
|
||||
|
||||
Bits 0-3 of quadword 12 are used to select 4 address registers for program execution. Each bit chooses one register from a pair of integer registers according to Table 4.5.3.
|
||||
|
||||
*Table 4.5.3 - Address registers*
|
||||
|
||||
|address register (bit)|value = 0|value = 1|
|
||||
|----------------------|-|-|
|
||||
|`readReg0` (0)|`r0`|`r1`|
|
||||
|`readReg1` (1)|`r2`|`r3`|
|
||||
|`readReg2` (2)|`r4`|`r5`|
|
||||
|`readReg3` (3)|`r6`|`r7`|
|
||||
|
||||
#### 4.5.5 Dataset offset
|
||||
|
||||
The `datasetOffset` is calculated as the remainder of dividing quadword 13 by `RANDOMX_DATASET_EXTRA_SIZE / 64 + 1`. The result is multiplied by `64`. This offset is used when reading values from the Dataset.
|
||||
|
||||
#### 4.5.6 Group E register masks
|
||||
|
||||
These masks are used for the conversion of group E registers (see 4.3.2). The low and high halves each have their own masks initialized from quadwords 14 and 15. The fraction mask is given by bits 0-21 and the exponent mask by bits 60-63 of the initialization quadword.
|
||||
|
||||
### 4.6 VM execution
|
||||
|
||||
During VM execution, 3 additional temporary registers are used: `ic`, `spAddr0` and `spAddr1`. Program execution consists of initialization and loop execution.
|
||||
|
||||
#### 4.6.1 Initialization
|
||||
|
||||
1. `ic` register is set to `RANDOMX_PROGRAM_ITERATIONS`.
|
||||
2. `spAddr0` is set to the value of `mx`.
|
||||
3. `spAddr1` is set to the value of `ma`.
|
||||
4. The values of all integer registers `r0`-`r7` are set to zero.
|
||||
|
||||
#### 4.6.2 Loop execution
|
||||
|
||||
The loop described below is repeated until the value of the `ic` register reaches zero.
|
||||
|
||||
1. XOR of registers `readReg0` and `readReg1` (see Table 4.5.3) is calculated and `spAddr0` is XORed with the low 32 bits of the result and `spAddr1` with the high 32 bits.
|
||||
2. `spAddr0` is used to perform a 64-byte aligned read from Scratchpad level 3 (using mask from Table 4.2.1). The 64 bytes are XORed with all integer registers in order `r0`-`r7`.
|
||||
3. `spAddr1` is used to perform a 64-byte aligned read from Scratchpad level 3 (using mask from Table 4.2.1). Each floating point register `f0`-`f3` and `e0`-`e3` is initialized using an 8-byte value according to the conversion rules from chapters 4.3.1 and 4.3.2.
|
||||
4. The 256 instructions stored in the Program Buffer are executed.
|
||||
5. The `mx` register is XORed with the low 32 bits of registers `readReg2` and `readReg3` (see Table 4.5.3).
|
||||
6. A 64-byte Dataset item at address `datasetOffset + mx % RANDOMX_DATASET_BASE_SIZE` is prefetched from the Dataset (it will be used during the next iteration).
|
||||
7. A 64-byte Dataset item at address `datasetOffset + ma % RANDOMX_DATASET_BASE_SIZE` is loaded from the Dataset. The 64 bytes are XORed with all integer registers in order `r0`-`r7`.
|
||||
8. The values of registers `mx` and `ma` are swapped.
|
||||
9. The values of all integer registers `r0`-`r7` are written to the Scratchpad (L3) at address `spAddr1` (64-byte aligned).
|
||||
10. Register `f0` is XORed with register `e0` and the result is stored in register `f0`. Register `f1` is XORed with register `e1` and the result is stored in register `f1`. Register `f2` is XORed with register `e2` and the result is stored in register `f2`. Register `f3` is XORed with register `e3` and the result is stored in register `f3`.
|
||||
11. The values of registers `f0`-`f3` are written to the Scratchpad (L3) at address `spAddr0` (64-byte aligned).
|
||||
12. `spAddr0` and `spAddr1` are both set to zero.
|
||||
13. `ic` is decreased by 1.
|
||||
|
||||
|
||||
## 5. Instruction set
|
||||
|
||||
The VM executes programs in a special instruction set, which was designed in such way that any random 8-byte word is a valid instruction and any sequence of valid instructions is a valid program. Because there are no "syntax" rules, generating a random program is as easy as filling the program buffer with random data.
|
||||
|
||||
### 5.1 Instruction encoding
|
||||
|
||||
Each instruction word is 64 bits long. Instruction fields are encoded as shown in Fig. 5.1.
|
||||
|
||||
*Figure 5.1 - Instruction encoding*
|
||||
|
||||

|
||||
|
||||
#### 5.1.1 opcode
|
||||
There are 256 opcodes, which are distributed between 29 distinct instructions. Each instruction can be encoded using multiple opcodes (the number of opcodes specifies the frequency of the instruction in a random program).
|
||||
|
||||
*Table 5.1.1: Instruction groups*
|
||||
|
||||
|group|# instructions|# opcodes||
|
||||
|---------|-----------------|----|-|
|
||||
|integer |17|120|46.9%|
|
||||
|floating point |9|94|36.7%|
|
||||
|control |2|26|10.2%|
|
||||
|store |1|16|6.2%|
|
||||
||**29**|**256**|**100%**
|
||||
|
||||
All instructions are described below in chapters 5.2 - 5.5.
|
||||
|
||||
#### 5.1.2 dst
|
||||
Destination register. Only bits 0-1 (register groups A, F, E) or 0-2 (groups R, F+E) are used to encode a register according to Table 5.1.2.
|
||||
|
||||
*Table 5.1.2: Addressable register groups*
|
||||
|
||||
|index|R|A|F|E|F+E|
|
||||
|--|--|--|--|--|--|
|
||||
|0|`r0`|`a0`|`f0`|`e0`|`f0`|
|
||||
|1|`r1`|`a1`|`f1`|`e1`|`f1`|
|
||||
|2|`r2`|`a2`|`f2`|`e2`|`f2`|
|
||||
|3|`r3`|`a3`|`f3`|`e3`|`f3`|
|
||||
|4|`r4`||||`e0`|
|
||||
|5|`r5`||||`e1`|
|
||||
|6|`r6`||||`e2`|
|
||||
|7|`r7`||||`e3`|
|
||||
|
||||
#### 5.1.3 src
|
||||
|
||||
The `src` flag encodes a source operand register according to Table 5.1.2 (only bits 0-1 or 0-2 are used).
|
||||
|
||||
Some integer instructions use a constant value as the source operand in cases when `dst` and `src` encode the same register (see Table 5.2.1).
|
||||
|
||||
For register-memory instructions, the source operand is used to calculate the memory address.
|
||||
|
||||
#### 5.1.4 mod
|
||||
|
||||
The `mod` flag is encoded as:
|
||||
|
||||
*Table 5.1.3: mod flag encoding*
|
||||
|
||||
|`mod` bits|description|range of values|
|
||||
|----|--------|----|
|
||||
|0-1|`mod.mem` flag|0-3|
|
||||
|2-3|`mod.shift` flag|0-3|
|
||||
|4-7|`mod.cond` flag|0-15|
|
||||
|
||||
The `mod.mem` flag selects between Scratchpad levels L1 and L2 when reading from or writing to memory except for two cases:
|
||||
|
||||
* it's a memory read and `dst` and `src` encode the same register
|
||||
* it's a memory write `mod.cond` is 14 or 15
|
||||
|
||||
In these two cases, the Scratchpad level is L3 (see Table 5.1.4).
|
||||
|
||||
*Table 5.1.4: memory access Scratchpad level*
|
||||
|
||||
|condition|Scratchpad level|
|
||||
|---------|-|
|
||||
|`src == dst` (read)|L3|
|
||||
|`mod.cond >= 14` (write)|L3|
|
||||
|`mod.mem == 0`|L2|
|
||||
|`mod.mem != 0`|L1|
|
||||
|
||||
The address for reading/writing is calculated by applying bitwise AND operation to the address and the 8-byte aligned address mask listed in Table 4.2.1.
|
||||
|
||||
The `mod.cond` and `mod.shift` flags are used by some instructions (see 5.2, 5.4).
|
||||
|
||||
#### 5.1.5 imm32
|
||||
A 32-bit immediate value that can be used as the source operand and is used to calculate addresses for memory operations. The immediate value is sign-extended to 64 bits unless specified otherwise.
|
||||
|
||||
### 5.2 Integer instructions
|
||||
For integer instructions, the destination is always an integer register (register group R). Source operand (if applicable) can be either an integer register or memory value. If `dst` and `src` refer to the same register, most instructions use `0` or `imm32` instead of the register. This is indicated in the 'src == dst' column in Table 5.2.1.
|
||||
|
||||
`[mem]` indicates a memory operand loaded as an 8-byte value from the address `src + imm32`.
|
||||
|
||||
*Table 5.2.1 Integer instructions*
|
||||
|
||||
|frequency|instruction|dst|src|`src == dst ?`|operation|
|
||||
|-|-|-|-|-|-|
|
||||
|16/256|IADD_RS|R|R|`src = dst`|`dst = dst + (src << mod.shift) (+ imm32)`|
|
||||
|7/256|IADD_M|R|R|`src = 0`|`dst = dst + [mem]`|
|
||||
|16/256|ISUB_R|R|R|`src = imm32`|`dst = dst - src`|
|
||||
|7/256|ISUB_M|R|R|`src = 0`|`dst = dst - [mem]`|
|
||||
|16/256|IMUL_R|R|R|`src = imm32`|`dst = dst * src`|
|
||||
|4/256|IMUL_M|R|R|`src = 0`|`dst = dst * [mem]`|
|
||||
|4/256|IMULH_R|R|R|`src = dst`|`dst = (dst * src) >> 64`|
|
||||
|1/256|IMULH_M|R|R|`src = 0`|`dst = (dst * [mem]) >> 64`|
|
||||
|4/256|ISMULH_R|R|R|`src = dst`|`dst = (dst * src) >> 64` (signed)|
|
||||
|1/256|ISMULH_M|R|R|`src = 0`|`dst = (dst * [mem]) >> 64` (signed)|
|
||||
|8/256|IMUL_RCP|R|-|-|<code>dst = 2<sup>x</sup> / imm32 * dst</code>|
|
||||
|2/256|INEG_R|R|-|-|`dst = -dst`|
|
||||
|15/256|IXOR_R|R|R|`src = imm32`|`dst = dst ^ src`|
|
||||
|5/256|IXOR_M|R|R|`src = 0`|`dst = dst ^ [mem]`|
|
||||
|8/256|IROR_R|R|R|`src = imm32`|`dst = dst >>> src`|
|
||||
|2/256|IROL_R|R|R|`src = imm32`|`dst = dst <<< src`|
|
||||
|4/256|ISWAP_R|R|R|`src = dst`|`temp = src; src = dst; dst = temp`|
|
||||
|
||||
#### 5.2.1 IADD_RS
|
||||
|
||||
This instructions adds the values of two registers (modulo 2<sup>64</sup>). The value of the second operand is shifted left by 0-3 bits (determined by the `mod.shift` flag). Additionally, if `dst` is register `r5`, the immediate value `imm32` is added to the result.
|
||||
|
||||
#### 5.2.2 IADD_M
|
||||
|
||||
64-bit integer addition operation (performed modulo 2<sup>64</sup>) with a memory source operand.
|
||||
|
||||
#### 5.2.3 ISUB_R, ISUB_M
|
||||
|
||||
64-bit integer subtraction (performed modulo 2<sup>64</sup>). ISUB_R uses register source operand, ISUB_M uses a memory source operand.
|
||||
|
||||
#### 5.2.4 IMUL_R, IMUL_M
|
||||
|
||||
64-bit integer multiplication (performed modulo 2<sup>64</sup>). IMUL_R uses a register source operand, IMUL_M uses a memory source operand.
|
||||
|
||||
#### 5.2.5 IMULH_R, IMULH_M, ISMULH_R, ISMULH_M
|
||||
These instructions output the high 64 bits of the whole 128-bit multiplication result. The result differs for signed and unsigned multiplication (IMULH is unsigned, ISMULH is signed). The variants with a register source operand perform a squaring operation if `dst` equals `src`.
|
||||
|
||||
#### 5.2.6 IMUL_RCP
|
||||
If `imm32` equals 0 or is a power of 2, IMUL_RCP is a no-op. In other cases, the instruction multiplies the destination register by a reciprocal of `imm32` (the immediate value is zero-extended and treated as unsigned). The reciprocal is calculated as <code>rcp = 2<sup>x</sup> / imm32</code> by choosing the largest integer `x` such that <code>rcp < 2<sup>64</sup></code>.
|
||||
|
||||
#### 5.2.7 INEG_R
|
||||
Performs two's complement negation of the destination register.
|
||||
|
||||
#### 5.2.8 IXOR_R, IXOR_M
|
||||
64-bit exclusive OR operation. IXOR_R uses a register source operand, IXOR_M uses a memory source operand.
|
||||
|
||||
#### 5.2.9 IROR_R, IROL_R
|
||||
Performs a cyclic shift (rotation) of the destination register. Source operand (shift count) is implicitly masked to 6 bits. IROR rotates bits right, IROL left.
|
||||
|
||||
#### 5.2.9 ISWAP_R
|
||||
This instruction swaps the values of two registers. If source and destination refer to the same register, the result is a no-op.
|
||||
|
||||
### 5.3 Floating point instructions
|
||||
For floating point instructions, the destination can be a group F or group E register. Source operand is either a group A register or a memory value.
|
||||
|
||||
`[mem]` indicates a memory operand loaded as an 8-byte value from the address `src + imm32` and converted according to the rules in chapters 4.3.1 (group F) or 4.3.2 (group E). The lower and upper memory operands are denoted as `[mem][0]` and `[mem][1]`.
|
||||
|
||||
All floating point operations are rounded according to the current value of the `fprc` register (see Table 4.3.1). Due to restrictions on the values of the floating point registers, no operation results in `NaN` or a denormal number.
|
||||
|
||||
*Table 5.3.1 Floating point instructions*
|
||||
|
||||
|frequency|instruction|dst|src|operation|
|
||||
|-|-|-|-|-|
|
||||
|4/256|FSWAP_R|F+E|-|`(dst0, dst1) = (dst1, dst0)`|
|
||||
|16/256|FADD_R|F|A|`(dst0, dst1) = (dst0 + src0, dst1 + src1)`|
|
||||
|5/256|FADD_M|F|R|`(dst0, dst1) = (dst0 + [mem][0], dst1 + [mem][1])`|
|
||||
|16/256|FSUB_R|F|A|`(dst0, dst1) = (dst0 - src0, dst1 - src1)`|
|
||||
|5/256|FSUB_M|F|R|`(dst0, dst1) = (dst0 - [mem][0], dst1 - [mem][1])`|
|
||||
|6/256|FSCAL_R|F|-|<code>(dst0, dst1) = (-2<sup>x0</sup> * dst0, -2<sup>x1</sup> * dst1)</code>|
|
||||
|32/256|FMUL_R|E|A|`(dst0, dst1) = (dst0 * src0, dst1 * src1)`|
|
||||
|4/256|FDIV_M|E|R|`(dst0, dst1) = (dst0 / [mem][0], dst1 / [mem][1])`|
|
||||
|6/256|FSQRT_R|E|-|`(dst0, dst1) = (√dst0, √dst1)`|
|
||||
|
||||
#### 5.3.1 FSWAP_R
|
||||
|
||||
Swaps the lower and upper halves of the destination register. This is the only instruction that is applicable to both F an E register groups.
|
||||
|
||||
#### 5.3.2 FADD_R, FADD_M
|
||||
|
||||
Double precision floating point addition. FADD_R uses a group A register source operand, FADD_M uses a memory operand.
|
||||
|
||||
#### 5.3.3 FSUB_R, FSUB_M
|
||||
|
||||
Double precision floating point subtraction. FSUB_R uses a group A register source operand, FSUB_M uses a memory operand.
|
||||
|
||||
#### 5.3.4 FSCAL_R
|
||||
This instruction negates the number and multiplies it by <code>2<sup>x</sup></code>. `x` is calculated by taking the 4 least significant digits of the biased exponent and interpreting them as a binary number using the digit set `{+1, -1}` as opposed to the traditional `{0, 1}`. The possible values of `x` are all odd numbers from -15 to +15.
|
||||
|
||||
The mathematical operation described above is equivalent to a bitwise XOR of the binary representation with the value of `0x80F0000000000000`.
|
||||
|
||||
#### 5.3.5 FMUL_R
|
||||
|
||||
Double precision floating point multiplication. This instruction uses only a register source operand.
|
||||
|
||||
#### 5.3.6 FDIV_M
|
||||
|
||||
Double precision floating point division. This instruction uses only a memory source operand.
|
||||
|
||||
#### 5.3.7 FSQRT_R
|
||||
|
||||
Double precision floating point square root of the destination register.
|
||||
|
||||
### 5.4 Control instructions
|
||||
|
||||
There are 2 control instructions.
|
||||
|
||||
*Table 5.4.1 - Control instructions*
|
||||
|
||||
|frequency|instruction|dst|src|operation|
|
||||
|-|-|-|-|-|
|
||||
|1/256|CFROUND|-|R|`fprc = src >>> imm32`
|
||||
|25/256|CBRANCH|R|-|`dst = dst + cimm`, conditional jump
|
||||
|
||||
#### 5.4.1 CFROUND
|
||||
This instruction calculates a 2-bit value by rotating the source register right by `imm32` bits and taking the 2 least significant bits (the value of the source register is unaffected). The result is stored in the `fprc` register. This changes the rounding mode of all subsequent floating point instructions.
|
||||
|
||||
#### 5.4.2 CBRANCH
|
||||
|
||||
This instruction adds an immediate value `cimm` (constructed from `imm32`, see below) to the destination register and then performs a conditional jump in the Program Buffer based on the value of the destination register. The target of the jump is the instruction following the instruction when register `dst` was last modified.
|
||||
|
||||
At the beginning of each program iteration, all registers are considered to be unmodified. A register is considered as modified by an instruction in the following cases:
|
||||
|
||||
* It is the destination register of an integer instruction except IMUL_RCP and ISWAP_R.
|
||||
* It is the destination register of IMUL_RCP and `imm32` is not zero or a power of 2.
|
||||
* It is the source or the destination register of ISWAP_R and the destination and source registers are distinct.
|
||||
* The CBRANCH instruction is considered to modify all integer registers.
|
||||
|
||||
If register `dst` has not been modified yet, the jump target is the first instruction in the Program Buffer.
|
||||
|
||||
The CBRANCH instruction performs the following steps:
|
||||
|
||||
1. A constant `b` is calculated as `mod.cond + RANDOMX_JUMP_OFFSET`.
|
||||
1. A constant `cimm` is constructed as sign-extended `imm32` with bit `b` set to 1 and bit `b-1` set to 0 (if `b > 0`).
|
||||
1. `cimm` is added to the destination register.
|
||||
1. If bits `b` to `b + RANDOMX_JUMP_BITS - 1` of the destination register are zero, the jump is executed (target is the instruction following the instruction where `dst` was last modified).
|
||||
|
||||
Bits in immediate and register values are numbered from 0 to 63 with 0 being the least significant bit. For example, for `b = 10` and `RANDOMX_JUMP_BITS = 8`, the bits are arranged like this:
|
||||
|
||||
```
|
||||
cimm = SSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSMMMMMMMMMMMMMMMMMMMMM10MMMMMMMMM
|
||||
dst = ..............................................XXXXXXXX..........
|
||||
```
|
||||
|
||||
`S` is a copied sign bit from `imm32`. `M` denotes bits of `imm32`. The 9th bit is set to 0 and the 10th bit is set to 1. This value will be added to `dst`.
|
||||
|
||||
The second line uses `X` to mark bits of `dst` that will be checked by the condition. If all these bits are 0 after adding `cimm`, the jump is executed.
|
||||
|
||||
The construction of the CBRANCH instruction ensures that no inifinite loops are possible in the program.
|
||||
|
||||
### 5.5 Store instruction
|
||||
There is one explicit store instruction for integer values.
|
||||
|
||||
`[mem]` indicates the destination is an 8-byte value at the address `dst + imm32`.
|
||||
|
||||
*Table 5.5.1 - Store instruction*
|
||||
|
||||
|frequency|instruction|dst|src|operation|
|
||||
|-|-|-|-|-|
|
||||
|16/256|ISTORE|R|R|`[mem] = src`
|
||||
|
||||
#### 5.5.1 ISTORE
|
||||
This instruction stores the value of the source integer register to the memory at the address calculated from the value of the destination register. The `src` and `dst` can be the same register.
|
||||
|
||||
## 6. SuperscalarHash
|
||||
|
||||
SuperscalarHash is a custom diffusion function that was designed to burn as much power as possible using only the CPU's integer ALUs.
|
||||
|
||||
The input and output of SuperscalarHash are 8 integer registers `r0`-`r7`, each 64 bits wide. The output of SuperscalarHash is used to construct the Dataset (see chapter 7.3).
|
||||
|
||||
### 6.1 Instructions
|
||||
The body of SuperscalarHash is a random sequence of instructions that can run on the Virtual Machine. SuperscalarHash uses a reduced set of only integer register-register instructions listed in Table 6.1.1. `dst` refers to the destination register, `src` to the source register.
|
||||
|
||||
*Table 6.1.1 - SuperscalarHash instructions*
|
||||
|
||||
|freq. †|instruction|Macro-ops|operation|rules|
|
||||
|-|-|-|-|-|
|
||||
|0.11|ISUB_R|`sub_rr`|`dst = dst - src`|`dst != src`|
|
||||
|0.11|IXOR_R|`xor_rr`|`dst = dst ^ src`|`dst != src`|
|
||||
|0.11|IADD_RS|`lea_sib`|`dst = dst + (src << mod.shift)`|`dst != src`, `dst != r5`
|
||||
|0.22|IMUL_R|`imul_rr`|`dst = dst * src`|`dst != src`|
|
||||
|0.11|IROR_C|`ror_ri`|`dst = dst >>> imm32`|`imm32 % 64 != 0`
|
||||
|0.10|IADD_C|`add_ri`|`dst = dst + imm32`|
|
||||
|0.10|IXOR_C|`xor_ri`|`dst = dst ^ imm32`|
|
||||
|0.03|IMULH_R|`mov_rr`,`mul_r`,`mov_rr`|`dst = (dst * src) >> 64`|
|
||||
|0.03|ISMULH_R|`mov_rr`,`imul_r`,`mov_rr`|`dst = (dst * src) >> 64` (signed)|
|
||||
|0.06|IMUL_RCP|`mov_ri`,`imul_rr`|<code>dst = 2<sup>x</sup> / imm32 * dst</code>|`imm32 != 0`, <code>imm32 != 2<sup>N</sup></code>|
|
||||
|
||||
† Frequencies are approximate. Instructions are generated based on complex rules.
|
||||
|
||||
#### 6.1.1 ISUB_R
|
||||
See chapter 5.2.3. Source and destination are always distinct registers.
|
||||
|
||||
#### 6.1.2 IXOR_R
|
||||
See chapter 5.2.8. Source and destination are always distinct registers.
|
||||
|
||||
#### 6.1.3 IADD_RS
|
||||
See chapter 5.2.1. Source and destination are always distinct registers and register `r5` cannot be the destination.
|
||||
|
||||
#### 6.1.4 IMUL_R
|
||||
See chapter 5.2.4. Source and destination are always distinct registers.
|
||||
|
||||
#### 6.1.5 IROR_C
|
||||
The destination register is rotated right. The rotation count is given by `imm32` masked to 6 bits and cannot be 0.
|
||||
|
||||
#### 6.1.6 IADD_C
|
||||
A sign-extended `imm32` is added to the destination register.
|
||||
|
||||
#### 6.1.7 IXOR_C
|
||||
The destination register is XORed with a sign-extended `imm32`.
|
||||
|
||||
#### 6.1.8 IMULH_R, ISMULH_R
|
||||
See chapter 5.2.5.
|
||||
|
||||
#### 6.1.9 IMUL_RCP
|
||||
See chapter 5.2.6. `imm32` is never 0 or a power of 2.
|
||||
|
||||
### 6.2 The reference CPU
|
||||
|
||||
Unlike a standard RandomX program, a SuperscalarHash program is generated using a strict set of rules to achieve the maximum performance on a superscalar CPU. For this purpose, the generator runs a simulation of a reference CPU.
|
||||
|
||||
The reference CPU is loosely based on the [Intel Ivy Bridge microarchitecture](https://en.wikipedia.org/wiki/Ivy_Bridge_(microarchitecture)). It has the following properties:
|
||||
|
||||
* The CPU has 3 integer execution ports P0, P1 and P5 that can execute instructions in parallel. Multiplication can run only on port P1.
|
||||
* Each of the Superscalar instructions listed in Table 6.1.1 consist of one or more *Macro-ops*. Each Macro-op has certain execution latency (in cycles) and size (in bytes) as shown in Table 6.2.1.
|
||||
* Each of the Macro-ops listed in Table 6.2.1 consists of 0-2 *Micro-ops* that can go to a subset of the 3 execution ports. If a Macro-op consists of 2 Micro-ops, both must be executed together.
|
||||
* The CPU can decode at most 16 bytes of code per cycle and at most 4 Micro-ops per cycle.
|
||||
|
||||
*Table 6.2.1 - Macro-ops*
|
||||
|
||||
|Macro-op|latency|size|1st Micro-op|2nd Micro-op|
|
||||
|-|-|-|-|-|
|
||||
|`sub_rr`|1|3|P015|-|
|
||||
|`xor_rr`|1|3|P015|-|
|
||||
|`lea_sib`|1|4|P01|-|
|
||||
|`imul_rr`|3|4|P1|-|
|
||||
|`ror_ri`|1|4|P05|-|
|
||||
|`add_ri`|1|7, 8, 9|P015|-|
|
||||
|`xor_ri`|1|7, 8, 9|P015|-|
|
||||
|`mov_rr`|0|3|-|-|
|
||||
|`mul_r`|4|3|P1|P5|
|
||||
|`imul_r`|4|3|P1|P5|
|
||||
|`mov_ri`|1|10|P015|-|
|
||||
|
||||
* P015 - Micro-op can be executed on any port
|
||||
* P01 - Micro-op can be executed on ports P0 or P1
|
||||
* P05 - Micro-op can be executed on ports P0 or P5
|
||||
* P1 - Micro-op can be executed only on port P1
|
||||
* P5 - Micro-op can be executed only on port P5
|
||||
|
||||
Macro-ops `add_ri` and `xor_ri` can be optionally padded to a size of 8 or 9 bytes for code alignment purposes. `mov_rr` has 0 execution latency and doesn't use an execution port, but still occupies space during the decoding stage (see chapter 6.3.1).
|
||||
|
||||
### 6.3 CPU simulation
|
||||
|
||||
SuperscalarHash programs are generated to maximize the usage of all 3 execution ports of the reference CPU. The generation consists of 4 stages:
|
||||
|
||||
* Decoding stage
|
||||
* Instruction selection
|
||||
* Port assignment
|
||||
* Operand assignment
|
||||
|
||||
Program generation is complete when one of two conditions is met:
|
||||
|
||||
1. An instruction is scheduled for execution on cycle that is equal to or greater than `RANDOMX_SUPERSCALAR_LATENCY`
|
||||
1. The number of generated instructions reaches `3 * RANDOMX_SUPERSCALAR_LATENCY + 2`.
|
||||
|
||||
#### 6.3.1 Decoding stage
|
||||
|
||||
The generator produces instructions in groups of 3 or 4 Macro-op slots such that the size of each group is exactly 16 bytes.
|
||||
|
||||
*Table 6.3.1 - Decoder configurations*
|
||||
|
||||
|decoder group|configuration|
|
||||
|-------------|-------------|
|
||||
|0|4-8-4|
|
||||
|1|7-3-3-3|
|
||||
|2|3-7-3-3|
|
||||
|3|4-9-3|
|
||||
|4|4-4-4-4|
|
||||
|5|3-3-10|
|
||||
|
||||
The rules for the selection of the decoder group are following:
|
||||
|
||||
* If the currently processed instruction is IMULH_R or ISMULH_R, the next decode group is group 5 (the only group that starts with a 3-byte slot and has only 3 slots).
|
||||
* If the total number of multiplications that have been generated is less than or equal to the current decoding cycle, the next decode group is group 4.
|
||||
* If the currently processed instruction is IMUL_RCP, the next decode group is group 0 or 3 (must begin with a 4-byte slot for multiplication).
|
||||
* Otherwise a random decode group is selected from groups 0-3.
|
||||
|
||||
#### 6.3.2 Instruction selection
|
||||
|
||||
Instructions are selected based on the size of the current decode group slot - see Table 6.3.2.
|
||||
|
||||
*Table 6.3.2 - Decoder configurations*
|
||||
|
||||
|slot size|note|instructions|
|
||||
|-------------|-------------|-----|
|
||||
|3|-|ISUB_R, IXOR_R
|
||||
|3|last slot in the group|ISUB_R, IXOR_R, IMULH_R, ISMULH_R|
|
||||
|4|decode group 4, not the last slot|IMUL_R|
|
||||
|4|-|IROR_C, IADD_RS|
|
||||
|7,8,9|-|IADD_C, IXOR_C|
|
||||
|10|-|IMUL_RCP|
|
||||
|
||||
#### 6.3.3 Port assignment
|
||||
|
||||
Micro-ops are issued to execution ports as soon as an available port is free. The scheduling is done optimistically by checking port availability in order P5 -> P0 -> P1 to not overload port P1 (multiplication) by instructions that can go to any port. The cycle when all Micro-ops of an instruction can be executed is called the 'scheduleCycle'.
|
||||
|
||||
#### 6.3.4 Operand assignment
|
||||
|
||||
The source operand (if needed) is selected first. is it selected from the group of registers that are available at the 'scheduleCycle' of the instruction. A register is available if the latency of its last operation has elapsed.
|
||||
|
||||
The destination operand is selected with more strict rules (see column 'rules' in Table 6.1.1):
|
||||
|
||||
* value must be ready at the required cycle
|
||||
* cannot be the same as the source register unless the instruction allows it (see column 'rules' in Table 6.1.1)
|
||||
* this avoids optimizable operations such as `reg ^ reg` or `reg - reg`
|
||||
* it also increases intermixing of register values
|
||||
* register cannot be multiplied twice in a row unless `allowChainedMul` is true
|
||||
* this avoids accumulation of trailing zeroes in registers due to excessive multiplication
|
||||
* `allowChainedMul` is set to true if an attempt to find source/destination registers failed (this is quite rare, but prevents a catastrophic failure of the generator)
|
||||
* either the last instruction applied to the register or its source must be different than the current instruction
|
||||
* this avoids optimizable instruction sequences such as `r1 = r1 ^ r2; r1 = r1 ^ r2` (can be eliminated) or `reg = reg >>> C1; reg = reg >>> C2` (can be reduced to one rotation) or `reg = reg + C1; reg = reg + C2` (can be reduced to one addition)
|
||||
* register `r5` cannot be the destination of the IADD_RS instruction (limitation of the x86 lea instruction)
|
||||
|
||||
## 7. Dataset
|
||||
|
||||
The Dataset is a read-only memory structure that is used during program execution (chapter 4.6.2, steps 6 and 7). The size of the Dataset is `RANDOMX_DATASET_BASE_SIZE + RANDOMX_DATASET_EXTRA_SIZE` bytes and it's divided into 64-byte 'items'.
|
||||
|
||||
In order to allow PoW verification with a lower amount of memory, the Dataset is constructed in two steps using an intermediate structure called the "Cache", which can be used to calculate Dataset items on the fly.
|
||||
|
||||
The whole Dataset is constructed from the key value `K`, which is an input parameter of RandomX. The whole Dataset needs to be recalculated everytime the key value changes. Fig. 7.1 shows the process of Dataset construction. Note: the maximum supported length of `K` is 60 bytes. Using a longer key results in implementation-defined behavior.
|
||||
|
||||
*Figure 7.1 - Dataset construction*
|
||||
|
||||

|
||||
|
||||
### 7.1 Cache construction
|
||||
|
||||
The key `K` is expanded into the Cache using the "memory fill" function of Argon2d with parameters according to Table 7.1.1. The key is used as the "password" field.
|
||||
|
||||
*Table 7.1.1 - Argon2 parameters*
|
||||
|
||||
|parameter|value|
|
||||
|------------|--|
|
||||
|parallelism|`RANDOMX_ARGON_LANES`|
|
||||
|output size|0|
|
||||
|memory|`RANDOMX_ARGON_MEMORY`|
|
||||
|iterations|`RANDOMX_ARGON_ITERATIONS`|
|
||||
|version|`0x13`|
|
||||
|hash type|0 (Argon2d)|
|
||||
|password|key value `K`|
|
||||
|salt|`RANDOMX_ARGON_SALT`
|
||||
|secret size|0|
|
||||
|assoc. data size|0|
|
||||
|
||||
The finalizer and output calculation steps of Argon2 are omitted. The output is the filled memory array.
|
||||
|
||||
### 7.2 SuperscalarHash initialization
|
||||
|
||||
The key value `K` is used to initialize a BlakeGenerator (see chapter 3.5), which is then used to generate 8 SuperscalarHash instances for Dataset initialization.
|
||||
|
||||
### 7.3 Dataset block generation
|
||||
Dataset items are numbered sequentially with `itemNumber` starting from 0. Each 64-byte Dataset item is generated independently using 8 SuperscalarHash functions (generated according to chapter 7.2) and by XORing randomly selected data from the Cache (constructed according to chapter 7.1).
|
||||
|
||||
The item data is represented by 8 64-bit integer registers: `r0`-`r7`.
|
||||
|
||||
1. The register values are initialized as follows (`*` = multiplication, `^` = XOR):
|
||||
* `r0 = (itemNumber + 1) * 6364136223846793005`
|
||||
* `r1 = r0 ^ 9298411001130361340`
|
||||
* `r2 = r0 ^ 12065312585734608966`
|
||||
* `r3 = r0 ^ 9306329213124626780`
|
||||
* `r4 = r0 ^ 5281919268842080866`
|
||||
* `r5 = r0 ^ 10536153434571861004`
|
||||
* `r6 = r0 ^ 3398623926847679864`
|
||||
* `r7 = r0 ^ 9549104520008361294`
|
||||
1. Let `cacheIndex = itemNumber`
|
||||
1. Let `i = 0`
|
||||
1. Load a 64-byte item from the Cache. The item index is given by `cacheIndex` modulo the total number of 64-byte items in Cache.
|
||||
1. Execute `SuperscalarHash[i](r0, r1, r2, r3, r4, r5, r6, r7)`, where `SuperscalarHash[i]` refers to the i-th SuperscalarHash function. This modifies the values of the registers `r0`-`r7`.
|
||||
1. XOR all registers with the 64 bytes loaded in step 4 (8 bytes per column in order `r0`-`r7`).
|
||||
1. Set `cacheIndex` to the value of the register that has the longest dependency chain in the SuperscalarHash function executed in step 5.
|
||||
1. Set `i = i + 1` and go back to step 4 if `i < RANDOMX_CACHE_ACCESSES`.
|
||||
1. Concatenate registers `r0`-`r7` in little endian format to get the final Dataset item data.
|
||||
|
||||
The constants used to initialize register values in step 1 were determined as follows:
|
||||
|
||||
* Multiplier `6364136223846793005` was selected because it gives an excellent distribution for linear generators (D. Knuth: The Art of Computer Programming – Vol 2., also listed in [Commonly used LCG parameters](https://en.wikipedia.org/wiki/Linear_congruential_generator#Parameters_in_common_use))
|
||||
* XOR constants used to initialize registers `r1`-`r7` were determined by calculating `Hash512` of the ASCII value `"RandomX SuperScalarHash initialize"` and taking bytes 8-63 as 7 little-endian unsigned 64-bit integers. Additionally, the constant for `r1` was increased by <code>2<sup>33</sup>+700</code> and the constant for `r3` was increased by <code>2<sup>14</sup></code> (these changes are necessary to ensure that all registers have unique initial values for all values of `itemNumber`).
|
||||
|
||||
13
app/src/main/cpp/randomx/doc/tevador.asc
Normal file
13
app/src/main/cpp/randomx/doc/tevador.asc
Normal file
|
|
@ -0,0 +1,13 @@
|
|||
-----BEGIN PGP PUBLIC KEY BLOCK-----
|
||||
|
||||
mDMEXd+PeBYJKwYBBAHaRw8BAQdAZ0nqJ+nRYoScG2QLX62pl+WO1+Mkv6Yyt2Kb
|
||||
ntGUuLq0G3RldmFkb3IgPHRldmFkb3JAZ21haWwuY29tPoiWBBMWCAA+AhsDBQsJ
|
||||
CAcCBhUKCQgLAgQWAgMBAh4BAheAFiEEMoWjLVEwdmMs6CUQWijIaue9c6YFAmRP
|
||||
r8MFCQ/ZS2YACgkQWijIaue9c6bR5gEA0tnQ4Al+yOLoRUBQitAV8FU4FLy8Xx8U
|
||||
IyyivjJ0UhIA/2jwJfMXmJdMKtar8xfIA5mZLLofkEP6hug4knhitpkBuDgEXd+P
|
||||
eBIKKwYBBAGXVQEFAQEHQBNbQuPcDojMCkRb5B5u7Ld/AFLClOh+6ElL+u61rIY/
|
||||
AwEIB4h+BBgWCAAmAhsMFiEEMoWjLVEwdmMs6CUQWijIaue9c6YFAmRQoAMFCQ/Z
|
||||
S2YACgkQWijIaue9c6bUfwD9Hw20kGCaZ8rWghz9W3bc645ys1vPQpQW28CD9w3B
|
||||
cTMBALsV1xpS2pGwTfn1PUimqESZfTrREmNvOjKSQwe0yicI
|
||||
=D4lm
|
||||
-----END PGP PUBLIC KEY BLOCK-----
|
||||
177
app/src/main/cpp/randomx/randomx.sln
Normal file
177
app/src/main/cpp/randomx/randomx.sln
Normal file
|
|
@ -0,0 +1,177 @@
|
|||
|
||||
Microsoft Visual Studio Solution File, Format Version 12.00
|
||||
# Visual Studio 15
|
||||
VisualStudioVersion = 15.0.28307.572
|
||||
MinimumVisualStudioVersion = 10.0.40219.1
|
||||
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "randomx", "vcxproj\randomx.vcxproj", "{3346A4AD-C438-4324-8B77-47A16452954B}"
|
||||
EndProject
|
||||
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "tests", "tests", "{4A4A689F-86AF-41C0-A974-1080506D0923}"
|
||||
EndProject
|
||||
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "superscalar-avalanche", "vcxproj\superscalar-avalanche.vcxproj", "{CF34A7EF-7DC9-4077-94A5-76F5425EA938}"
|
||||
EndProject
|
||||
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "superscalar-init", "vcxproj\superscalar-init.vcxproj", "{E59DC709-9B12-4A53-BAF3-79398821C376}"
|
||||
EndProject
|
||||
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "superscalar-stats", "vcxproj\superscalar-stats.vcxproj", "{0173D560-8C12-46B3-B467-0C6E7573AA0B}"
|
||||
EndProject
|
||||
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "benchmark", "vcxproj\benchmark.vcxproj", "{1E8A2E2F-9F9F-43AA-BB19-9107FEC64A70}"
|
||||
EndProject
|
||||
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "api-example1", "vcxproj\api-example1.vcxproj", "{83EA3E54-5D91-4E01-8EF6-C1E718334F83}"
|
||||
EndProject
|
||||
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "api-example2", "vcxproj\api-example2.vcxproj", "{44947B9C-E6B1-4C06-BD01-F8EF43B59223}"
|
||||
EndProject
|
||||
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "code-generator", "vcxproj\code-generator.vcxproj", "{3E490DEC-1874-43AA-92DA-1AC57C217EAC}"
|
||||
EndProject
|
||||
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "scratchpad-entropy", "vcxproj\scratchpad-entropy.vcxproj", "{FF8BD408-AFD8-43C6-BE98-4D03B37E840B}"
|
||||
EndProject
|
||||
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "jit-performance", "vcxproj\jit-performance.vcxproj", "{535F2111-FA81-4C76-A354-EDD2F9AA00E3}"
|
||||
EndProject
|
||||
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "perf-simulation", "vcxproj\perf-simulation.vcxproj", "{F1FC7AC0-2773-4A57-AFA7-56BB07216AA2}"
|
||||
EndProject
|
||||
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "runtime-distr", "vcxproj\runtime-distr.vcxproj", "{F207EC8C-C55F-46C0-8851-887A71574F54}"
|
||||
EndProject
|
||||
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "randomx-dll", "vcxproj\randomx-dll.vcxproj", "{59560AD8-18E3-463E-A941-BBD808EC7C83}"
|
||||
EndProject
|
||||
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "tests", "vcxproj\tests.vcxproj", "{41F3F4DF-8113-4029-9915-FDDC44C43D49}"
|
||||
EndProject
|
||||
Global
|
||||
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
||||
Debug|x64 = Debug|x64
|
||||
Debug|x86 = Debug|x86
|
||||
Release|x64 = Release|x64
|
||||
Release|x86 = Release|x86
|
||||
EndGlobalSection
|
||||
GlobalSection(ProjectConfigurationPlatforms) = postSolution
|
||||
{3346A4AD-C438-4324-8B77-47A16452954B}.Debug|x64.ActiveCfg = Debug|x64
|
||||
{3346A4AD-C438-4324-8B77-47A16452954B}.Debug|x64.Build.0 = Debug|x64
|
||||
{3346A4AD-C438-4324-8B77-47A16452954B}.Debug|x86.ActiveCfg = Debug|Win32
|
||||
{3346A4AD-C438-4324-8B77-47A16452954B}.Debug|x86.Build.0 = Debug|Win32
|
||||
{3346A4AD-C438-4324-8B77-47A16452954B}.Release|x64.ActiveCfg = Release|x64
|
||||
{3346A4AD-C438-4324-8B77-47A16452954B}.Release|x64.Build.0 = Release|x64
|
||||
{3346A4AD-C438-4324-8B77-47A16452954B}.Release|x86.ActiveCfg = Release|Win32
|
||||
{3346A4AD-C438-4324-8B77-47A16452954B}.Release|x86.Build.0 = Release|Win32
|
||||
{CF34A7EF-7DC9-4077-94A5-76F5425EA938}.Debug|x64.ActiveCfg = Debug|x64
|
||||
{CF34A7EF-7DC9-4077-94A5-76F5425EA938}.Debug|x64.Build.0 = Debug|x64
|
||||
{CF34A7EF-7DC9-4077-94A5-76F5425EA938}.Debug|x86.ActiveCfg = Debug|Win32
|
||||
{CF34A7EF-7DC9-4077-94A5-76F5425EA938}.Debug|x86.Build.0 = Debug|Win32
|
||||
{CF34A7EF-7DC9-4077-94A5-76F5425EA938}.Release|x64.ActiveCfg = Release|x64
|
||||
{CF34A7EF-7DC9-4077-94A5-76F5425EA938}.Release|x64.Build.0 = Release|x64
|
||||
{CF34A7EF-7DC9-4077-94A5-76F5425EA938}.Release|x86.ActiveCfg = Release|Win32
|
||||
{CF34A7EF-7DC9-4077-94A5-76F5425EA938}.Release|x86.Build.0 = Release|Win32
|
||||
{E59DC709-9B12-4A53-BAF3-79398821C376}.Debug|x64.ActiveCfg = Debug|x64
|
||||
{E59DC709-9B12-4A53-BAF3-79398821C376}.Debug|x64.Build.0 = Debug|x64
|
||||
{E59DC709-9B12-4A53-BAF3-79398821C376}.Debug|x86.ActiveCfg = Debug|Win32
|
||||
{E59DC709-9B12-4A53-BAF3-79398821C376}.Debug|x86.Build.0 = Debug|Win32
|
||||
{E59DC709-9B12-4A53-BAF3-79398821C376}.Release|x64.ActiveCfg = Release|x64
|
||||
{E59DC709-9B12-4A53-BAF3-79398821C376}.Release|x64.Build.0 = Release|x64
|
||||
{E59DC709-9B12-4A53-BAF3-79398821C376}.Release|x86.ActiveCfg = Release|Win32
|
||||
{E59DC709-9B12-4A53-BAF3-79398821C376}.Release|x86.Build.0 = Release|Win32
|
||||
{0173D560-8C12-46B3-B467-0C6E7573AA0B}.Debug|x64.ActiveCfg = Debug|x64
|
||||
{0173D560-8C12-46B3-B467-0C6E7573AA0B}.Debug|x64.Build.0 = Debug|x64
|
||||
{0173D560-8C12-46B3-B467-0C6E7573AA0B}.Debug|x86.ActiveCfg = Debug|Win32
|
||||
{0173D560-8C12-46B3-B467-0C6E7573AA0B}.Debug|x86.Build.0 = Debug|Win32
|
||||
{0173D560-8C12-46B3-B467-0C6E7573AA0B}.Release|x64.ActiveCfg = Release|x64
|
||||
{0173D560-8C12-46B3-B467-0C6E7573AA0B}.Release|x64.Build.0 = Release|x64
|
||||
{0173D560-8C12-46B3-B467-0C6E7573AA0B}.Release|x86.ActiveCfg = Release|Win32
|
||||
{0173D560-8C12-46B3-B467-0C6E7573AA0B}.Release|x86.Build.0 = Release|Win32
|
||||
{1E8A2E2F-9F9F-43AA-BB19-9107FEC64A70}.Debug|x64.ActiveCfg = Debug|x64
|
||||
{1E8A2E2F-9F9F-43AA-BB19-9107FEC64A70}.Debug|x64.Build.0 = Debug|x64
|
||||
{1E8A2E2F-9F9F-43AA-BB19-9107FEC64A70}.Debug|x86.ActiveCfg = Debug|Win32
|
||||
{1E8A2E2F-9F9F-43AA-BB19-9107FEC64A70}.Debug|x86.Build.0 = Debug|Win32
|
||||
{1E8A2E2F-9F9F-43AA-BB19-9107FEC64A70}.Release|x64.ActiveCfg = Release|x64
|
||||
{1E8A2E2F-9F9F-43AA-BB19-9107FEC64A70}.Release|x64.Build.0 = Release|x64
|
||||
{1E8A2E2F-9F9F-43AA-BB19-9107FEC64A70}.Release|x86.ActiveCfg = Release|Win32
|
||||
{1E8A2E2F-9F9F-43AA-BB19-9107FEC64A70}.Release|x86.Build.0 = Release|Win32
|
||||
{83EA3E54-5D91-4E01-8EF6-C1E718334F83}.Debug|x64.ActiveCfg = Debug|x64
|
||||
{83EA3E54-5D91-4E01-8EF6-C1E718334F83}.Debug|x64.Build.0 = Debug|x64
|
||||
{83EA3E54-5D91-4E01-8EF6-C1E718334F83}.Debug|x86.ActiveCfg = Debug|Win32
|
||||
{83EA3E54-5D91-4E01-8EF6-C1E718334F83}.Debug|x86.Build.0 = Debug|Win32
|
||||
{83EA3E54-5D91-4E01-8EF6-C1E718334F83}.Release|x64.ActiveCfg = Release|x64
|
||||
{83EA3E54-5D91-4E01-8EF6-C1E718334F83}.Release|x64.Build.0 = Release|x64
|
||||
{83EA3E54-5D91-4E01-8EF6-C1E718334F83}.Release|x86.ActiveCfg = Release|Win32
|
||||
{83EA3E54-5D91-4E01-8EF6-C1E718334F83}.Release|x86.Build.0 = Release|Win32
|
||||
{44947B9C-E6B1-4C06-BD01-F8EF43B59223}.Debug|x64.ActiveCfg = Debug|x64
|
||||
{44947B9C-E6B1-4C06-BD01-F8EF43B59223}.Debug|x64.Build.0 = Debug|x64
|
||||
{44947B9C-E6B1-4C06-BD01-F8EF43B59223}.Debug|x86.ActiveCfg = Debug|Win32
|
||||
{44947B9C-E6B1-4C06-BD01-F8EF43B59223}.Debug|x86.Build.0 = Debug|Win32
|
||||
{44947B9C-E6B1-4C06-BD01-F8EF43B59223}.Release|x64.ActiveCfg = Release|x64
|
||||
{44947B9C-E6B1-4C06-BD01-F8EF43B59223}.Release|x64.Build.0 = Release|x64
|
||||
{44947B9C-E6B1-4C06-BD01-F8EF43B59223}.Release|x86.ActiveCfg = Release|Win32
|
||||
{44947B9C-E6B1-4C06-BD01-F8EF43B59223}.Release|x86.Build.0 = Release|Win32
|
||||
{3E490DEC-1874-43AA-92DA-1AC57C217EAC}.Debug|x64.ActiveCfg = Debug|x64
|
||||
{3E490DEC-1874-43AA-92DA-1AC57C217EAC}.Debug|x64.Build.0 = Debug|x64
|
||||
{3E490DEC-1874-43AA-92DA-1AC57C217EAC}.Debug|x86.ActiveCfg = Debug|Win32
|
||||
{3E490DEC-1874-43AA-92DA-1AC57C217EAC}.Debug|x86.Build.0 = Debug|Win32
|
||||
{3E490DEC-1874-43AA-92DA-1AC57C217EAC}.Release|x64.ActiveCfg = Release|x64
|
||||
{3E490DEC-1874-43AA-92DA-1AC57C217EAC}.Release|x64.Build.0 = Release|x64
|
||||
{3E490DEC-1874-43AA-92DA-1AC57C217EAC}.Release|x86.ActiveCfg = Release|Win32
|
||||
{3E490DEC-1874-43AA-92DA-1AC57C217EAC}.Release|x86.Build.0 = Release|Win32
|
||||
{FF8BD408-AFD8-43C6-BE98-4D03B37E840B}.Debug|x64.ActiveCfg = Debug|x64
|
||||
{FF8BD408-AFD8-43C6-BE98-4D03B37E840B}.Debug|x64.Build.0 = Debug|x64
|
||||
{FF8BD408-AFD8-43C6-BE98-4D03B37E840B}.Debug|x86.ActiveCfg = Debug|Win32
|
||||
{FF8BD408-AFD8-43C6-BE98-4D03B37E840B}.Debug|x86.Build.0 = Debug|Win32
|
||||
{FF8BD408-AFD8-43C6-BE98-4D03B37E840B}.Release|x64.ActiveCfg = Release|x64
|
||||
{FF8BD408-AFD8-43C6-BE98-4D03B37E840B}.Release|x64.Build.0 = Release|x64
|
||||
{FF8BD408-AFD8-43C6-BE98-4D03B37E840B}.Release|x86.ActiveCfg = Release|Win32
|
||||
{FF8BD408-AFD8-43C6-BE98-4D03B37E840B}.Release|x86.Build.0 = Release|Win32
|
||||
{535F2111-FA81-4C76-A354-EDD2F9AA00E3}.Debug|x64.ActiveCfg = Debug|x64
|
||||
{535F2111-FA81-4C76-A354-EDD2F9AA00E3}.Debug|x64.Build.0 = Debug|x64
|
||||
{535F2111-FA81-4C76-A354-EDD2F9AA00E3}.Debug|x86.ActiveCfg = Debug|Win32
|
||||
{535F2111-FA81-4C76-A354-EDD2F9AA00E3}.Debug|x86.Build.0 = Debug|Win32
|
||||
{535F2111-FA81-4C76-A354-EDD2F9AA00E3}.Release|x64.ActiveCfg = Release|x64
|
||||
{535F2111-FA81-4C76-A354-EDD2F9AA00E3}.Release|x64.Build.0 = Release|x64
|
||||
{535F2111-FA81-4C76-A354-EDD2F9AA00E3}.Release|x86.ActiveCfg = Release|Win32
|
||||
{535F2111-FA81-4C76-A354-EDD2F9AA00E3}.Release|x86.Build.0 = Release|Win32
|
||||
{F1FC7AC0-2773-4A57-AFA7-56BB07216AA2}.Debug|x64.ActiveCfg = Debug|x64
|
||||
{F1FC7AC0-2773-4A57-AFA7-56BB07216AA2}.Debug|x64.Build.0 = Debug|x64
|
||||
{F1FC7AC0-2773-4A57-AFA7-56BB07216AA2}.Debug|x86.ActiveCfg = Debug|Win32
|
||||
{F1FC7AC0-2773-4A57-AFA7-56BB07216AA2}.Debug|x86.Build.0 = Debug|Win32
|
||||
{F1FC7AC0-2773-4A57-AFA7-56BB07216AA2}.Release|x64.ActiveCfg = Release|x64
|
||||
{F1FC7AC0-2773-4A57-AFA7-56BB07216AA2}.Release|x64.Build.0 = Release|x64
|
||||
{F1FC7AC0-2773-4A57-AFA7-56BB07216AA2}.Release|x86.ActiveCfg = Release|Win32
|
||||
{F1FC7AC0-2773-4A57-AFA7-56BB07216AA2}.Release|x86.Build.0 = Release|Win32
|
||||
{F207EC8C-C55F-46C0-8851-887A71574F54}.Debug|x64.ActiveCfg = Debug|x64
|
||||
{F207EC8C-C55F-46C0-8851-887A71574F54}.Debug|x64.Build.0 = Debug|x64
|
||||
{F207EC8C-C55F-46C0-8851-887A71574F54}.Debug|x86.ActiveCfg = Debug|Win32
|
||||
{F207EC8C-C55F-46C0-8851-887A71574F54}.Debug|x86.Build.0 = Debug|Win32
|
||||
{F207EC8C-C55F-46C0-8851-887A71574F54}.Release|x64.ActiveCfg = Release|x64
|
||||
{F207EC8C-C55F-46C0-8851-887A71574F54}.Release|x64.Build.0 = Release|x64
|
||||
{F207EC8C-C55F-46C0-8851-887A71574F54}.Release|x86.ActiveCfg = Release|Win32
|
||||
{F207EC8C-C55F-46C0-8851-887A71574F54}.Release|x86.Build.0 = Release|Win32
|
||||
{59560AD8-18E3-463E-A941-BBD808EC7C83}.Debug|x64.ActiveCfg = Debug|x64
|
||||
{59560AD8-18E3-463E-A941-BBD808EC7C83}.Debug|x64.Build.0 = Debug|x64
|
||||
{59560AD8-18E3-463E-A941-BBD808EC7C83}.Debug|x86.ActiveCfg = Debug|Win32
|
||||
{59560AD8-18E3-463E-A941-BBD808EC7C83}.Debug|x86.Build.0 = Debug|Win32
|
||||
{59560AD8-18E3-463E-A941-BBD808EC7C83}.Release|x64.ActiveCfg = Release|x64
|
||||
{59560AD8-18E3-463E-A941-BBD808EC7C83}.Release|x64.Build.0 = Release|x64
|
||||
{59560AD8-18E3-463E-A941-BBD808EC7C83}.Release|x86.ActiveCfg = Release|Win32
|
||||
{59560AD8-18E3-463E-A941-BBD808EC7C83}.Release|x86.Build.0 = Release|Win32
|
||||
{41F3F4DF-8113-4029-9915-FDDC44C43D49}.Debug|x64.ActiveCfg = Debug|x64
|
||||
{41F3F4DF-8113-4029-9915-FDDC44C43D49}.Debug|x64.Build.0 = Debug|x64
|
||||
{41F3F4DF-8113-4029-9915-FDDC44C43D49}.Debug|x86.ActiveCfg = Debug|Win32
|
||||
{41F3F4DF-8113-4029-9915-FDDC44C43D49}.Debug|x86.Build.0 = Debug|Win32
|
||||
{41F3F4DF-8113-4029-9915-FDDC44C43D49}.Release|x64.ActiveCfg = Release|x64
|
||||
{41F3F4DF-8113-4029-9915-FDDC44C43D49}.Release|x64.Build.0 = Release|x64
|
||||
{41F3F4DF-8113-4029-9915-FDDC44C43D49}.Release|x86.ActiveCfg = Release|Win32
|
||||
{41F3F4DF-8113-4029-9915-FDDC44C43D49}.Release|x86.Build.0 = Release|Win32
|
||||
EndGlobalSection
|
||||
GlobalSection(SolutionProperties) = preSolution
|
||||
HideSolutionNode = FALSE
|
||||
EndGlobalSection
|
||||
GlobalSection(NestedProjects) = preSolution
|
||||
{CF34A7EF-7DC9-4077-94A5-76F5425EA938} = {4A4A689F-86AF-41C0-A974-1080506D0923}
|
||||
{E59DC709-9B12-4A53-BAF3-79398821C376} = {4A4A689F-86AF-41C0-A974-1080506D0923}
|
||||
{0173D560-8C12-46B3-B467-0C6E7573AA0B} = {4A4A689F-86AF-41C0-A974-1080506D0923}
|
||||
{1E8A2E2F-9F9F-43AA-BB19-9107FEC64A70} = {4A4A689F-86AF-41C0-A974-1080506D0923}
|
||||
{83EA3E54-5D91-4E01-8EF6-C1E718334F83} = {4A4A689F-86AF-41C0-A974-1080506D0923}
|
||||
{44947B9C-E6B1-4C06-BD01-F8EF43B59223} = {4A4A689F-86AF-41C0-A974-1080506D0923}
|
||||
{3E490DEC-1874-43AA-92DA-1AC57C217EAC} = {4A4A689F-86AF-41C0-A974-1080506D0923}
|
||||
{FF8BD408-AFD8-43C6-BE98-4D03B37E840B} = {4A4A689F-86AF-41C0-A974-1080506D0923}
|
||||
{535F2111-FA81-4C76-A354-EDD2F9AA00E3} = {4A4A689F-86AF-41C0-A974-1080506D0923}
|
||||
{F1FC7AC0-2773-4A57-AFA7-56BB07216AA2} = {4A4A689F-86AF-41C0-A974-1080506D0923}
|
||||
{F207EC8C-C55F-46C0-8851-887A71574F54} = {4A4A689F-86AF-41C0-A974-1080506D0923}
|
||||
{41F3F4DF-8113-4029-9915-FDDC44C43D49} = {4A4A689F-86AF-41C0-A974-1080506D0923}
|
||||
EndGlobalSection
|
||||
GlobalSection(ExtensibilityGlobals) = postSolution
|
||||
SolutionGuid = {4EBC03DB-AE37-4141-8147-692F16E0ED02}
|
||||
EndGlobalSection
|
||||
EndGlobal
|
||||
322
app/src/main/cpp/randomx/src/aes_hash.cpp
Normal file
322
app/src/main/cpp/randomx/src/aes_hash.cpp
Normal file
|
|
@ -0,0 +1,322 @@
|
|||
/*
|
||||
Copyright (c) 2018-2019, tevador <tevador@gmail.com>
|
||||
|
||||
All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted provided that the following conditions are met:
|
||||
* Redistributions of source code must retain the above copyright
|
||||
notice, this list of conditions and the following disclaimer.
|
||||
* Redistributions in binary form must reproduce the above copyright
|
||||
notice, this list of conditions and the following disclaimer in the
|
||||
documentation and/or other materials provided with the distribution.
|
||||
* Neither the name of the copyright holder nor the
|
||||
names of its contributors may be used to endorse or promote products
|
||||
derived from this software without specific prior written permission.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
|
||||
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
|
||||
FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
||||
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
|
||||
CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
|
||||
OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
#include "soft_aes.h"
|
||||
#include <cassert>
|
||||
|
||||
//NOTE: The functions below were tuned for maximum performance
|
||||
//and are not cryptographically secure outside of the scope of RandomX.
|
||||
//It's not recommended to use them as general hash functions and PRNGs.
|
||||
|
||||
//AesHash1R:
|
||||
//state0, state1, state2, state3 = Blake2b-512("RandomX AesHash1R state")
|
||||
//xkey0, xkey1 = Blake2b-256("RandomX AesHash1R xkeys")
|
||||
|
||||
#define AES_HASH_1R_STATE0 0xd7983aad, 0xcc82db47, 0x9fa856de, 0x92b52c0d
|
||||
#define AES_HASH_1R_STATE1 0xace78057, 0xf59e125a, 0x15c7b798, 0x338d996e
|
||||
#define AES_HASH_1R_STATE2 0xe8a07ce4, 0x5079506b, 0xae62c7d0, 0x6a770017
|
||||
#define AES_HASH_1R_STATE3 0x7e994948, 0x79a10005, 0x07ad828d, 0x630a240c
|
||||
|
||||
#define AES_HASH_1R_XKEY0 0x06890201, 0x90dc56bf, 0x8b24949f, 0xf6fa8389
|
||||
#define AES_HASH_1R_XKEY1 0xed18f99b, 0xee1043c6, 0x51f4e03c, 0x61b263d1
|
||||
|
||||
/*
|
||||
Calculate a 512-bit hash of 'input' using 4 lanes of AES.
|
||||
The input is treated as a set of round keys for the encryption
|
||||
of the initial state.
|
||||
|
||||
'inputSize' must be a multiple of 64.
|
||||
|
||||
For a 2 MiB input, this has the same security as 32768-round
|
||||
AES encryption.
|
||||
|
||||
Hashing throughput: >20 GiB/s per CPU core with hardware AES
|
||||
*/
|
||||
template<bool softAes>
|
||||
void hashAes1Rx4(const void *input, size_t inputSize, void *hash) {
|
||||
assert(inputSize % 64 == 0);
|
||||
const uint8_t* inptr = (uint8_t*)input;
|
||||
const uint8_t* inputEnd = inptr + inputSize;
|
||||
|
||||
rx_vec_i128 state0, state1, state2, state3;
|
||||
rx_vec_i128 in0, in1, in2, in3;
|
||||
|
||||
//intial state
|
||||
state0 = rx_set_int_vec_i128(AES_HASH_1R_STATE0);
|
||||
state1 = rx_set_int_vec_i128(AES_HASH_1R_STATE1);
|
||||
state2 = rx_set_int_vec_i128(AES_HASH_1R_STATE2);
|
||||
state3 = rx_set_int_vec_i128(AES_HASH_1R_STATE3);
|
||||
|
||||
//process 64 bytes at a time in 4 lanes
|
||||
while (inptr < inputEnd) {
|
||||
in0 = rx_load_vec_i128((rx_vec_i128*)inptr + 0);
|
||||
in1 = rx_load_vec_i128((rx_vec_i128*)inptr + 1);
|
||||
in2 = rx_load_vec_i128((rx_vec_i128*)inptr + 2);
|
||||
in3 = rx_load_vec_i128((rx_vec_i128*)inptr + 3);
|
||||
|
||||
state0 = aesenc<softAes>(state0, in0);
|
||||
state1 = aesdec<softAes>(state1, in1);
|
||||
state2 = aesenc<softAes>(state2, in2);
|
||||
state3 = aesdec<softAes>(state3, in3);
|
||||
|
||||
inptr += 64;
|
||||
}
|
||||
|
||||
//two extra rounds to achieve full diffusion
|
||||
rx_vec_i128 xkey0 = rx_set_int_vec_i128(AES_HASH_1R_XKEY0);
|
||||
rx_vec_i128 xkey1 = rx_set_int_vec_i128(AES_HASH_1R_XKEY1);
|
||||
|
||||
state0 = aesenc<softAes>(state0, xkey0);
|
||||
state1 = aesdec<softAes>(state1, xkey0);
|
||||
state2 = aesenc<softAes>(state2, xkey0);
|
||||
state3 = aesdec<softAes>(state3, xkey0);
|
||||
|
||||
state0 = aesenc<softAes>(state0, xkey1);
|
||||
state1 = aesdec<softAes>(state1, xkey1);
|
||||
state2 = aesenc<softAes>(state2, xkey1);
|
||||
state3 = aesdec<softAes>(state3, xkey1);
|
||||
|
||||
//output hash
|
||||
rx_store_vec_i128((rx_vec_i128*)hash + 0, state0);
|
||||
rx_store_vec_i128((rx_vec_i128*)hash + 1, state1);
|
||||
rx_store_vec_i128((rx_vec_i128*)hash + 2, state2);
|
||||
rx_store_vec_i128((rx_vec_i128*)hash + 3, state3);
|
||||
}
|
||||
|
||||
template void hashAes1Rx4<false>(const void *input, size_t inputSize, void *hash);
|
||||
template void hashAes1Rx4<true>(const void *input, size_t inputSize, void *hash);
|
||||
|
||||
//AesGenerator1R:
|
||||
//key0, key1, key2, key3 = Blake2b-512("RandomX AesGenerator1R keys")
|
||||
|
||||
#define AES_GEN_1R_KEY0 0xb4f44917, 0xdbb5552b, 0x62716609, 0x6daca553
|
||||
#define AES_GEN_1R_KEY1 0x0da1dc4e, 0x1725d378, 0x846a710d, 0x6d7caf07
|
||||
#define AES_GEN_1R_KEY2 0x3e20e345, 0xf4c0794f, 0x9f947ec6, 0x3f1262f1
|
||||
#define AES_GEN_1R_KEY3 0x49169154, 0x16314c88, 0xb1ba317c, 0x6aef8135
|
||||
|
||||
/*
|
||||
Fill 'buffer' with pseudorandom data based on 512-bit 'state'.
|
||||
The state is encrypted using a single AES round per 16 bytes of output
|
||||
in 4 lanes.
|
||||
|
||||
'outputSize' must be a multiple of 64.
|
||||
|
||||
The modified state is written back to 'state' to allow multiple
|
||||
calls to this function.
|
||||
*/
|
||||
template<bool softAes>
|
||||
void fillAes1Rx4(void *state, size_t outputSize, void *buffer) {
|
||||
assert(outputSize % 64 == 0);
|
||||
const uint8_t* outptr = (uint8_t*)buffer;
|
||||
const uint8_t* outputEnd = outptr + outputSize;
|
||||
|
||||
rx_vec_i128 state0, state1, state2, state3;
|
||||
rx_vec_i128 key0, key1, key2, key3;
|
||||
|
||||
key0 = rx_set_int_vec_i128(AES_GEN_1R_KEY0);
|
||||
key1 = rx_set_int_vec_i128(AES_GEN_1R_KEY1);
|
||||
key2 = rx_set_int_vec_i128(AES_GEN_1R_KEY2);
|
||||
key3 = rx_set_int_vec_i128(AES_GEN_1R_KEY3);
|
||||
|
||||
state0 = rx_load_vec_i128((rx_vec_i128*)state + 0);
|
||||
state1 = rx_load_vec_i128((rx_vec_i128*)state + 1);
|
||||
state2 = rx_load_vec_i128((rx_vec_i128*)state + 2);
|
||||
state3 = rx_load_vec_i128((rx_vec_i128*)state + 3);
|
||||
|
||||
while (outptr < outputEnd) {
|
||||
state0 = aesdec<softAes>(state0, key0);
|
||||
state1 = aesenc<softAes>(state1, key1);
|
||||
state2 = aesdec<softAes>(state2, key2);
|
||||
state3 = aesenc<softAes>(state3, key3);
|
||||
|
||||
rx_store_vec_i128((rx_vec_i128*)outptr + 0, state0);
|
||||
rx_store_vec_i128((rx_vec_i128*)outptr + 1, state1);
|
||||
rx_store_vec_i128((rx_vec_i128*)outptr + 2, state2);
|
||||
rx_store_vec_i128((rx_vec_i128*)outptr + 3, state3);
|
||||
|
||||
outptr += 64;
|
||||
}
|
||||
|
||||
rx_store_vec_i128((rx_vec_i128*)state + 0, state0);
|
||||
rx_store_vec_i128((rx_vec_i128*)state + 1, state1);
|
||||
rx_store_vec_i128((rx_vec_i128*)state + 2, state2);
|
||||
rx_store_vec_i128((rx_vec_i128*)state + 3, state3);
|
||||
}
|
||||
|
||||
template void fillAes1Rx4<true>(void *state, size_t outputSize, void *buffer);
|
||||
template void fillAes1Rx4<false>(void *state, size_t outputSize, void *buffer);
|
||||
|
||||
//AesGenerator4R:
|
||||
//key0, key1, key2, key3 = Blake2b-512("RandomX AesGenerator4R keys 0-3")
|
||||
//key4, key5, key6, key7 = Blake2b-512("RandomX AesGenerator4R keys 4-7")
|
||||
|
||||
#define AES_GEN_4R_KEY0 0x99e5d23f, 0x2f546d2b, 0xd1833ddb, 0x6421aadd
|
||||
#define AES_GEN_4R_KEY1 0xa5dfcde5, 0x06f79d53, 0xb6913f55, 0xb20e3450
|
||||
#define AES_GEN_4R_KEY2 0x171c02bf, 0x0aa4679f, 0x515e7baf, 0x5c3ed904
|
||||
#define AES_GEN_4R_KEY3 0xd8ded291, 0xcd673785, 0xe78f5d08, 0x85623763
|
||||
#define AES_GEN_4R_KEY4 0x229effb4, 0x3d518b6d, 0xe3d6a7a6, 0xb5826f73
|
||||
#define AES_GEN_4R_KEY5 0xb272b7d2, 0xe9024d4e, 0x9c10b3d9, 0xc7566bf3
|
||||
#define AES_GEN_4R_KEY6 0xf63befa7, 0x2ba9660a, 0xf765a38b, 0xf273c9e7
|
||||
#define AES_GEN_4R_KEY7 0xc0b0762d, 0x0c06d1fd, 0x915839de, 0x7a7cd609
|
||||
|
||||
template<bool softAes>
|
||||
void fillAes4Rx4(void *state, size_t outputSize, void *buffer) {
|
||||
assert(outputSize % 64 == 0);
|
||||
const uint8_t* outptr = (uint8_t*)buffer;
|
||||
const uint8_t* outputEnd = outptr + outputSize;
|
||||
|
||||
rx_vec_i128 state0, state1, state2, state3;
|
||||
rx_vec_i128 key0, key1, key2, key3, key4, key5, key6, key7;
|
||||
|
||||
key0 = rx_set_int_vec_i128(AES_GEN_4R_KEY0);
|
||||
key1 = rx_set_int_vec_i128(AES_GEN_4R_KEY1);
|
||||
key2 = rx_set_int_vec_i128(AES_GEN_4R_KEY2);
|
||||
key3 = rx_set_int_vec_i128(AES_GEN_4R_KEY3);
|
||||
key4 = rx_set_int_vec_i128(AES_GEN_4R_KEY4);
|
||||
key5 = rx_set_int_vec_i128(AES_GEN_4R_KEY5);
|
||||
key6 = rx_set_int_vec_i128(AES_GEN_4R_KEY6);
|
||||
key7 = rx_set_int_vec_i128(AES_GEN_4R_KEY7);
|
||||
|
||||
state0 = rx_load_vec_i128((rx_vec_i128*)state + 0);
|
||||
state1 = rx_load_vec_i128((rx_vec_i128*)state + 1);
|
||||
state2 = rx_load_vec_i128((rx_vec_i128*)state + 2);
|
||||
state3 = rx_load_vec_i128((rx_vec_i128*)state + 3);
|
||||
|
||||
while (outptr < outputEnd) {
|
||||
state0 = aesdec<softAes>(state0, key0);
|
||||
state1 = aesenc<softAes>(state1, key0);
|
||||
state2 = aesdec<softAes>(state2, key4);
|
||||
state3 = aesenc<softAes>(state3, key4);
|
||||
|
||||
state0 = aesdec<softAes>(state0, key1);
|
||||
state1 = aesenc<softAes>(state1, key1);
|
||||
state2 = aesdec<softAes>(state2, key5);
|
||||
state3 = aesenc<softAes>(state3, key5);
|
||||
|
||||
state0 = aesdec<softAes>(state0, key2);
|
||||
state1 = aesenc<softAes>(state1, key2);
|
||||
state2 = aesdec<softAes>(state2, key6);
|
||||
state3 = aesenc<softAes>(state3, key6);
|
||||
|
||||
state0 = aesdec<softAes>(state0, key3);
|
||||
state1 = aesenc<softAes>(state1, key3);
|
||||
state2 = aesdec<softAes>(state2, key7);
|
||||
state3 = aesenc<softAes>(state3, key7);
|
||||
|
||||
rx_store_vec_i128((rx_vec_i128*)outptr + 0, state0);
|
||||
rx_store_vec_i128((rx_vec_i128*)outptr + 1, state1);
|
||||
rx_store_vec_i128((rx_vec_i128*)outptr + 2, state2);
|
||||
rx_store_vec_i128((rx_vec_i128*)outptr + 3, state3);
|
||||
|
||||
outptr += 64;
|
||||
}
|
||||
}
|
||||
|
||||
template void fillAes4Rx4<true>(void *state, size_t outputSize, void *buffer);
|
||||
template void fillAes4Rx4<false>(void *state, size_t outputSize, void *buffer);
|
||||
|
||||
template<bool softAes>
|
||||
void hashAndFillAes1Rx4(void *scratchpad, size_t scratchpadSize, void *hash, void* fill_state) {
|
||||
uint8_t* scratchpadPtr = (uint8_t*)scratchpad;
|
||||
const uint8_t* scratchpadEnd = scratchpadPtr + scratchpadSize;
|
||||
|
||||
// initial state
|
||||
rx_vec_i128 hash_state0 = rx_set_int_vec_i128(AES_HASH_1R_STATE0);
|
||||
rx_vec_i128 hash_state1 = rx_set_int_vec_i128(AES_HASH_1R_STATE1);
|
||||
rx_vec_i128 hash_state2 = rx_set_int_vec_i128(AES_HASH_1R_STATE2);
|
||||
rx_vec_i128 hash_state3 = rx_set_int_vec_i128(AES_HASH_1R_STATE3);
|
||||
|
||||
const rx_vec_i128 key0 = rx_set_int_vec_i128(AES_GEN_1R_KEY0);
|
||||
const rx_vec_i128 key1 = rx_set_int_vec_i128(AES_GEN_1R_KEY1);
|
||||
const rx_vec_i128 key2 = rx_set_int_vec_i128(AES_GEN_1R_KEY2);
|
||||
const rx_vec_i128 key3 = rx_set_int_vec_i128(AES_GEN_1R_KEY3);
|
||||
|
||||
rx_vec_i128 fill_state0 = rx_load_vec_i128((rx_vec_i128*)fill_state + 0);
|
||||
rx_vec_i128 fill_state1 = rx_load_vec_i128((rx_vec_i128*)fill_state + 1);
|
||||
rx_vec_i128 fill_state2 = rx_load_vec_i128((rx_vec_i128*)fill_state + 2);
|
||||
rx_vec_i128 fill_state3 = rx_load_vec_i128((rx_vec_i128*)fill_state + 3);
|
||||
|
||||
constexpr int PREFETCH_DISTANCE = 4096;
|
||||
const char* prefetchPtr = ((const char*)scratchpad) + PREFETCH_DISTANCE;
|
||||
scratchpadEnd -= PREFETCH_DISTANCE;
|
||||
|
||||
for (int i = 0; i < 2; ++i) {
|
||||
//process 64 bytes at a time in 4 lanes
|
||||
while (scratchpadPtr < scratchpadEnd) {
|
||||
hash_state0 = aesenc<softAes>(hash_state0, rx_load_vec_i128((rx_vec_i128*)scratchpadPtr + 0));
|
||||
hash_state1 = aesdec<softAes>(hash_state1, rx_load_vec_i128((rx_vec_i128*)scratchpadPtr + 1));
|
||||
hash_state2 = aesenc<softAes>(hash_state2, rx_load_vec_i128((rx_vec_i128*)scratchpadPtr + 2));
|
||||
hash_state3 = aesdec<softAes>(hash_state3, rx_load_vec_i128((rx_vec_i128*)scratchpadPtr + 3));
|
||||
|
||||
fill_state0 = aesdec<softAes>(fill_state0, key0);
|
||||
fill_state1 = aesenc<softAes>(fill_state1, key1);
|
||||
fill_state2 = aesdec<softAes>(fill_state2, key2);
|
||||
fill_state3 = aesenc<softAes>(fill_state3, key3);
|
||||
|
||||
rx_store_vec_i128((rx_vec_i128*)scratchpadPtr + 0, fill_state0);
|
||||
rx_store_vec_i128((rx_vec_i128*)scratchpadPtr + 1, fill_state1);
|
||||
rx_store_vec_i128((rx_vec_i128*)scratchpadPtr + 2, fill_state2);
|
||||
rx_store_vec_i128((rx_vec_i128*)scratchpadPtr + 3, fill_state3);
|
||||
|
||||
rx_prefetch_t0(prefetchPtr);
|
||||
|
||||
scratchpadPtr += 64;
|
||||
prefetchPtr += 64;
|
||||
}
|
||||
prefetchPtr = (const char*) scratchpad;
|
||||
scratchpadEnd += PREFETCH_DISTANCE;
|
||||
}
|
||||
|
||||
rx_store_vec_i128((rx_vec_i128*)fill_state + 0, fill_state0);
|
||||
rx_store_vec_i128((rx_vec_i128*)fill_state + 1, fill_state1);
|
||||
rx_store_vec_i128((rx_vec_i128*)fill_state + 2, fill_state2);
|
||||
rx_store_vec_i128((rx_vec_i128*)fill_state + 3, fill_state3);
|
||||
|
||||
//two extra rounds to achieve full diffusion
|
||||
rx_vec_i128 xkey0 = rx_set_int_vec_i128(AES_HASH_1R_XKEY0);
|
||||
rx_vec_i128 xkey1 = rx_set_int_vec_i128(AES_HASH_1R_XKEY1);
|
||||
|
||||
hash_state0 = aesenc<softAes>(hash_state0, xkey0);
|
||||
hash_state1 = aesdec<softAes>(hash_state1, xkey0);
|
||||
hash_state2 = aesenc<softAes>(hash_state2, xkey0);
|
||||
hash_state3 = aesdec<softAes>(hash_state3, xkey0);
|
||||
|
||||
hash_state0 = aesenc<softAes>(hash_state0, xkey1);
|
||||
hash_state1 = aesdec<softAes>(hash_state1, xkey1);
|
||||
hash_state2 = aesenc<softAes>(hash_state2, xkey1);
|
||||
hash_state3 = aesdec<softAes>(hash_state3, xkey1);
|
||||
|
||||
//output hash
|
||||
rx_store_vec_i128((rx_vec_i128*)hash + 0, hash_state0);
|
||||
rx_store_vec_i128((rx_vec_i128*)hash + 1, hash_state1);
|
||||
rx_store_vec_i128((rx_vec_i128*)hash + 2, hash_state2);
|
||||
rx_store_vec_i128((rx_vec_i128*)hash + 3, hash_state3);
|
||||
}
|
||||
|
||||
template void hashAndFillAes1Rx4<false>(void *scratchpad, size_t scratchpadSize, void *hash, void* fill_state);
|
||||
template void hashAndFillAes1Rx4<true>(void *scratchpad, size_t scratchpadSize, void *hash, void* fill_state);
|
||||
43
app/src/main/cpp/randomx/src/aes_hash.hpp
Normal file
43
app/src/main/cpp/randomx/src/aes_hash.hpp
Normal file
|
|
@ -0,0 +1,43 @@
|
|||
/*
|
||||
Copyright (c) 2018-2019, tevador <tevador@gmail.com>
|
||||
|
||||
All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted provided that the following conditions are met:
|
||||
* Redistributions of source code must retain the above copyright
|
||||
notice, this list of conditions and the following disclaimer.
|
||||
* Redistributions in binary form must reproduce the above copyright
|
||||
notice, this list of conditions and the following disclaimer in the
|
||||
documentation and/or other materials provided with the distribution.
|
||||
* Neither the name of the copyright holder nor the
|
||||
names of its contributors may be used to endorse or promote products
|
||||
derived from this software without specific prior written permission.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
|
||||
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
|
||||
FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
||||
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
|
||||
CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
|
||||
OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <cstddef>
|
||||
|
||||
template<bool softAes>
|
||||
void hashAes1Rx4(const void *input, size_t inputSize, void *hash);
|
||||
|
||||
template<bool softAes>
|
||||
void fillAes1Rx4(void *state, size_t outputSize, void *buffer);
|
||||
|
||||
template<bool softAes>
|
||||
void fillAes4Rx4(void *state, size_t outputSize, void *buffer);
|
||||
|
||||
template<bool softAes>
|
||||
void hashAndFillAes1Rx4(void *scratchpad, size_t scratchpadSize, void *hash, void* fill_state);
|
||||
63
app/src/main/cpp/randomx/src/allocator.cpp
Normal file
63
app/src/main/cpp/randomx/src/allocator.cpp
Normal file
|
|
@ -0,0 +1,63 @@
|
|||
/*
|
||||
Copyright (c) 2018-2019, tevador <tevador@gmail.com>
|
||||
|
||||
All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted provided that the following conditions are met:
|
||||
* Redistributions of source code must retain the above copyright
|
||||
notice, this list of conditions and the following disclaimer.
|
||||
* Redistributions in binary form must reproduce the above copyright
|
||||
notice, this list of conditions and the following disclaimer in the
|
||||
documentation and/or other materials provided with the distribution.
|
||||
* Neither the name of the copyright holder nor the
|
||||
names of its contributors may be used to endorse or promote products
|
||||
derived from this software without specific prior written permission.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
|
||||
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
|
||||
FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
||||
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
|
||||
CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
|
||||
OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
#include <new>
|
||||
#include "allocator.hpp"
|
||||
#include "intrin_portable.h"
|
||||
#include "virtual_memory.h"
|
||||
#include "common.hpp"
|
||||
|
||||
namespace randomx {
|
||||
|
||||
template<size_t alignment>
|
||||
void* AlignedAllocator<alignment>::allocMemory(size_t count) {
|
||||
void *mem = rx_aligned_alloc(count, alignment);
|
||||
if (mem == nullptr)
|
||||
throw std::bad_alloc();
|
||||
return mem;
|
||||
}
|
||||
|
||||
template<size_t alignment>
|
||||
void AlignedAllocator<alignment>::freeMemory(void* ptr, size_t count) {
|
||||
rx_aligned_free(ptr);
|
||||
}
|
||||
|
||||
template struct AlignedAllocator<CacheLineSize>;
|
||||
|
||||
void* LargePageAllocator::allocMemory(size_t count) {
|
||||
void *mem = allocLargePagesMemory(count);
|
||||
if (mem == nullptr)
|
||||
throw std::bad_alloc();
|
||||
return mem;
|
||||
}
|
||||
|
||||
void LargePageAllocator::freeMemory(void* ptr, size_t count) {
|
||||
freePagedMemory(ptr, count);
|
||||
};
|
||||
|
||||
}
|
||||
46
app/src/main/cpp/randomx/src/allocator.hpp
Normal file
46
app/src/main/cpp/randomx/src/allocator.hpp
Normal file
|
|
@ -0,0 +1,46 @@
|
|||
/*
|
||||
Copyright (c) 2018-2019, tevador <tevador@gmail.com>
|
||||
|
||||
All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted provided that the following conditions are met:
|
||||
* Redistributions of source code must retain the above copyright
|
||||
notice, this list of conditions and the following disclaimer.
|
||||
* Redistributions in binary form must reproduce the above copyright
|
||||
notice, this list of conditions and the following disclaimer in the
|
||||
documentation and/or other materials provided with the distribution.
|
||||
* Neither the name of the copyright holder nor the
|
||||
names of its contributors may be used to endorse or promote products
|
||||
derived from this software without specific prior written permission.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
|
||||
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
|
||||
FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
||||
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
|
||||
CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
|
||||
OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <cstddef>
|
||||
|
||||
namespace randomx {
|
||||
|
||||
template<size_t alignment>
|
||||
struct AlignedAllocator {
|
||||
static void* allocMemory(size_t);
|
||||
static void freeMemory(void*, size_t);
|
||||
};
|
||||
|
||||
struct LargePageAllocator {
|
||||
static void* allocMemory(size_t);
|
||||
static void freeMemory(void*, size_t);
|
||||
};
|
||||
|
||||
}
|
||||
261
app/src/main/cpp/randomx/src/argon2.h
Normal file
261
app/src/main/cpp/randomx/src/argon2.h
Normal file
|
|
@ -0,0 +1,261 @@
|
|||
/*
|
||||
Copyright (c) 2018-2019, tevador <tevador@gmail.com>
|
||||
|
||||
All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted provided that the following conditions are met:
|
||||
* Redistributions of source code must retain the above copyright
|
||||
notice, this list of conditions and the following disclaimer.
|
||||
* Redistributions in binary form must reproduce the above copyright
|
||||
notice, this list of conditions and the following disclaimer in the
|
||||
documentation and/or other materials provided with the distribution.
|
||||
* Neither the name of the copyright holder nor the
|
||||
names of its contributors may be used to endorse or promote products
|
||||
derived from this software without specific prior written permission.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
|
||||
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
|
||||
FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
||||
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
|
||||
CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
|
||||
OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
/* Original code from Argon2 reference source code package used under CC0 Licence
|
||||
* https://github.com/P-H-C/phc-winner-argon2
|
||||
* Copyright 2015
|
||||
* Daniel Dinu, Dmitry Khovratovich, Jean-Philippe Aumasson, and Samuel Neves
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <stdint.h>
|
||||
#include <stddef.h>
|
||||
#include <limits.h>
|
||||
|
||||
/*
|
||||
* Argon2 input parameter restrictions
|
||||
*/
|
||||
|
||||
/* Minimum and maximum number of lanes (degree of parallelism) */
|
||||
#define ARGON2_MIN_LANES UINT32_C(1)
|
||||
#define ARGON2_MAX_LANES UINT32_C(0xFFFFFF)
|
||||
|
||||
/* Minimum and maximum number of threads */
|
||||
#define ARGON2_MIN_THREADS UINT32_C(1)
|
||||
#define ARGON2_MAX_THREADS UINT32_C(0xFFFFFF)
|
||||
|
||||
/* Number of synchronization points between lanes per pass */
|
||||
#define ARGON2_SYNC_POINTS UINT32_C(4)
|
||||
|
||||
/* Minimum and maximum digest size in bytes */
|
||||
#define ARGON2_MIN_OUTLEN UINT32_C(4)
|
||||
#define ARGON2_MAX_OUTLEN UINT32_C(0xFFFFFFFF)
|
||||
|
||||
/* Minimum and maximum number of memory blocks (each of BLOCK_SIZE bytes) */
|
||||
#define ARGON2_MIN_MEMORY (2 * ARGON2_SYNC_POINTS) /* 2 blocks per slice */
|
||||
|
||||
#define ARGON2_MIN(a, b) ((a) < (b) ? (a) : (b))
|
||||
/* Max memory size is addressing-space/2, topping at 2^32 blocks (4 TB) */
|
||||
#define ARGON2_MAX_MEMORY_BITS \
|
||||
ARGON2_MIN(UINT32_C(32), (sizeof(void *) * CHAR_BIT - 10 - 1))
|
||||
#define ARGON2_MAX_MEMORY \
|
||||
ARGON2_MIN(UINT32_C(0xFFFFFFFF), UINT64_C(1) << ARGON2_MAX_MEMORY_BITS)
|
||||
|
||||
/* Minimum and maximum number of passes */
|
||||
#define ARGON2_MIN_TIME UINT32_C(1)
|
||||
#define ARGON2_MAX_TIME UINT32_C(0xFFFFFFFF)
|
||||
|
||||
/* Minimum and maximum password length in bytes */
|
||||
#define ARGON2_MIN_PWD_LENGTH UINT32_C(0)
|
||||
#define ARGON2_MAX_PWD_LENGTH UINT32_C(0xFFFFFFFF)
|
||||
|
||||
/* Minimum and maximum associated data length in bytes */
|
||||
#define ARGON2_MIN_AD_LENGTH UINT32_C(0)
|
||||
#define ARGON2_MAX_AD_LENGTH UINT32_C(0xFFFFFFFF)
|
||||
|
||||
/* Minimum and maximum salt length in bytes */
|
||||
#define ARGON2_MIN_SALT_LENGTH UINT32_C(8)
|
||||
#define ARGON2_MAX_SALT_LENGTH UINT32_C(0xFFFFFFFF)
|
||||
|
||||
/* Minimum and maximum key length in bytes */
|
||||
#define ARGON2_MIN_SECRET UINT32_C(0)
|
||||
#define ARGON2_MAX_SECRET UINT32_C(0xFFFFFFFF)
|
||||
|
||||
/* Flags to determine which fields are securely wiped (default = no wipe). */
|
||||
#define ARGON2_DEFAULT_FLAGS UINT32_C(0)
|
||||
#define ARGON2_FLAG_CLEAR_PASSWORD (UINT32_C(1) << 0)
|
||||
#define ARGON2_FLAG_CLEAR_SECRET (UINT32_C(1) << 1)
|
||||
|
||||
|
||||
/* Error codes */
|
||||
typedef enum Argon2_ErrorCodes {
|
||||
ARGON2_OK = 0,
|
||||
|
||||
ARGON2_OUTPUT_PTR_NULL = -1,
|
||||
|
||||
ARGON2_OUTPUT_TOO_SHORT = -2,
|
||||
ARGON2_OUTPUT_TOO_LONG = -3,
|
||||
|
||||
ARGON2_PWD_TOO_SHORT = -4,
|
||||
ARGON2_PWD_TOO_LONG = -5,
|
||||
|
||||
ARGON2_SALT_TOO_SHORT = -6,
|
||||
ARGON2_SALT_TOO_LONG = -7,
|
||||
|
||||
ARGON2_AD_TOO_SHORT = -8,
|
||||
ARGON2_AD_TOO_LONG = -9,
|
||||
|
||||
ARGON2_SECRET_TOO_SHORT = -10,
|
||||
ARGON2_SECRET_TOO_LONG = -11,
|
||||
|
||||
ARGON2_TIME_TOO_SMALL = -12,
|
||||
ARGON2_TIME_TOO_LARGE = -13,
|
||||
|
||||
ARGON2_MEMORY_TOO_LITTLE = -14,
|
||||
ARGON2_MEMORY_TOO_MUCH = -15,
|
||||
|
||||
ARGON2_LANES_TOO_FEW = -16,
|
||||
ARGON2_LANES_TOO_MANY = -17,
|
||||
|
||||
ARGON2_PWD_PTR_MISMATCH = -18, /* NULL ptr with non-zero length */
|
||||
ARGON2_SALT_PTR_MISMATCH = -19, /* NULL ptr with non-zero length */
|
||||
ARGON2_SECRET_PTR_MISMATCH = -20, /* NULL ptr with non-zero length */
|
||||
ARGON2_AD_PTR_MISMATCH = -21, /* NULL ptr with non-zero length */
|
||||
|
||||
ARGON2_MEMORY_ALLOCATION_ERROR = -22,
|
||||
|
||||
ARGON2_FREE_MEMORY_CBK_NULL = -23,
|
||||
ARGON2_ALLOCATE_MEMORY_CBK_NULL = -24,
|
||||
|
||||
ARGON2_INCORRECT_PARAMETER = -25,
|
||||
ARGON2_INCORRECT_TYPE = -26,
|
||||
|
||||
ARGON2_OUT_PTR_MISMATCH = -27,
|
||||
|
||||
ARGON2_THREADS_TOO_FEW = -28,
|
||||
ARGON2_THREADS_TOO_MANY = -29,
|
||||
|
||||
ARGON2_MISSING_ARGS = -30,
|
||||
|
||||
ARGON2_ENCODING_FAIL = -31,
|
||||
|
||||
ARGON2_DECODING_FAIL = -32,
|
||||
|
||||
ARGON2_THREAD_FAIL = -33,
|
||||
|
||||
ARGON2_DECODING_LENGTH_FAIL = -34,
|
||||
|
||||
ARGON2_VERIFY_MISMATCH = -35
|
||||
} argon2_error_codes;
|
||||
|
||||
/* Memory allocator types --- for external allocation */
|
||||
typedef int(*allocate_fptr)(uint8_t **memory, size_t bytes_to_allocate);
|
||||
typedef void(*deallocate_fptr)(uint8_t *memory, size_t bytes_to_allocate);
|
||||
|
||||
/* Argon2 external data structures */
|
||||
|
||||
/*
|
||||
*****
|
||||
* Context: structure to hold Argon2 inputs:
|
||||
* output array and its length,
|
||||
* password and its length,
|
||||
* salt and its length,
|
||||
* secret and its length,
|
||||
* associated data and its length,
|
||||
* number of passes, amount of used memory (in KBytes, can be rounded up a bit)
|
||||
* number of parallel threads that will be run.
|
||||
* All the parameters above affect the output hash value.
|
||||
* Additionally, two function pointers can be provided to allocate and
|
||||
* deallocate the memory (if NULL, memory will be allocated internally).
|
||||
* Also, three flags indicate whether to erase password, secret as soon as they
|
||||
* are pre-hashed (and thus not needed anymore), and the entire memory
|
||||
*****
|
||||
* Simplest situation: you have output array out[8], password is stored in
|
||||
* pwd[32], salt is stored in salt[16], you do not have keys nor associated
|
||||
* data. You need to spend 1 GB of RAM and you run 5 passes of Argon2d with
|
||||
* 4 parallel lanes.
|
||||
* You want to erase the password, but you're OK with last pass not being
|
||||
* erased. You want to use the default memory allocator.
|
||||
* Then you initialize:
|
||||
Argon2_Context(out,8,pwd,32,salt,16,NULL,0,NULL,0,5,1<<20,4,4,NULL,NULL,true,false,false,false)
|
||||
*/
|
||||
typedef struct Argon2_Context {
|
||||
uint8_t *out; /* output array */
|
||||
uint32_t outlen; /* digest length */
|
||||
|
||||
uint8_t *pwd; /* password array */
|
||||
uint32_t pwdlen; /* password length */
|
||||
|
||||
uint8_t *salt; /* salt array */
|
||||
uint32_t saltlen; /* salt length */
|
||||
|
||||
uint8_t *secret; /* key array */
|
||||
uint32_t secretlen; /* key length */
|
||||
|
||||
uint8_t *ad; /* associated data array */
|
||||
uint32_t adlen; /* associated data length */
|
||||
|
||||
uint32_t t_cost; /* number of passes */
|
||||
uint32_t m_cost; /* amount of memory requested (KB) */
|
||||
uint32_t lanes; /* number of lanes */
|
||||
uint32_t threads; /* maximum number of threads */
|
||||
|
||||
uint32_t version; /* version number */
|
||||
|
||||
allocate_fptr allocate_cbk; /* pointer to memory allocator */
|
||||
deallocate_fptr free_cbk; /* pointer to memory deallocator */
|
||||
|
||||
uint32_t flags; /* array of bool options */
|
||||
} argon2_context;
|
||||
|
||||
/* Argon2 primitive type */
|
||||
typedef enum Argon2_type {
|
||||
Argon2_d = 0,
|
||||
Argon2_i = 1,
|
||||
Argon2_id = 2
|
||||
} argon2_type;
|
||||
|
||||
/* Version of the algorithm */
|
||||
typedef enum Argon2_version {
|
||||
ARGON2_VERSION_10 = 0x10,
|
||||
ARGON2_VERSION_13 = 0x13,
|
||||
ARGON2_VERSION_NUMBER = ARGON2_VERSION_13
|
||||
} argon2_version;
|
||||
|
||||
//Argon2 instance - forward declaration
|
||||
typedef struct Argon2_instance_t argon2_instance_t;
|
||||
|
||||
//Argon2 position = forward declaration
|
||||
typedef struct Argon2_position_t argon2_position_t;
|
||||
|
||||
//Argon2 implementation function
|
||||
typedef void randomx_argon2_impl(const argon2_instance_t* instance,
|
||||
argon2_position_t position);
|
||||
|
||||
#if defined(__cplusplus)
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/*
|
||||
* Function that fills the segment using previous segments also from other
|
||||
* threads
|
||||
* @param context current context
|
||||
* @param instance Pointer to the current instance
|
||||
* @param position Current position
|
||||
* @pre all block pointers must be valid
|
||||
*/
|
||||
void randomx_argon2_fill_segment_ref(const argon2_instance_t* instance,
|
||||
argon2_position_t position);
|
||||
|
||||
randomx_argon2_impl *randomx_argon2_impl_ssse3();
|
||||
randomx_argon2_impl *randomx_argon2_impl_avx2();
|
||||
|
||||
#if defined(__cplusplus)
|
||||
}
|
||||
#endif
|
||||
174
app/src/main/cpp/randomx/src/argon2_avx2.c
Normal file
174
app/src/main/cpp/randomx/src/argon2_avx2.c
Normal file
|
|
@ -0,0 +1,174 @@
|
|||
/*
|
||||
Copyright (c) 2018-2019, tevador <tevador@gmail.com>
|
||||
|
||||
All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted provided that the following conditions are met:
|
||||
* Redistributions of source code must retain the above copyright
|
||||
notice, this list of conditions and the following disclaimer.
|
||||
* Redistributions in binary form must reproduce the above copyright
|
||||
notice, this list of conditions and the following disclaimer in the
|
||||
documentation and/or other materials provided with the distribution.
|
||||
* Neither the name of the copyright holder nor the
|
||||
names of its contributors may be used to endorse or promote products
|
||||
derived from this software without specific prior written permission.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
|
||||
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
|
||||
FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
||||
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
|
||||
CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
|
||||
OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
/* Original code from Argon2 reference source code package used under CC0 Licence
|
||||
* https://github.com/P-H-C/phc-winner-argon2
|
||||
* Copyright 2015
|
||||
* Daniel Dinu, Dmitry Khovratovich, Jean-Philippe Aumasson, and Samuel Neves
|
||||
*/
|
||||
|
||||
#include <stdint.h>
|
||||
#include <string.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
#include "argon2.h"
|
||||
|
||||
void randomx_argon2_fill_segment_avx2(const argon2_instance_t* instance,
|
||||
argon2_position_t position);
|
||||
|
||||
randomx_argon2_impl* randomx_argon2_impl_avx2() {
|
||||
#if defined(__AVX2__)
|
||||
return &randomx_argon2_fill_segment_avx2;
|
||||
#endif
|
||||
return NULL;
|
||||
}
|
||||
|
||||
#if defined(__AVX2__)
|
||||
|
||||
#include "argon2_core.h"
|
||||
|
||||
#include "blake2/blamka-round-avx2.h"
|
||||
#include "blake2/blake2-impl.h"
|
||||
#include "blake2/blake2.h"
|
||||
|
||||
static void fill_block(__m256i* state, const block* ref_block,
|
||||
block* next_block, int with_xor) {
|
||||
__m256i block_XY[ARGON2_HWORDS_IN_BLOCK];
|
||||
unsigned int i;
|
||||
|
||||
if (with_xor) {
|
||||
for (i = 0; i < ARGON2_HWORDS_IN_BLOCK; i++) {
|
||||
state[i] = _mm256_xor_si256(
|
||||
state[i], _mm256_loadu_si256((const __m256i*)ref_block->v + i));
|
||||
block_XY[i] = _mm256_xor_si256(
|
||||
state[i], _mm256_loadu_si256((const __m256i*)next_block->v + i));
|
||||
}
|
||||
}
|
||||
else {
|
||||
for (i = 0; i < ARGON2_HWORDS_IN_BLOCK; i++) {
|
||||
block_XY[i] = state[i] = _mm256_xor_si256(
|
||||
state[i], _mm256_loadu_si256((const __m256i*)ref_block->v + i));
|
||||
}
|
||||
}
|
||||
|
||||
for (i = 0; i < 4; ++i) {
|
||||
BLAKE2_ROUND_1(state[8 * i + 0], state[8 * i + 4], state[8 * i + 1], state[8 * i + 5],
|
||||
state[8 * i + 2], state[8 * i + 6], state[8 * i + 3], state[8 * i + 7]);
|
||||
}
|
||||
|
||||
for (i = 0; i < 4; ++i) {
|
||||
BLAKE2_ROUND_2(state[0 + i], state[4 + i], state[8 + i], state[12 + i],
|
||||
state[16 + i], state[20 + i], state[24 + i], state[28 + i]);
|
||||
}
|
||||
|
||||
for (i = 0; i < ARGON2_HWORDS_IN_BLOCK; i++) {
|
||||
state[i] = _mm256_xor_si256(state[i], block_XY[i]);
|
||||
_mm256_storeu_si256((__m256i*)next_block->v + i, state[i]);
|
||||
}
|
||||
}
|
||||
|
||||
void randomx_argon2_fill_segment_avx2(const argon2_instance_t* instance,
|
||||
argon2_position_t position) {
|
||||
block* ref_block = NULL, * curr_block = NULL;
|
||||
block address_block, input_block;
|
||||
uint64_t pseudo_rand, ref_index, ref_lane;
|
||||
uint32_t prev_offset, curr_offset;
|
||||
uint32_t starting_index, i;
|
||||
__m256i state[ARGON2_HWORDS_IN_BLOCK];
|
||||
|
||||
if (instance == NULL) {
|
||||
return;
|
||||
}
|
||||
|
||||
starting_index = 0;
|
||||
|
||||
if ((0 == position.pass) && (0 == position.slice)) {
|
||||
starting_index = 2; /* we have already generated the first two blocks */
|
||||
}
|
||||
|
||||
/* Offset of the current block */
|
||||
curr_offset = position.lane * instance->lane_length +
|
||||
position.slice * instance->segment_length + starting_index;
|
||||
|
||||
if (0 == curr_offset % instance->lane_length) {
|
||||
/* Last block in this lane */
|
||||
prev_offset = curr_offset + instance->lane_length - 1;
|
||||
}
|
||||
else {
|
||||
/* Previous block */
|
||||
prev_offset = curr_offset - 1;
|
||||
}
|
||||
|
||||
memcpy(state, ((instance->memory + prev_offset)->v), ARGON2_BLOCK_SIZE);
|
||||
|
||||
for (i = starting_index; i < instance->segment_length;
|
||||
++i, ++curr_offset, ++prev_offset) {
|
||||
/*1.1 Rotating prev_offset if needed */
|
||||
if (curr_offset % instance->lane_length == 1) {
|
||||
prev_offset = curr_offset - 1;
|
||||
}
|
||||
|
||||
/* 1.2 Computing the index of the reference block */
|
||||
/* 1.2.1 Taking pseudo-random value from the previous block */
|
||||
pseudo_rand = instance->memory[prev_offset].v[0];
|
||||
|
||||
/* 1.2.2 Computing the lane of the reference block */
|
||||
ref_lane = ((pseudo_rand >> 32)) % instance->lanes;
|
||||
|
||||
if ((position.pass == 0) && (position.slice == 0)) {
|
||||
/* Can not reference other lanes yet */
|
||||
ref_lane = position.lane;
|
||||
}
|
||||
|
||||
/* 1.2.3 Computing the number of possible reference block within the
|
||||
* lane.
|
||||
*/
|
||||
position.index = i;
|
||||
ref_index = randomx_argon2_index_alpha(instance, &position, pseudo_rand & 0xFFFFFFFF,
|
||||
ref_lane == position.lane);
|
||||
|
||||
/* 2 Creating a new block */
|
||||
ref_block =
|
||||
instance->memory + instance->lane_length * ref_lane + ref_index;
|
||||
curr_block = instance->memory + curr_offset;
|
||||
if (ARGON2_VERSION_10 == instance->version) {
|
||||
/* version 1.2.1 and earlier: overwrite, not XOR */
|
||||
fill_block(state, ref_block, curr_block, 0);
|
||||
}
|
||||
else {
|
||||
if (0 == position.pass) {
|
||||
fill_block(state, ref_block, curr_block, 0);
|
||||
}
|
||||
else {
|
||||
fill_block(state, ref_block, curr_block, 1);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
411
app/src/main/cpp/randomx/src/argon2_core.c
Normal file
411
app/src/main/cpp/randomx/src/argon2_core.c
Normal file
|
|
@ -0,0 +1,411 @@
|
|||
/*
|
||||
Copyright (c) 2018-2019, tevador <tevador@gmail.com>
|
||||
|
||||
All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted provided that the following conditions are met:
|
||||
* Redistributions of source code must retain the above copyright
|
||||
notice, this list of conditions and the following disclaimer.
|
||||
* Redistributions in binary form must reproduce the above copyright
|
||||
notice, this list of conditions and the following disclaimer in the
|
||||
documentation and/or other materials provided with the distribution.
|
||||
* Neither the name of the copyright holder nor the
|
||||
names of its contributors may be used to endorse or promote products
|
||||
derived from this software without specific prior written permission.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
|
||||
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
|
||||
FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
||||
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
|
||||
CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
|
||||
OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
/* Original code from Argon2 reference source code package used under CC0 Licence
|
||||
* https://github.com/P-H-C/phc-winner-argon2
|
||||
* Copyright 2015
|
||||
* Daniel Dinu, Dmitry Khovratovich, Jean-Philippe Aumasson, and Samuel Neves
|
||||
*/
|
||||
|
||||
/*For memory wiping*/
|
||||
#ifdef _MSC_VER
|
||||
#include <windows.h>
|
||||
#include <winbase.h> /* For SecureZeroMemory */
|
||||
#endif
|
||||
#if defined __STDC_LIB_EXT1__
|
||||
#define __STDC_WANT_LIB_EXT1__ 1
|
||||
#endif
|
||||
#define VC_GE_2005(version) (version >= 1400)
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "argon2_core.h"
|
||||
#include "blake2/blake2.h"
|
||||
#include "blake2/blake2-impl.h"
|
||||
|
||||
#ifdef GENKAT
|
||||
#include "genkat.h"
|
||||
#endif
|
||||
|
||||
#if defined(__clang__)
|
||||
#if __has_attribute(optnone)
|
||||
#define NOT_OPTIMIZED __attribute__((optnone))
|
||||
#endif
|
||||
#elif defined(__GNUC__)
|
||||
#define GCC_VERSION \
|
||||
(__GNUC__ * 10000 + __GNUC_MINOR__ * 100 + __GNUC_PATCHLEVEL__)
|
||||
#if GCC_VERSION >= 40400
|
||||
#define NOT_OPTIMIZED __attribute__((optimize("O0")))
|
||||
#endif
|
||||
#endif
|
||||
#ifndef NOT_OPTIMIZED
|
||||
#define NOT_OPTIMIZED
|
||||
#endif
|
||||
|
||||
/***************Instance and Position constructors**********/
|
||||
|
||||
static void load_block(block *dst, const void *input) {
|
||||
unsigned i;
|
||||
for (i = 0; i < ARGON2_QWORDS_IN_BLOCK; ++i) {
|
||||
dst->v[i] = load64((const uint8_t *)input + i * sizeof(dst->v[i]));
|
||||
}
|
||||
}
|
||||
|
||||
static void store_block(void *output, const block *src) {
|
||||
unsigned i;
|
||||
for (i = 0; i < ARGON2_QWORDS_IN_BLOCK; ++i) {
|
||||
store64((uint8_t *)output + i * sizeof(src->v[i]), src->v[i]);
|
||||
}
|
||||
}
|
||||
|
||||
uint32_t randomx_argon2_index_alpha(const argon2_instance_t *instance,
|
||||
const argon2_position_t *position, uint32_t pseudo_rand,
|
||||
int same_lane) {
|
||||
/*
|
||||
* Pass 0:
|
||||
* This lane : all already finished segments plus already constructed
|
||||
* blocks in this segment
|
||||
* Other lanes : all already finished segments
|
||||
* Pass 1+:
|
||||
* This lane : (SYNC_POINTS - 1) last segments plus already constructed
|
||||
* blocks in this segment
|
||||
* Other lanes : (SYNC_POINTS - 1) last segments
|
||||
*/
|
||||
uint32_t reference_area_size;
|
||||
uint64_t relative_position;
|
||||
uint32_t start_position, absolute_position;
|
||||
|
||||
if (0 == position->pass) {
|
||||
/* First pass */
|
||||
if (0 == position->slice) {
|
||||
/* First slice */
|
||||
reference_area_size =
|
||||
position->index - 1; /* all but the previous */
|
||||
}
|
||||
else {
|
||||
if (same_lane) {
|
||||
/* The same lane => add current segment */
|
||||
reference_area_size =
|
||||
position->slice * instance->segment_length +
|
||||
position->index - 1;
|
||||
}
|
||||
else {
|
||||
reference_area_size =
|
||||
position->slice * instance->segment_length +
|
||||
((position->index == 0) ? (-1) : 0);
|
||||
}
|
||||
}
|
||||
}
|
||||
else {
|
||||
/* Second pass */
|
||||
if (same_lane) {
|
||||
reference_area_size = instance->lane_length -
|
||||
instance->segment_length + position->index -
|
||||
1;
|
||||
}
|
||||
else {
|
||||
reference_area_size = instance->lane_length -
|
||||
instance->segment_length +
|
||||
((position->index == 0) ? (-1) : 0);
|
||||
}
|
||||
}
|
||||
|
||||
/* 1.2.4. Mapping pseudo_rand to 0..<reference_area_size-1> and produce
|
||||
* relative position */
|
||||
relative_position = pseudo_rand;
|
||||
relative_position = relative_position * relative_position >> 32;
|
||||
relative_position = reference_area_size - 1 -
|
||||
(reference_area_size * relative_position >> 32);
|
||||
|
||||
/* 1.2.5 Computing starting position */
|
||||
start_position = 0;
|
||||
|
||||
if (0 != position->pass) {
|
||||
start_position = (position->slice == ARGON2_SYNC_POINTS - 1)
|
||||
? 0
|
||||
: (position->slice + 1) * instance->segment_length;
|
||||
}
|
||||
|
||||
/* 1.2.6. Computing absolute position */
|
||||
absolute_position = (start_position + relative_position) %
|
||||
instance->lane_length; /* absolute position */
|
||||
return absolute_position;
|
||||
}
|
||||
|
||||
/* Single-threaded version for p=1 case */
|
||||
static int fill_memory_blocks_st(argon2_instance_t *instance) {
|
||||
uint32_t r, s, l;
|
||||
|
||||
for (r = 0; r < instance->passes; ++r) {
|
||||
for (s = 0; s < ARGON2_SYNC_POINTS; ++s) {
|
||||
for (l = 0; l < instance->lanes; ++l) {
|
||||
argon2_position_t position = { r, l, (uint8_t)s, 0 };
|
||||
//fill the segment using the selected implementation
|
||||
instance->impl(instance, position);
|
||||
}
|
||||
}
|
||||
}
|
||||
return ARGON2_OK;
|
||||
}
|
||||
|
||||
int randomx_argon2_fill_memory_blocks(argon2_instance_t *instance) {
|
||||
if (instance == NULL || instance->lanes == 0) {
|
||||
return ARGON2_INCORRECT_PARAMETER;
|
||||
}
|
||||
return fill_memory_blocks_st(instance);
|
||||
}
|
||||
|
||||
int randomx_argon2_validate_inputs(const argon2_context *context) {
|
||||
if (NULL == context) {
|
||||
return ARGON2_INCORRECT_PARAMETER;
|
||||
}
|
||||
|
||||
/* Validate password (required param) */
|
||||
if (NULL == context->pwd) {
|
||||
if (0 != context->pwdlen) {
|
||||
return ARGON2_PWD_PTR_MISMATCH;
|
||||
}
|
||||
}
|
||||
|
||||
if (ARGON2_MIN_PWD_LENGTH > context->pwdlen) {
|
||||
return ARGON2_PWD_TOO_SHORT;
|
||||
}
|
||||
|
||||
if (ARGON2_MAX_PWD_LENGTH < context->pwdlen) {
|
||||
return ARGON2_PWD_TOO_LONG;
|
||||
}
|
||||
|
||||
/* Validate salt (required param) */
|
||||
if (NULL == context->salt) {
|
||||
if (0 != context->saltlen) {
|
||||
return ARGON2_SALT_PTR_MISMATCH;
|
||||
}
|
||||
}
|
||||
|
||||
if (ARGON2_MIN_SALT_LENGTH > context->saltlen) {
|
||||
return ARGON2_SALT_TOO_SHORT;
|
||||
}
|
||||
|
||||
if (ARGON2_MAX_SALT_LENGTH < context->saltlen) {
|
||||
return ARGON2_SALT_TOO_LONG;
|
||||
}
|
||||
|
||||
/* Validate secret (optional param) */
|
||||
if (NULL == context->secret) {
|
||||
if (0 != context->secretlen) {
|
||||
return ARGON2_SECRET_PTR_MISMATCH;
|
||||
}
|
||||
}
|
||||
else {
|
||||
if (ARGON2_MIN_SECRET > context->secretlen) {
|
||||
return ARGON2_SECRET_TOO_SHORT;
|
||||
}
|
||||
if (ARGON2_MAX_SECRET < context->secretlen) {
|
||||
return ARGON2_SECRET_TOO_LONG;
|
||||
}
|
||||
}
|
||||
|
||||
/* Validate associated data (optional param) */
|
||||
if (NULL == context->ad) {
|
||||
if (0 != context->adlen) {
|
||||
return ARGON2_AD_PTR_MISMATCH;
|
||||
}
|
||||
}
|
||||
else {
|
||||
if (ARGON2_MIN_AD_LENGTH > context->adlen) {
|
||||
return ARGON2_AD_TOO_SHORT;
|
||||
}
|
||||
if (ARGON2_MAX_AD_LENGTH < context->adlen) {
|
||||
return ARGON2_AD_TOO_LONG;
|
||||
}
|
||||
}
|
||||
|
||||
/* Validate memory cost */
|
||||
if (ARGON2_MIN_MEMORY > context->m_cost) {
|
||||
return ARGON2_MEMORY_TOO_LITTLE;
|
||||
}
|
||||
|
||||
if (ARGON2_MAX_MEMORY < context->m_cost) {
|
||||
return ARGON2_MEMORY_TOO_MUCH;
|
||||
}
|
||||
|
||||
if (context->m_cost < 8 * context->lanes) {
|
||||
return ARGON2_MEMORY_TOO_LITTLE;
|
||||
}
|
||||
|
||||
/* Validate time cost */
|
||||
if (ARGON2_MIN_TIME > context->t_cost) {
|
||||
return ARGON2_TIME_TOO_SMALL;
|
||||
}
|
||||
|
||||
if (ARGON2_MAX_TIME < context->t_cost) {
|
||||
return ARGON2_TIME_TOO_LARGE;
|
||||
}
|
||||
|
||||
/* Validate lanes */
|
||||
if (ARGON2_MIN_LANES > context->lanes) {
|
||||
return ARGON2_LANES_TOO_FEW;
|
||||
}
|
||||
|
||||
if (ARGON2_MAX_LANES < context->lanes) {
|
||||
return ARGON2_LANES_TOO_MANY;
|
||||
}
|
||||
|
||||
/* Validate threads */
|
||||
if (ARGON2_MIN_THREADS > context->threads) {
|
||||
return ARGON2_THREADS_TOO_FEW;
|
||||
}
|
||||
|
||||
if (ARGON2_MAX_THREADS < context->threads) {
|
||||
return ARGON2_THREADS_TOO_MANY;
|
||||
}
|
||||
|
||||
if (NULL != context->allocate_cbk && NULL == context->free_cbk) {
|
||||
return ARGON2_FREE_MEMORY_CBK_NULL;
|
||||
}
|
||||
|
||||
if (NULL == context->allocate_cbk && NULL != context->free_cbk) {
|
||||
return ARGON2_ALLOCATE_MEMORY_CBK_NULL;
|
||||
}
|
||||
|
||||
return ARGON2_OK;
|
||||
}
|
||||
|
||||
void rxa2_fill_first_blocks(uint8_t *blockhash, const argon2_instance_t *instance) {
|
||||
uint32_t l;
|
||||
/* Make the first and second block in each lane as G(H0||0||i) or
|
||||
G(H0||1||i) */
|
||||
uint8_t blockhash_bytes[ARGON2_BLOCK_SIZE];
|
||||
for (l = 0; l < instance->lanes; ++l) {
|
||||
|
||||
store32(blockhash + ARGON2_PREHASH_DIGEST_LENGTH, 0);
|
||||
store32(blockhash + ARGON2_PREHASH_DIGEST_LENGTH + 4, l);
|
||||
blake2b_long(blockhash_bytes, ARGON2_BLOCK_SIZE, blockhash,
|
||||
ARGON2_PREHASH_SEED_LENGTH);
|
||||
load_block(&instance->memory[l * instance->lane_length + 0],
|
||||
blockhash_bytes);
|
||||
|
||||
store32(blockhash + ARGON2_PREHASH_DIGEST_LENGTH, 1);
|
||||
blake2b_long(blockhash_bytes, ARGON2_BLOCK_SIZE, blockhash,
|
||||
ARGON2_PREHASH_SEED_LENGTH);
|
||||
load_block(&instance->memory[l * instance->lane_length + 1],
|
||||
blockhash_bytes);
|
||||
}
|
||||
}
|
||||
|
||||
void rxa2_initial_hash(uint8_t *blockhash, argon2_context *context, argon2_type type) {
|
||||
blake2b_state BlakeHash;
|
||||
uint8_t value[sizeof(uint32_t)];
|
||||
|
||||
if (NULL == context || NULL == blockhash) {
|
||||
return;
|
||||
}
|
||||
|
||||
blake2b_init(&BlakeHash, ARGON2_PREHASH_DIGEST_LENGTH);
|
||||
|
||||
store32(&value, context->lanes);
|
||||
blake2b_update(&BlakeHash, (const uint8_t *)&value, sizeof(value));
|
||||
|
||||
store32(&value, context->outlen);
|
||||
blake2b_update(&BlakeHash, (const uint8_t *)&value, sizeof(value));
|
||||
|
||||
store32(&value, context->m_cost);
|
||||
blake2b_update(&BlakeHash, (const uint8_t *)&value, sizeof(value));
|
||||
|
||||
store32(&value, context->t_cost);
|
||||
blake2b_update(&BlakeHash, (const uint8_t *)&value, sizeof(value));
|
||||
|
||||
store32(&value, context->version);
|
||||
blake2b_update(&BlakeHash, (const uint8_t *)&value, sizeof(value));
|
||||
|
||||
store32(&value, (uint32_t)type);
|
||||
blake2b_update(&BlakeHash, (const uint8_t *)&value, sizeof(value));
|
||||
|
||||
store32(&value, context->pwdlen);
|
||||
blake2b_update(&BlakeHash, (const uint8_t *)&value, sizeof(value));
|
||||
|
||||
if (context->pwd != NULL) {
|
||||
blake2b_update(&BlakeHash, (const uint8_t *)context->pwd,
|
||||
context->pwdlen);
|
||||
}
|
||||
|
||||
store32(&value, context->saltlen);
|
||||
blake2b_update(&BlakeHash, (const uint8_t *)&value, sizeof(value));
|
||||
|
||||
if (context->salt != NULL) {
|
||||
blake2b_update(&BlakeHash, (const uint8_t *)context->salt, context->saltlen);
|
||||
}
|
||||
|
||||
store32(&value, context->secretlen);
|
||||
blake2b_update(&BlakeHash, (const uint8_t *)&value, sizeof(value));
|
||||
|
||||
if (context->secret != NULL) {
|
||||
blake2b_update(&BlakeHash, (const uint8_t *)context->secret,
|
||||
context->secretlen);
|
||||
}
|
||||
|
||||
store32(&value, context->adlen);
|
||||
blake2b_update(&BlakeHash, (const uint8_t *)&value, sizeof(value));
|
||||
|
||||
if (context->ad != NULL) {
|
||||
blake2b_update(&BlakeHash, (const uint8_t *)context->ad,
|
||||
context->adlen);
|
||||
}
|
||||
|
||||
blake2b_final(&BlakeHash, blockhash, ARGON2_PREHASH_DIGEST_LENGTH);
|
||||
}
|
||||
|
||||
int randomx_argon2_initialize(argon2_instance_t *instance, argon2_context *context) {
|
||||
uint8_t blockhash[ARGON2_PREHASH_SEED_LENGTH];
|
||||
int result = ARGON2_OK;
|
||||
|
||||
if (instance == NULL || context == NULL)
|
||||
return ARGON2_INCORRECT_PARAMETER;
|
||||
instance->context_ptr = context;
|
||||
|
||||
/* 1. Memory allocation */
|
||||
//RandomX takes care of memory allocation
|
||||
|
||||
/* 2. Initial hashing */
|
||||
/* H_0 + 8 extra bytes to produce the first blocks */
|
||||
/* uint8_t blockhash[ARGON2_PREHASH_SEED_LENGTH]; */
|
||||
/* Hashing all inputs */
|
||||
rxa2_initial_hash(blockhash, context, instance->type);
|
||||
/* Zeroing 8 extra bytes */
|
||||
/*rxa2_clear_internal_memory(blockhash + ARGON2_PREHASH_DIGEST_LENGTH,
|
||||
ARGON2_PREHASH_SEED_LENGTH -
|
||||
ARGON2_PREHASH_DIGEST_LENGTH);*/
|
||||
|
||||
/* 3. Creating first blocks, we always have at least two blocks in a slice
|
||||
*/
|
||||
rxa2_fill_first_blocks(blockhash, instance);
|
||||
|
||||
return ARGON2_OK;
|
||||
}
|
||||
163
app/src/main/cpp/randomx/src/argon2_core.h
Normal file
163
app/src/main/cpp/randomx/src/argon2_core.h
Normal file
|
|
@ -0,0 +1,163 @@
|
|||
/*
|
||||
Copyright (c) 2018-2019, tevador <tevador@gmail.com>
|
||||
|
||||
All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted provided that the following conditions are met:
|
||||
* Redistributions of source code must retain the above copyright
|
||||
notice, this list of conditions and the following disclaimer.
|
||||
* Redistributions in binary form must reproduce the above copyright
|
||||
notice, this list of conditions and the following disclaimer in the
|
||||
documentation and/or other materials provided with the distribution.
|
||||
* Neither the name of the copyright holder nor the
|
||||
names of its contributors may be used to endorse or promote products
|
||||
derived from this software without specific prior written permission.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
|
||||
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
|
||||
FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
||||
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
|
||||
CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
|
||||
OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
/* Original code from Argon2 reference source code package used under CC0 Licence
|
||||
* https://github.com/P-H-C/phc-winner-argon2
|
||||
* Copyright 2015
|
||||
* Daniel Dinu, Dmitry Khovratovich, Jean-Philippe Aumasson, and Samuel Neves
|
||||
*/
|
||||
|
||||
#ifndef ARGON2_CORE_H
|
||||
#define ARGON2_CORE_H
|
||||
|
||||
#include <stdint.h>
|
||||
#include "argon2.h"
|
||||
|
||||
#if defined(__cplusplus)
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
#define CONST_CAST(x) (x)(uintptr_t)
|
||||
|
||||
/**********************Argon2 internal constants*******************************/
|
||||
|
||||
enum argon2_core_constants {
|
||||
/* Memory block size in bytes */
|
||||
ARGON2_BLOCK_SIZE = 1024,
|
||||
ARGON2_QWORDS_IN_BLOCK = ARGON2_BLOCK_SIZE / 8,
|
||||
ARGON2_OWORDS_IN_BLOCK = ARGON2_BLOCK_SIZE / 16,
|
||||
ARGON2_HWORDS_IN_BLOCK = ARGON2_BLOCK_SIZE / 32,
|
||||
ARGON2_512BIT_WORDS_IN_BLOCK = ARGON2_BLOCK_SIZE / 64,
|
||||
|
||||
/* Number of pseudo-random values generated by one call to Blake in Argon2i
|
||||
to
|
||||
generate reference block positions */
|
||||
ARGON2_ADDRESSES_IN_BLOCK = 128,
|
||||
|
||||
/* Pre-hashing digest length and its extension*/
|
||||
ARGON2_PREHASH_DIGEST_LENGTH = 64,
|
||||
ARGON2_PREHASH_SEED_LENGTH = 72
|
||||
};
|
||||
|
||||
/*************************Argon2 internal data types***********************/
|
||||
|
||||
/*
|
||||
* Structure for the (1KB) memory block implemented as 128 64-bit words.
|
||||
* Memory blocks can be copied, XORed. Internal words can be accessed by [] (no
|
||||
* bounds checking).
|
||||
*/
|
||||
typedef struct block_ { uint64_t v[ARGON2_QWORDS_IN_BLOCK]; } block;
|
||||
|
||||
/*
|
||||
* Argon2 instance: memory pointer, number of passes, amount of memory, type,
|
||||
* and derived values.
|
||||
* Used to evaluate the number and location of blocks to construct in each
|
||||
* thread
|
||||
*/
|
||||
typedef struct Argon2_instance_t {
|
||||
block *memory; /* Memory pointer */
|
||||
uint32_t version;
|
||||
uint32_t passes; /* Number of passes */
|
||||
uint32_t memory_blocks; /* Number of blocks in memory */
|
||||
uint32_t segment_length;
|
||||
uint32_t lane_length;
|
||||
uint32_t lanes;
|
||||
uint32_t threads;
|
||||
argon2_type type;
|
||||
int print_internals; /* whether to print the memory blocks */
|
||||
argon2_context *context_ptr; /* points back to original context */
|
||||
randomx_argon2_impl *impl;
|
||||
} argon2_instance_t;
|
||||
|
||||
/*
|
||||
* Argon2 position: where we construct the block right now. Used to distribute
|
||||
* work between threads.
|
||||
*/
|
||||
typedef struct Argon2_position_t {
|
||||
uint32_t pass;
|
||||
uint32_t lane;
|
||||
uint8_t slice;
|
||||
uint32_t index;
|
||||
} argon2_position_t;
|
||||
|
||||
/*Struct that holds the inputs for thread handling FillSegment*/
|
||||
typedef struct Argon2_thread_data {
|
||||
argon2_instance_t *instance_ptr;
|
||||
argon2_position_t pos;
|
||||
} argon2_thread_data;
|
||||
|
||||
/*************************Argon2 core functions********************************/
|
||||
|
||||
/*
|
||||
* Computes absolute position of reference block in the lane following a skewed
|
||||
* distribution and using a pseudo-random value as input
|
||||
* @param instance Pointer to the current instance
|
||||
* @param position Pointer to the current position
|
||||
* @param pseudo_rand 32-bit pseudo-random value used to determine the position
|
||||
* @param same_lane Indicates if the block will be taken from the current lane.
|
||||
* If so we can reference the current segment
|
||||
* @pre All pointers must be valid
|
||||
*/
|
||||
uint32_t randomx_argon2_index_alpha(const argon2_instance_t *instance,
|
||||
const argon2_position_t *position, uint32_t pseudo_rand,
|
||||
int same_lane);
|
||||
|
||||
/*
|
||||
* Function that validates all inputs against predefined restrictions and return
|
||||
* an error code
|
||||
* @param context Pointer to current Argon2 context
|
||||
* @return ARGON2_OK if everything is all right, otherwise one of error codes
|
||||
* (all defined in <argon2.h>
|
||||
*/
|
||||
int randomx_argon2_validate_inputs(const argon2_context *context);
|
||||
|
||||
/*
|
||||
* Function allocates memory, hashes the inputs with Blake, and creates first
|
||||
* two blocks. Returns the pointer to the main memory with 2 blocks per lane
|
||||
* initialized
|
||||
* @param context Pointer to the Argon2 internal structure containing memory
|
||||
* pointer, and parameters for time and space requirements.
|
||||
* @param instance Current Argon2 instance
|
||||
* @return Zero if successful, -1 if memory failed to allocate. @context->state
|
||||
* will be modified if successful.
|
||||
*/
|
||||
int randomx_argon2_initialize(argon2_instance_t *instance, argon2_context *context);
|
||||
|
||||
/*
|
||||
* Function that fills the entire memory t_cost times based on the first two
|
||||
* blocks in each lane
|
||||
* @param instance Pointer to the current instance
|
||||
* @return ARGON2_OK if successful, @context->state
|
||||
*/
|
||||
int randomx_argon2_fill_memory_blocks(argon2_instance_t* instance);
|
||||
|
||||
#if defined(__cplusplus)
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif
|
||||
187
app/src/main/cpp/randomx/src/argon2_ref.c
Normal file
187
app/src/main/cpp/randomx/src/argon2_ref.c
Normal file
|
|
@ -0,0 +1,187 @@
|
|||
/*
|
||||
Copyright (c) 2018-2019, tevador <tevador@gmail.com>
|
||||
|
||||
All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted provided that the following conditions are met:
|
||||
* Redistributions of source code must retain the above copyright
|
||||
notice, this list of conditions and the following disclaimer.
|
||||
* Redistributions in binary form must reproduce the above copyright
|
||||
notice, this list of conditions and the following disclaimer in the
|
||||
documentation and/or other materials provided with the distribution.
|
||||
* Neither the name of the copyright holder nor the
|
||||
names of its contributors may be used to endorse or promote products
|
||||
derived from this software without specific prior written permission.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
|
||||
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
|
||||
FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
||||
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
|
||||
CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
|
||||
OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
/* Original code from Argon2 reference source code package used under CC0 Licence
|
||||
* https://github.com/P-H-C/phc-winner-argon2
|
||||
* Copyright 2015
|
||||
* Daniel Dinu, Dmitry Khovratovich, Jean-Philippe Aumasson, and Samuel Neves
|
||||
*/
|
||||
|
||||
#include <stdint.h>
|
||||
#include <string.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
#include "argon2.h"
|
||||
#include "argon2_core.h"
|
||||
|
||||
#include "blake2/blamka-round-ref.h"
|
||||
#include "blake2/blake2-impl.h"
|
||||
#include "blake2/blake2.h"
|
||||
|
||||
static void copy_block(block* dst, const block* src) {
|
||||
memcpy(dst->v, src->v, sizeof(uint64_t) * ARGON2_QWORDS_IN_BLOCK);
|
||||
}
|
||||
|
||||
static void xor_block(block* dst, const block* src) {
|
||||
int i;
|
||||
for (i = 0; i < ARGON2_QWORDS_IN_BLOCK; ++i) {
|
||||
dst->v[i] ^= src->v[i];
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Function fills a new memory block and optionally XORs the old block over the new one.
|
||||
* @next_block must be initialized.
|
||||
* @param prev_block Pointer to the previous block
|
||||
* @param ref_block Pointer to the reference block
|
||||
* @param next_block Pointer to the block to be constructed
|
||||
* @param with_xor Whether to XOR into the new block (1) or just overwrite (0)
|
||||
* @pre all block pointers must be valid
|
||||
*/
|
||||
static void fill_block(const block *prev_block, const block *ref_block,
|
||||
block *next_block, int with_xor) {
|
||||
block blockR, block_tmp;
|
||||
unsigned i;
|
||||
|
||||
copy_block(&blockR, ref_block);
|
||||
xor_block(&blockR, prev_block);
|
||||
copy_block(&block_tmp, &blockR);
|
||||
/* Now blockR = ref_block + prev_block and block_tmp = ref_block + prev_block */
|
||||
if (with_xor) {
|
||||
/* Saving the next block contents for XOR over: */
|
||||
xor_block(&block_tmp, next_block);
|
||||
/* Now blockR = ref_block + prev_block and
|
||||
block_tmp = ref_block + prev_block + next_block */
|
||||
}
|
||||
|
||||
/* Apply Blake2 on columns of 64-bit words: (0,1,...,15) , then
|
||||
(16,17,..31)... finally (112,113,...127) */
|
||||
for (i = 0; i < 8; ++i) {
|
||||
BLAKE2_ROUND_NOMSG(
|
||||
blockR.v[16 * i], blockR.v[16 * i + 1], blockR.v[16 * i + 2],
|
||||
blockR.v[16 * i + 3], blockR.v[16 * i + 4], blockR.v[16 * i + 5],
|
||||
blockR.v[16 * i + 6], blockR.v[16 * i + 7], blockR.v[16 * i + 8],
|
||||
blockR.v[16 * i + 9], blockR.v[16 * i + 10], blockR.v[16 * i + 11],
|
||||
blockR.v[16 * i + 12], blockR.v[16 * i + 13], blockR.v[16 * i + 14],
|
||||
blockR.v[16 * i + 15]);
|
||||
}
|
||||
|
||||
/* Apply Blake2 on rows of 64-bit words: (0,1,16,17,...112,113), then
|
||||
(2,3,18,19,...,114,115).. finally (14,15,30,31,...,126,127) */
|
||||
for (i = 0; i < 8; i++) {
|
||||
BLAKE2_ROUND_NOMSG(
|
||||
blockR.v[2 * i], blockR.v[2 * i + 1], blockR.v[2 * i + 16],
|
||||
blockR.v[2 * i + 17], blockR.v[2 * i + 32], blockR.v[2 * i + 33],
|
||||
blockR.v[2 * i + 48], blockR.v[2 * i + 49], blockR.v[2 * i + 64],
|
||||
blockR.v[2 * i + 65], blockR.v[2 * i + 80], blockR.v[2 * i + 81],
|
||||
blockR.v[2 * i + 96], blockR.v[2 * i + 97], blockR.v[2 * i + 112],
|
||||
blockR.v[2 * i + 113]);
|
||||
}
|
||||
|
||||
copy_block(next_block, &block_tmp);
|
||||
xor_block(next_block, &blockR);
|
||||
}
|
||||
|
||||
void randomx_argon2_fill_segment_ref(const argon2_instance_t *instance,
|
||||
argon2_position_t position) {
|
||||
block *ref_block = NULL, *curr_block = NULL;
|
||||
block address_block, input_block, zero_block;
|
||||
uint64_t pseudo_rand, ref_index, ref_lane;
|
||||
uint32_t prev_offset, curr_offset;
|
||||
uint32_t starting_index;
|
||||
uint32_t i;
|
||||
|
||||
if (instance == NULL) {
|
||||
return;
|
||||
}
|
||||
|
||||
starting_index = 0;
|
||||
|
||||
if ((0 == position.pass) && (0 == position.slice)) {
|
||||
starting_index = 2; /* we have already generated the first two blocks */
|
||||
}
|
||||
|
||||
/* Offset of the current block */
|
||||
curr_offset = position.lane * instance->lane_length +
|
||||
position.slice * instance->segment_length + starting_index;
|
||||
|
||||
if (0 == curr_offset % instance->lane_length) {
|
||||
/* Last block in this lane */
|
||||
prev_offset = curr_offset + instance->lane_length - 1;
|
||||
}
|
||||
else {
|
||||
/* Previous block */
|
||||
prev_offset = curr_offset - 1;
|
||||
}
|
||||
|
||||
for (i = starting_index; i < instance->segment_length;
|
||||
++i, ++curr_offset, ++prev_offset) {
|
||||
/*1.1 Rotating prev_offset if needed */
|
||||
if (curr_offset % instance->lane_length == 1) {
|
||||
prev_offset = curr_offset - 1;
|
||||
}
|
||||
|
||||
/* 1.2 Computing the index of the reference block */
|
||||
/* 1.2.1 Taking pseudo-random value from the previous block */
|
||||
pseudo_rand = instance->memory[prev_offset].v[0];
|
||||
|
||||
/* 1.2.2 Computing the lane of the reference block */
|
||||
ref_lane = ((pseudo_rand >> 32)) % instance->lanes;
|
||||
|
||||
if ((position.pass == 0) && (position.slice == 0)) {
|
||||
/* Can not reference other lanes yet */
|
||||
ref_lane = position.lane;
|
||||
}
|
||||
|
||||
/* 1.2.3 Computing the number of possible reference block within the
|
||||
* lane.
|
||||
*/
|
||||
position.index = i;
|
||||
ref_index = randomx_argon2_index_alpha(instance, &position, pseudo_rand & 0xFFFFFFFF,
|
||||
ref_lane == position.lane);
|
||||
|
||||
/* 2 Creating a new block */
|
||||
ref_block =
|
||||
instance->memory + instance->lane_length * ref_lane + ref_index;
|
||||
curr_block = instance->memory + curr_offset;
|
||||
if (ARGON2_VERSION_10 == instance->version) {
|
||||
/* version 1.2.1 and earlier: overwrite, not XOR */
|
||||
fill_block(instance->memory + prev_offset, ref_block, curr_block, 0);
|
||||
}
|
||||
else {
|
||||
if (0 == position.pass) {
|
||||
fill_block(instance->memory + prev_offset, ref_block,
|
||||
curr_block, 0);
|
||||
}
|
||||
else {
|
||||
fill_block(instance->memory + prev_offset, ref_block,
|
||||
curr_block, 1);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
182
app/src/main/cpp/randomx/src/argon2_ssse3.c
Normal file
182
app/src/main/cpp/randomx/src/argon2_ssse3.c
Normal file
|
|
@ -0,0 +1,182 @@
|
|||
/*
|
||||
Copyright (c) 2018-2019, tevador <tevador@gmail.com>
|
||||
|
||||
All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted provided that the following conditions are met:
|
||||
* Redistributions of source code must retain the above copyright
|
||||
notice, this list of conditions and the following disclaimer.
|
||||
* Redistributions in binary form must reproduce the above copyright
|
||||
notice, this list of conditions and the following disclaimer in the
|
||||
documentation and/or other materials provided with the distribution.
|
||||
* Neither the name of the copyright holder nor the
|
||||
names of its contributors may be used to endorse or promote products
|
||||
derived from this software without specific prior written permission.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
|
||||
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
|
||||
FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
||||
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
|
||||
CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
|
||||
OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
/* Original code from Argon2 reference source code package used under CC0 Licence
|
||||
* https://github.com/P-H-C/phc-winner-argon2
|
||||
* Copyright 2015
|
||||
* Daniel Dinu, Dmitry Khovratovich, Jean-Philippe Aumasson, and Samuel Neves
|
||||
*/
|
||||
|
||||
#include <stdint.h>
|
||||
#include <string.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
#include "argon2.h"
|
||||
|
||||
#if defined(_MSC_VER) //MSVC doesn't define SSSE3
|
||||
#define __SSSE3__
|
||||
#endif
|
||||
|
||||
void randomx_argon2_fill_segment_ssse3(const argon2_instance_t* instance,
|
||||
argon2_position_t position);
|
||||
|
||||
randomx_argon2_impl* randomx_argon2_impl_ssse3() {
|
||||
#if defined(__SSSE3__)
|
||||
return &randomx_argon2_fill_segment_ssse3;
|
||||
#endif
|
||||
return NULL;
|
||||
}
|
||||
|
||||
#if defined(__SSSE3__)
|
||||
|
||||
#include <tmmintrin.h> /* for _mm_shuffle_epi8 and _mm_alignr_epi8 */
|
||||
|
||||
#include "argon2_core.h"
|
||||
|
||||
#include "blake2/blamka-round-ssse3.h"
|
||||
#include "blake2/blake2-impl.h"
|
||||
#include "blake2/blake2.h"
|
||||
|
||||
static void fill_block(__m128i* state, const block* ref_block,
|
||||
block* next_block, int with_xor) {
|
||||
__m128i block_XY[ARGON2_OWORDS_IN_BLOCK];
|
||||
unsigned int i;
|
||||
|
||||
if (with_xor) {
|
||||
for (i = 0; i < ARGON2_OWORDS_IN_BLOCK; i++) {
|
||||
state[i] = _mm_xor_si128(
|
||||
state[i], _mm_loadu_si128((const __m128i*)ref_block->v + i));
|
||||
block_XY[i] = _mm_xor_si128(
|
||||
state[i], _mm_loadu_si128((const __m128i*)next_block->v + i));
|
||||
}
|
||||
}
|
||||
else {
|
||||
for (i = 0; i < ARGON2_OWORDS_IN_BLOCK; i++) {
|
||||
block_XY[i] = state[i] = _mm_xor_si128(
|
||||
state[i], _mm_loadu_si128((const __m128i*)ref_block->v + i));
|
||||
}
|
||||
}
|
||||
|
||||
for (i = 0; i < 8; ++i) {
|
||||
BLAKE2_ROUND(state[8 * i + 0], state[8 * i + 1], state[8 * i + 2],
|
||||
state[8 * i + 3], state[8 * i + 4], state[8 * i + 5],
|
||||
state[8 * i + 6], state[8 * i + 7]);
|
||||
}
|
||||
|
||||
for (i = 0; i < 8; ++i) {
|
||||
BLAKE2_ROUND(state[8 * 0 + i], state[8 * 1 + i], state[8 * 2 + i],
|
||||
state[8 * 3 + i], state[8 * 4 + i], state[8 * 5 + i],
|
||||
state[8 * 6 + i], state[8 * 7 + i]);
|
||||
}
|
||||
|
||||
for (i = 0; i < ARGON2_OWORDS_IN_BLOCK; i++) {
|
||||
state[i] = _mm_xor_si128(state[i], block_XY[i]);
|
||||
_mm_storeu_si128((__m128i*)next_block->v + i, state[i]);
|
||||
}
|
||||
}
|
||||
|
||||
void randomx_argon2_fill_segment_ssse3(const argon2_instance_t* instance,
|
||||
argon2_position_t position) {
|
||||
block* ref_block = NULL, * curr_block = NULL;
|
||||
block address_block, input_block;
|
||||
uint64_t pseudo_rand, ref_index, ref_lane;
|
||||
uint32_t prev_offset, curr_offset;
|
||||
uint32_t starting_index, i;
|
||||
__m128i state[ARGON2_OWORDS_IN_BLOCK];
|
||||
|
||||
if (instance == NULL) {
|
||||
return;
|
||||
}
|
||||
|
||||
starting_index = 0;
|
||||
|
||||
if ((0 == position.pass) && (0 == position.slice)) {
|
||||
starting_index = 2; /* we have already generated the first two blocks */
|
||||
}
|
||||
|
||||
/* Offset of the current block */
|
||||
curr_offset = position.lane * instance->lane_length +
|
||||
position.slice * instance->segment_length + starting_index;
|
||||
|
||||
if (0 == curr_offset % instance->lane_length) {
|
||||
/* Last block in this lane */
|
||||
prev_offset = curr_offset + instance->lane_length - 1;
|
||||
}
|
||||
else {
|
||||
/* Previous block */
|
||||
prev_offset = curr_offset - 1;
|
||||
}
|
||||
|
||||
memcpy(state, ((instance->memory + prev_offset)->v), ARGON2_BLOCK_SIZE);
|
||||
|
||||
for (i = starting_index; i < instance->segment_length;
|
||||
++i, ++curr_offset, ++prev_offset) {
|
||||
/*1.1 Rotating prev_offset if needed */
|
||||
if (curr_offset % instance->lane_length == 1) {
|
||||
prev_offset = curr_offset - 1;
|
||||
}
|
||||
|
||||
/* 1.2 Computing the index of the reference block */
|
||||
/* 1.2.1 Taking pseudo-random value from the previous block */
|
||||
pseudo_rand = instance->memory[prev_offset].v[0];
|
||||
|
||||
/* 1.2.2 Computing the lane of the reference block */
|
||||
ref_lane = ((pseudo_rand >> 32)) % instance->lanes;
|
||||
|
||||
if ((position.pass == 0) && (position.slice == 0)) {
|
||||
/* Can not reference other lanes yet */
|
||||
ref_lane = position.lane;
|
||||
}
|
||||
|
||||
/* 1.2.3 Computing the number of possible reference block within the
|
||||
* lane.
|
||||
*/
|
||||
position.index = i;
|
||||
ref_index = randomx_argon2_index_alpha(instance, &position, pseudo_rand & 0xFFFFFFFF,
|
||||
ref_lane == position.lane);
|
||||
|
||||
/* 2 Creating a new block */
|
||||
ref_block =
|
||||
instance->memory + instance->lane_length * ref_lane + ref_index;
|
||||
curr_block = instance->memory + curr_offset;
|
||||
if (ARGON2_VERSION_10 == instance->version) {
|
||||
/* version 1.2.1 and earlier: overwrite, not XOR */
|
||||
fill_block(state, ref_block, curr_block, 0);
|
||||
}
|
||||
else {
|
||||
if (0 == position.pass) {
|
||||
fill_block(state, ref_block, curr_block, 0);
|
||||
}
|
||||
else {
|
||||
fill_block(state, ref_block, curr_block, 1);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
48
app/src/main/cpp/randomx/src/asm/configuration.asm
Normal file
48
app/src/main/cpp/randomx/src/asm/configuration.asm
Normal file
|
|
@ -0,0 +1,48 @@
|
|||
; File start: ..\src\configuration.h
|
||||
RANDOMX_ARGON_MEMORY EQU 262144t
|
||||
RANDOMX_ARGON_ITERATIONS EQU 3t
|
||||
RANDOMX_ARGON_LANES EQU 1t
|
||||
RANDOMX_ARGON_SALT TEXTEQU <"RandomX\x03">
|
||||
RANDOMX_CACHE_ACCESSES EQU 8t
|
||||
RANDOMX_SUPERSCALAR_LATENCY EQU 170t
|
||||
RANDOMX_DATASET_BASE_SIZE EQU 2147483648t
|
||||
RANDOMX_DATASET_EXTRA_SIZE EQU 33554368t
|
||||
RANDOMX_PROGRAM_SIZE EQU 256t
|
||||
RANDOMX_PROGRAM_ITERATIONS EQU 2048t
|
||||
RANDOMX_PROGRAM_COUNT EQU 8t
|
||||
RANDOMX_SCRATCHPAD_L3 EQU 2097152t
|
||||
RANDOMX_SCRATCHPAD_L2 EQU 262144t
|
||||
RANDOMX_SCRATCHPAD_L1 EQU 16384t
|
||||
RANDOMX_JUMP_BITS EQU 8t
|
||||
RANDOMX_JUMP_OFFSET EQU 8t
|
||||
RANDOMX_FREQ_IADD_RS EQU 16t
|
||||
RANDOMX_FREQ_IADD_M EQU 7t
|
||||
RANDOMX_FREQ_ISUB_R EQU 16t
|
||||
RANDOMX_FREQ_ISUB_M EQU 7t
|
||||
RANDOMX_FREQ_IMUL_R EQU 16t
|
||||
RANDOMX_FREQ_IMUL_M EQU 4t
|
||||
RANDOMX_FREQ_IMULH_R EQU 4t
|
||||
RANDOMX_FREQ_IMULH_M EQU 1t
|
||||
RANDOMX_FREQ_ISMULH_R EQU 4t
|
||||
RANDOMX_FREQ_ISMULH_M EQU 1t
|
||||
RANDOMX_FREQ_IMUL_RCP EQU 8t
|
||||
RANDOMX_FREQ_INEG_R EQU 2t
|
||||
RANDOMX_FREQ_IXOR_R EQU 15t
|
||||
RANDOMX_FREQ_IXOR_M EQU 5t
|
||||
RANDOMX_FREQ_IROR_R EQU 8t
|
||||
RANDOMX_FREQ_IROL_R EQU 2t
|
||||
RANDOMX_FREQ_ISWAP_R EQU 4t
|
||||
RANDOMX_FREQ_FSWAP_R EQU 4t
|
||||
RANDOMX_FREQ_FADD_R EQU 16t
|
||||
RANDOMX_FREQ_FADD_M EQU 5t
|
||||
RANDOMX_FREQ_FSUB_R EQU 16t
|
||||
RANDOMX_FREQ_FSUB_M EQU 5t
|
||||
RANDOMX_FREQ_FSCAL_R EQU 6t
|
||||
RANDOMX_FREQ_FMUL_R EQU 32t
|
||||
RANDOMX_FREQ_FDIV_M EQU 4t
|
||||
RANDOMX_FREQ_FSQRT_R EQU 6t
|
||||
RANDOMX_FREQ_CBRANCH EQU 25t
|
||||
RANDOMX_FREQ_CFROUND EQU 1t
|
||||
RANDOMX_FREQ_ISTORE EQU 16t
|
||||
RANDOMX_FREQ_NOP EQU 0t
|
||||
; File end: ..\src\configuration.h
|
||||
10
app/src/main/cpp/randomx/src/asm/program_epilogue_linux.inc
Normal file
10
app/src/main/cpp/randomx/src/asm/program_epilogue_linux.inc
Normal file
|
|
@ -0,0 +1,10 @@
|
|||
;# restore callee-saved registers - System V AMD64 ABI
|
||||
pop r15
|
||||
pop r14
|
||||
pop r13
|
||||
pop r12
|
||||
pop rbp
|
||||
pop rbx
|
||||
|
||||
;# program finished
|
||||
ret 0
|
||||
19
app/src/main/cpp/randomx/src/asm/program_epilogue_store.inc
Normal file
19
app/src/main/cpp/randomx/src/asm/program_epilogue_store.inc
Normal file
|
|
@ -0,0 +1,19 @@
|
|||
;# save VM register values
|
||||
pop rcx
|
||||
mov qword ptr [rcx+0], r8
|
||||
mov qword ptr [rcx+8], r9
|
||||
mov qword ptr [rcx+16], r10
|
||||
mov qword ptr [rcx+24], r11
|
||||
mov qword ptr [rcx+32], r12
|
||||
mov qword ptr [rcx+40], r13
|
||||
mov qword ptr [rcx+48], r14
|
||||
mov qword ptr [rcx+56], r15
|
||||
movdqa xmmword ptr [rcx+64], xmm0
|
||||
movdqa xmmword ptr [rcx+80], xmm1
|
||||
movdqa xmmword ptr [rcx+96], xmm2
|
||||
movdqa xmmword ptr [rcx+112], xmm3
|
||||
lea rcx, [rcx+64]
|
||||
movdqa xmmword ptr [rcx+64], xmm4
|
||||
movdqa xmmword ptr [rcx+80], xmm5
|
||||
movdqa xmmword ptr [rcx+96], xmm6
|
||||
movdqa xmmword ptr [rcx+112], xmm7
|
||||
24
app/src/main/cpp/randomx/src/asm/program_epilogue_win64.inc
Normal file
24
app/src/main/cpp/randomx/src/asm/program_epilogue_win64.inc
Normal file
|
|
@ -0,0 +1,24 @@
|
|||
;# restore callee-saved registers - Microsoft x64 calling convention
|
||||
movdqu xmm15, xmmword ptr [rsp]
|
||||
movdqu xmm14, xmmword ptr [rsp+16]
|
||||
movdqu xmm13, xmmword ptr [rsp+32]
|
||||
movdqu xmm12, xmmword ptr [rsp+48]
|
||||
movdqu xmm11, xmmword ptr [rsp+64]
|
||||
add rsp, 80
|
||||
movdqu xmm10, xmmword ptr [rsp]
|
||||
movdqu xmm9, xmmword ptr [rsp+16]
|
||||
movdqu xmm8, xmmword ptr [rsp+32]
|
||||
movdqu xmm7, xmmword ptr [rsp+48]
|
||||
movdqu xmm6, xmmword ptr [rsp+64]
|
||||
add rsp, 80
|
||||
pop r15
|
||||
pop r14
|
||||
pop r13
|
||||
pop r12
|
||||
pop rsi
|
||||
pop rdi
|
||||
pop rbp
|
||||
pop rbx
|
||||
|
||||
;# program finished
|
||||
ret
|
||||
28
app/src/main/cpp/randomx/src/asm/program_loop_load.inc
Normal file
28
app/src/main/cpp/randomx/src/asm/program_loop_load.inc
Normal file
|
|
@ -0,0 +1,28 @@
|
|||
lea rcx, [rsi+rax]
|
||||
push rcx
|
||||
xor r8, qword ptr [rcx+0]
|
||||
xor r9, qword ptr [rcx+8]
|
||||
xor r10, qword ptr [rcx+16]
|
||||
xor r11, qword ptr [rcx+24]
|
||||
xor r12, qword ptr [rcx+32]
|
||||
xor r13, qword ptr [rcx+40]
|
||||
xor r14, qword ptr [rcx+48]
|
||||
xor r15, qword ptr [rcx+56]
|
||||
lea rcx, [rsi+rdx]
|
||||
push rcx
|
||||
cvtdq2pd xmm0, qword ptr [rcx+0]
|
||||
cvtdq2pd xmm1, qword ptr [rcx+8]
|
||||
cvtdq2pd xmm2, qword ptr [rcx+16]
|
||||
cvtdq2pd xmm3, qword ptr [rcx+24]
|
||||
cvtdq2pd xmm4, qword ptr [rcx+32]
|
||||
cvtdq2pd xmm5, qword ptr [rcx+40]
|
||||
cvtdq2pd xmm6, qword ptr [rcx+48]
|
||||
cvtdq2pd xmm7, qword ptr [rcx+56]
|
||||
andps xmm4, xmm13
|
||||
andps xmm5, xmm13
|
||||
andps xmm6, xmm13
|
||||
andps xmm7, xmm13
|
||||
orps xmm4, xmm14
|
||||
orps xmm5, xmm14
|
||||
orps xmm6, xmm14
|
||||
orps xmm7, xmm14
|
||||
18
app/src/main/cpp/randomx/src/asm/program_loop_store.inc
Normal file
18
app/src/main/cpp/randomx/src/asm/program_loop_store.inc
Normal file
|
|
@ -0,0 +1,18 @@
|
|||
pop rcx
|
||||
mov qword ptr [rcx+0], r8
|
||||
mov qword ptr [rcx+8], r9
|
||||
mov qword ptr [rcx+16], r10
|
||||
mov qword ptr [rcx+24], r11
|
||||
mov qword ptr [rcx+32], r12
|
||||
mov qword ptr [rcx+40], r13
|
||||
mov qword ptr [rcx+48], r14
|
||||
mov qword ptr [rcx+56], r15
|
||||
pop rcx
|
||||
xorpd xmm0, xmm4
|
||||
xorpd xmm1, xmm5
|
||||
xorpd xmm2, xmm6
|
||||
xorpd xmm3, xmm7
|
||||
movapd xmmword ptr [rcx+0], xmm0
|
||||
movapd xmmword ptr [rcx+16], xmm1
|
||||
movapd xmmword ptr [rcx+32], xmm2
|
||||
movapd xmmword ptr [rcx+48], xmm3
|
||||
35
app/src/main/cpp/randomx/src/asm/program_prologue_linux.inc
Normal file
35
app/src/main/cpp/randomx/src/asm/program_prologue_linux.inc
Normal file
|
|
@ -0,0 +1,35 @@
|
|||
;# callee-saved registers - System V AMD64 ABI
|
||||
push rbx
|
||||
push rbp
|
||||
push r12
|
||||
push r13
|
||||
push r14
|
||||
push r15
|
||||
|
||||
;# function arguments
|
||||
mov rbx, rcx ;# loop counter
|
||||
push rdi ;# RegisterFile& registerFile
|
||||
mov rcx, rdi
|
||||
mov rbp, qword ptr [rsi] ;# "mx", "ma"
|
||||
mov rdi, qword ptr [rsi+8] ;# uint8_t* dataset
|
||||
mov rsi, rdx ;# uint8_t* scratchpad
|
||||
|
||||
mov rax, rbp
|
||||
ror rbp, 32
|
||||
|
||||
;# zero integer registers
|
||||
xor r8, r8
|
||||
xor r9, r9
|
||||
xor r10, r10
|
||||
xor r11, r11
|
||||
xor r12, r12
|
||||
xor r13, r13
|
||||
xor r14, r14
|
||||
xor r15, r15
|
||||
|
||||
;# load constant registers
|
||||
lea rcx, [rcx+120]
|
||||
movapd xmm8, xmmword ptr [rcx+72]
|
||||
movapd xmm9, xmmword ptr [rcx+88]
|
||||
movapd xmm10, xmmword ptr [rcx+104]
|
||||
movapd xmm11, xmmword ptr [rcx+120]
|
||||
48
app/src/main/cpp/randomx/src/asm/program_prologue_win64.inc
Normal file
48
app/src/main/cpp/randomx/src/asm/program_prologue_win64.inc
Normal file
|
|
@ -0,0 +1,48 @@
|
|||
;# callee-saved registers - Microsoft x64 calling convention
|
||||
push rbx
|
||||
push rbp
|
||||
push rdi
|
||||
push rsi
|
||||
push r12
|
||||
push r13
|
||||
push r14
|
||||
push r15
|
||||
sub rsp, 80
|
||||
movdqu xmmword ptr [rsp+64], xmm6
|
||||
movdqu xmmword ptr [rsp+48], xmm7
|
||||
movdqu xmmword ptr [rsp+32], xmm8
|
||||
movdqu xmmword ptr [rsp+16], xmm9
|
||||
movdqu xmmword ptr [rsp+0], xmm10
|
||||
sub rsp, 80
|
||||
movdqu xmmword ptr [rsp+64], xmm11
|
||||
movdqu xmmword ptr [rsp+48], xmm12
|
||||
movdqu xmmword ptr [rsp+32], xmm13
|
||||
movdqu xmmword ptr [rsp+16], xmm14
|
||||
movdqu xmmword ptr [rsp+0], xmm15
|
||||
|
||||
;# function arguments
|
||||
push rcx ;# RegisterFile& registerFile
|
||||
mov rbp, qword ptr [rdx] ;# "mx", "ma"
|
||||
mov rdi, qword ptr [rdx+8] ;# uint8_t* dataset
|
||||
mov rsi, r8 ;# uint8_t* scratchpad
|
||||
mov rbx, r9 ;# loop counter
|
||||
|
||||
mov rax, rbp
|
||||
ror rbp, 32
|
||||
|
||||
;# zero integer registers
|
||||
xor r8, r8
|
||||
xor r9, r9
|
||||
xor r10, r10
|
||||
xor r11, r11
|
||||
xor r12, r12
|
||||
xor r13, r13
|
||||
xor r14, r14
|
||||
xor r15, r15
|
||||
|
||||
;# load constant registers
|
||||
lea rcx, [rcx+120]
|
||||
movapd xmm8, xmmword ptr [rcx+72]
|
||||
movapd xmm9, xmmword ptr [rcx+88]
|
||||
movapd xmm10, xmmword ptr [rcx+104]
|
||||
movapd xmm11, xmmword ptr [rcx+120]
|
||||
16
app/src/main/cpp/randomx/src/asm/program_read_dataset.inc
Normal file
16
app/src/main/cpp/randomx/src/asm/program_read_dataset.inc
Normal file
|
|
@ -0,0 +1,16 @@
|
|||
mov ecx, ebp ;# ecx = ma
|
||||
and ecx, RANDOMX_DATASET_BASE_MASK
|
||||
xor r8, qword ptr [rdi+rcx]
|
||||
ror rbp, 32 ;# swap "ma" and "mx"
|
||||
xor rbp, rax ;# modify "mx"
|
||||
mov edx, ebp ;# edx = mx
|
||||
and edx, RANDOMX_DATASET_BASE_MASK
|
||||
prefetchnta byte ptr [rdi+rdx]
|
||||
xor r9, qword ptr [rdi+rcx+8]
|
||||
xor r10, qword ptr [rdi+rcx+16]
|
||||
xor r11, qword ptr [rdi+rcx+24]
|
||||
xor r12, qword ptr [rdi+rcx+32]
|
||||
xor r13, qword ptr [rdi+rcx+40]
|
||||
xor r14, qword ptr [rdi+rcx+48]
|
||||
xor r15, qword ptr [rdi+rcx+56]
|
||||
|
||||
|
|
@ -0,0 +1,10 @@
|
|||
mov rbx, qword ptr [rsp+64]
|
||||
xor r8, qword ptr [rsp+56]
|
||||
xor r9, qword ptr [rsp+48]
|
||||
xor r10, qword ptr [rsp+40]
|
||||
xor r11, qword ptr [rsp+32]
|
||||
xor r12, qword ptr [rsp+24]
|
||||
xor r13, qword ptr [rsp+16]
|
||||
xor r14, qword ptr [rsp+8]
|
||||
xor r15, qword ptr [rsp+0]
|
||||
add rsp, 72
|
||||
|
|
@ -0,0 +1,17 @@
|
|||
sub rsp, 72
|
||||
mov qword ptr [rsp+64], rbx
|
||||
mov qword ptr [rsp+56], r8
|
||||
mov qword ptr [rsp+48], r9
|
||||
mov qword ptr [rsp+40], r10
|
||||
mov qword ptr [rsp+32], r11
|
||||
mov qword ptr [rsp+24], r12
|
||||
mov qword ptr [rsp+16], r13
|
||||
mov qword ptr [rsp+8], r14
|
||||
mov qword ptr [rsp+0], r15
|
||||
ror rbp, 32 ;# swap "ma" and "mx"
|
||||
xor rbp, rax ;# modify "mx"
|
||||
mov rbx, rbp ;# ebx = ma
|
||||
shr rbx, 38
|
||||
and ebx, RANDOMX_DATASET_BASE_MASK / 64 ;# ebx = Dataset block number
|
||||
;# add ebx, datasetOffset / 64
|
||||
;# call 32768
|
||||
|
|
@ -0,0 +1,24 @@
|
|||
r0_mul:
|
||||
;#/ 6364136223846793005
|
||||
db 45, 127, 149, 76, 45, 244, 81, 88
|
||||
r1_add:
|
||||
;#/ 9298411001130361340
|
||||
db 252, 161, 245, 89, 138, 151, 10, 129
|
||||
r2_add:
|
||||
;#/ 12065312585734608966
|
||||
db 70, 216, 194, 56, 223, 153, 112, 167
|
||||
r3_add:
|
||||
;#/ 9306329213124626780
|
||||
db 92, 73, 34, 191, 28, 185, 38, 129
|
||||
r4_add:
|
||||
;#/ 5281919268842080866
|
||||
db 98, 138, 159, 23, 151, 37, 77, 73
|
||||
r5_add:
|
||||
;#/ 10536153434571861004
|
||||
db 12, 236, 170, 206, 185, 239, 55, 146
|
||||
r6_add:
|
||||
;#/ 3398623926847679864
|
||||
db 120, 45, 230, 108, 116, 86, 42, 47
|
||||
r7_add:
|
||||
;#/ 9549104520008361294
|
||||
db 78, 229, 44, 182, 247, 59, 133, 132
|
||||
8
app/src/main/cpp/randomx/src/asm/program_sshash_load.inc
Normal file
8
app/src/main/cpp/randomx/src/asm/program_sshash_load.inc
Normal file
|
|
@ -0,0 +1,8 @@
|
|||
xor r8, qword ptr [rbx+0]
|
||||
xor r9, qword ptr [rbx+8]
|
||||
xor r10, qword ptr [rbx+16]
|
||||
xor r11, qword ptr [rbx+24]
|
||||
xor r12, qword ptr [rbx+32]
|
||||
xor r13, qword ptr [rbx+40]
|
||||
xor r14, qword ptr [rbx+48]
|
||||
xor r15, qword ptr [rbx+56]
|
||||
|
|
@ -0,0 +1,4 @@
|
|||
and rbx, RANDOMX_CACHE_MASK
|
||||
shl rbx, 6
|
||||
add rbx, rdi
|
||||
prefetchnta byte ptr [rbx]
|
||||
|
|
@ -0,0 +1,6 @@
|
|||
mantissaMask:
|
||||
db 255, 255, 255, 255, 255, 255, 255, 0, 255, 255, 255, 255, 255, 255, 255, 0
|
||||
exp240:
|
||||
db 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
|
||||
scaleMask:
|
||||
db 0, 0, 0, 0, 0, 0, 240, 128, 0, 0, 0, 0, 0, 0, 240, 128
|
||||
7
app/src/main/cpp/randomx/src/asm/randomx_reciprocal.inc
Normal file
7
app/src/main/cpp/randomx/src/asm/randomx_reciprocal.inc
Normal file
|
|
@ -0,0 +1,7 @@
|
|||
mov edx, 1
|
||||
mov r8, rcx
|
||||
xor eax, eax
|
||||
bsr rcx, rcx
|
||||
shl rdx, cl
|
||||
div r8
|
||||
ret
|
||||
611
app/src/main/cpp/randomx/src/assembly_generator_x86.cpp
Normal file
611
app/src/main/cpp/randomx/src/assembly_generator_x86.cpp
Normal file
|
|
@ -0,0 +1,611 @@
|
|||
/*
|
||||
Copyright (c) 2018-2019, tevador <tevador@gmail.com>
|
||||
|
||||
All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted provided that the following conditions are met:
|
||||
* Redistributions of source code must retain the above copyright
|
||||
notice, this list of conditions and the following disclaimer.
|
||||
* Redistributions in binary form must reproduce the above copyright
|
||||
notice, this list of conditions and the following disclaimer in the
|
||||
documentation and/or other materials provided with the distribution.
|
||||
* Neither the name of the copyright holder nor the
|
||||
names of its contributors may be used to endorse or promote products
|
||||
derived from this software without specific prior written permission.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
|
||||
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
|
||||
FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
||||
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
|
||||
CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
|
||||
OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
#include <climits>
|
||||
#include "assembly_generator_x86.hpp"
|
||||
#include "common.hpp"
|
||||
#include "reciprocal.h"
|
||||
#include "program.hpp"
|
||||
#include "superscalar.hpp"
|
||||
|
||||
namespace randomx {
|
||||
|
||||
static const char* regR[] = { "r8", "r9", "r10", "r11", "r12", "r13", "r14", "r15" };
|
||||
static const char* regR32[] = { "r8d", "r9d", "r10d", "r11d", "r12d", "r13d", "r14d", "r15d" };
|
||||
static const char* regFE[] = { "xmm0", "xmm1", "xmm2", "xmm3", "xmm4", "xmm5", "xmm6", "xmm7" };
|
||||
static const char* regF[] = { "xmm0", "xmm1", "xmm2", "xmm3" };
|
||||
static const char* regE[] = { "xmm4", "xmm5", "xmm6", "xmm7" };
|
||||
static const char* regA[] = { "xmm8", "xmm9", "xmm10", "xmm11" };
|
||||
|
||||
static const char* tempRegx = "xmm12";
|
||||
static const char* mantissaMaskReg = "xmm13";
|
||||
static const char* exponentMaskReg = "xmm14";
|
||||
static const char* scaleMaskReg = "xmm15";
|
||||
static const char* regIc = "rbx";
|
||||
static const char* regIc32 = "ebx";
|
||||
static const char* regIc8 = "bl";
|
||||
static const char* regScratchpadAddr = "rsi";
|
||||
|
||||
void AssemblyGeneratorX86::generateProgram(Program& prog) {
|
||||
for (unsigned i = 0; i < RegistersCount; ++i) {
|
||||
registerUsage[i] = -1;
|
||||
}
|
||||
asmCode.str(std::string()); //clear
|
||||
for (unsigned i = 0; i < prog.getSize(); ++i) {
|
||||
asmCode << "randomx_isn_" << i << ":" << std::endl;
|
||||
Instruction& instr = prog(i);
|
||||
instr.src %= RegistersCount;
|
||||
instr.dst %= RegistersCount;
|
||||
generateCode(instr, i);
|
||||
}
|
||||
}
|
||||
|
||||
void AssemblyGeneratorX86::generateAsm(SuperscalarProgram& prog) {
|
||||
asmCode.str(std::string()); //clear
|
||||
#ifdef RANDOMX_ALIGN
|
||||
asmCode << "ALIGN 16" << std::endl;
|
||||
#endif
|
||||
for (unsigned i = 0; i < prog.getSize(); ++i) {
|
||||
Instruction& instr = prog(i);
|
||||
switch ((SuperscalarInstructionType)instr.opcode)
|
||||
{
|
||||
case SuperscalarInstructionType::ISUB_R:
|
||||
asmCode << "sub " << regR[instr.dst] << ", " << regR[instr.src] << std::endl;
|
||||
break;
|
||||
case SuperscalarInstructionType::IXOR_R:
|
||||
asmCode << "xor " << regR[instr.dst] << ", " << regR[instr.src] << std::endl;
|
||||
break;
|
||||
case SuperscalarInstructionType::IADD_RS:
|
||||
asmCode << "lea " << regR[instr.dst] << ", [" << regR[instr.dst] << "+" << regR[instr.src] << "*" << (1 << (instr.getModShift())) << "]" << std::endl;
|
||||
break;
|
||||
case SuperscalarInstructionType::IMUL_R:
|
||||
asmCode << "imul " << regR[instr.dst] << ", " << regR[instr.src] << std::endl;
|
||||
break;
|
||||
case SuperscalarInstructionType::IROR_C:
|
||||
asmCode << "ror " << regR[instr.dst] << ", " << instr.getImm32() << std::endl;
|
||||
break;
|
||||
case SuperscalarInstructionType::IADD_C7:
|
||||
asmCode << "add " << regR[instr.dst] << ", " << (int32_t)instr.getImm32() << std::endl;
|
||||
break;
|
||||
case SuperscalarInstructionType::IXOR_C7:
|
||||
asmCode << "xor " << regR[instr.dst] << ", " << (int32_t)instr.getImm32() << std::endl;
|
||||
break;
|
||||
case SuperscalarInstructionType::IADD_C8:
|
||||
asmCode << "add " << regR[instr.dst] << ", " << (int32_t)instr.getImm32() << std::endl;
|
||||
#ifdef RANDOMX_ALIGN
|
||||
asmCode << "nop" << std::endl;
|
||||
#endif
|
||||
break;
|
||||
case SuperscalarInstructionType::IXOR_C8:
|
||||
asmCode << "xor " << regR[instr.dst] << ", " << (int32_t)instr.getImm32() << std::endl;
|
||||
#ifdef RANDOMX_ALIGN
|
||||
asmCode << "nop" << std::endl;
|
||||
#endif
|
||||
break;
|
||||
case SuperscalarInstructionType::IADD_C9:
|
||||
asmCode << "add " << regR[instr.dst] << ", " << (int32_t)instr.getImm32() << std::endl;
|
||||
#ifdef RANDOMX_ALIGN
|
||||
asmCode << "xchg ax, ax ;nop" << std::endl;
|
||||
#endif
|
||||
break;
|
||||
case SuperscalarInstructionType::IXOR_C9:
|
||||
asmCode << "xor " << regR[instr.dst] << ", " << (int32_t)instr.getImm32() << std::endl;
|
||||
#ifdef RANDOMX_ALIGN
|
||||
asmCode << "xchg ax, ax ;nop" << std::endl;
|
||||
#endif
|
||||
break;
|
||||
case SuperscalarInstructionType::IMULH_R:
|
||||
asmCode << "mov rax, " << regR[instr.dst] << std::endl;
|
||||
asmCode << "mul " << regR[instr.src] << std::endl;
|
||||
asmCode << "mov " << regR[instr.dst] << ", rdx" << std::endl;
|
||||
break;
|
||||
case SuperscalarInstructionType::ISMULH_R:
|
||||
asmCode << "mov rax, " << regR[instr.dst] << std::endl;
|
||||
asmCode << "imul " << regR[instr.src] << std::endl;
|
||||
asmCode << "mov " << regR[instr.dst] << ", rdx" << std::endl;
|
||||
break;
|
||||
case SuperscalarInstructionType::IMUL_RCP:
|
||||
asmCode << "mov rax, " << (int64_t)randomx_reciprocal(instr.getImm32()) << std::endl;
|
||||
asmCode << "imul " << regR[instr.dst] << ", rax" << std::endl;
|
||||
break;
|
||||
default:
|
||||
UNREACHABLE;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void AssemblyGeneratorX86::generateC(SuperscalarProgram& prog) {
|
||||
asmCode.str(std::string()); //clear
|
||||
asmCode << "#include <stdint.h>" << std::endl;
|
||||
asmCode << "#if defined(__SIZEOF_INT128__)" << std::endl;
|
||||
asmCode << " static inline uint64_t mulh(uint64_t a, uint64_t b) {" << std::endl;
|
||||
asmCode << " return ((unsigned __int128)a * b) >> 64;" << std::endl;
|
||||
asmCode << " }" << std::endl;
|
||||
asmCode << " static inline int64_t smulh(int64_t a, int64_t b) {" << std::endl;
|
||||
asmCode << " return ((__int128)a * b) >> 64;" << std::endl;
|
||||
asmCode << " }" << std::endl;
|
||||
asmCode << " #define HAVE_MULH" << std::endl;
|
||||
asmCode << " #define HAVE_SMULH" << std::endl;
|
||||
asmCode << "#endif" << std::endl;
|
||||
asmCode << "#if defined(_MSC_VER)" << std::endl;
|
||||
asmCode << " #define HAS_VALUE(X) X ## 0" << std::endl;
|
||||
asmCode << " #define EVAL_DEFINE(X) HAS_VALUE(X)" << std::endl;
|
||||
asmCode << " #include <intrin.h>" << std::endl;
|
||||
asmCode << " #include <stdlib.h>" << std::endl;
|
||||
asmCode << " static __inline uint64_t rotr(uint64_t x , int c) {" << std::endl;
|
||||
asmCode << " return _rotr64(x, c);" << std::endl;
|
||||
asmCode << " }" << std::endl;
|
||||
asmCode << " #define HAVE_ROTR" << std::endl;
|
||||
asmCode << " #if EVAL_DEFINE(__MACHINEARM64_X64(1))" << std::endl;
|
||||
asmCode << " static __inline uint64_t mulh(uint64_t a, uint64_t b) {" << std::endl;
|
||||
asmCode << " return __umulh(a, b);" << std::endl;
|
||||
asmCode << " }" << std::endl;
|
||||
asmCode << " #define HAVE_MULH" << std::endl;
|
||||
asmCode << " #endif" << std::endl;
|
||||
asmCode << " #if EVAL_DEFINE(__MACHINEX64(1))" << std::endl;
|
||||
asmCode << " static __inline int64_t smulh(int64_t a, int64_t b) {" << std::endl;
|
||||
asmCode << " int64_t hi;" << std::endl;
|
||||
asmCode << " _mul128(a, b, &hi);" << std::endl;
|
||||
asmCode << " return hi;" << std::endl;
|
||||
asmCode << " }" << std::endl;
|
||||
asmCode << " #define HAVE_SMULH" << std::endl;
|
||||
asmCode << " #endif" << std::endl;
|
||||
asmCode << "#endif" << std::endl;
|
||||
asmCode << "#ifndef HAVE_ROTR" << std::endl;
|
||||
asmCode << " static inline uint64_t rotr(uint64_t a, int b) {" << std::endl;
|
||||
asmCode << " return (a >> b) | (a << (64 - b));" << std::endl;
|
||||
asmCode << " }" << std::endl;
|
||||
asmCode << " #define HAVE_ROTR" << std::endl;
|
||||
asmCode << "#endif" << std::endl;
|
||||
asmCode << "#if !defined(HAVE_MULH) || !defined(HAVE_SMULH) || !defined(HAVE_ROTR)" << std::endl;
|
||||
asmCode << " #error \"Required functions are not defined\"" << std::endl;
|
||||
asmCode << "#endif" << std::endl;
|
||||
asmCode << "void superScalar(uint64_t r[8]) {" << std::endl;
|
||||
asmCode << "uint64_t r8 = r[0], r9 = r[1], r10 = r[2], r11 = r[3], r12 = r[4], r13 = r[5], r14 = r[6], r15 = r[7];" << std::endl;
|
||||
for (unsigned i = 0; i < prog.getSize(); ++i) {
|
||||
Instruction& instr = prog(i);
|
||||
switch ((SuperscalarInstructionType)instr.opcode)
|
||||
{
|
||||
case SuperscalarInstructionType::ISUB_R:
|
||||
asmCode << regR[instr.dst] << " -= " << regR[instr.src] << ";" << std::endl;
|
||||
break;
|
||||
case SuperscalarInstructionType::IXOR_R:
|
||||
asmCode << regR[instr.dst] << " ^= " << regR[instr.src] << ";" << std::endl;
|
||||
break;
|
||||
case SuperscalarInstructionType::IADD_RS:
|
||||
asmCode << regR[instr.dst] << " += " << regR[instr.src] << "*" << (1 << (instr.getModShift())) << ";" << std::endl;
|
||||
break;
|
||||
case SuperscalarInstructionType::IMUL_R:
|
||||
asmCode << regR[instr.dst] << " *= " << regR[instr.src] << ";" << std::endl;
|
||||
break;
|
||||
case SuperscalarInstructionType::IROR_C:
|
||||
asmCode << regR[instr.dst] << " = rotr(" << regR[instr.dst] << ", " << instr.getImm32() << ");" << std::endl;
|
||||
break;
|
||||
case SuperscalarInstructionType::IADD_C7:
|
||||
case SuperscalarInstructionType::IADD_C8:
|
||||
case SuperscalarInstructionType::IADD_C9:
|
||||
asmCode << regR[instr.dst] << " += " << (int32_t)instr.getImm32() << ";" << std::endl;
|
||||
break;
|
||||
case SuperscalarInstructionType::IXOR_C7:
|
||||
case SuperscalarInstructionType::IXOR_C8:
|
||||
case SuperscalarInstructionType::IXOR_C9:
|
||||
asmCode << regR[instr.dst] << " ^= " << (int32_t)instr.getImm32() << ";" << std::endl;
|
||||
break;
|
||||
case SuperscalarInstructionType::IMULH_R:
|
||||
asmCode << regR[instr.dst] << " = mulh(" << regR[instr.dst] << ", " << regR[instr.src] << ");" << std::endl;
|
||||
break;
|
||||
case SuperscalarInstructionType::ISMULH_R:
|
||||
asmCode << regR[instr.dst] << " = smulh(" << regR[instr.dst] << ", " << regR[instr.src] << ");" << std::endl;
|
||||
break;
|
||||
case SuperscalarInstructionType::IMUL_RCP:
|
||||
asmCode << regR[instr.dst] << " *= " << (int64_t)randomx_reciprocal(instr.getImm32()) << ";" << std::endl;
|
||||
break;
|
||||
default:
|
||||
UNREACHABLE;
|
||||
}
|
||||
}
|
||||
asmCode << "r[0] = r8; r[1] = r9; r[2] = r10; r[3] = r11; r[4] = r12; r[5] = r13; r[6] = r14; r[7] = r15;" << std::endl;
|
||||
asmCode << "}" << std::endl;
|
||||
}
|
||||
|
||||
void AssemblyGeneratorX86::traceint(Instruction& instr) {
|
||||
if (trace) {
|
||||
asmCode << "\tpush " << regR[instr.dst] << std::endl;
|
||||
}
|
||||
}
|
||||
|
||||
void AssemblyGeneratorX86::traceflt(Instruction& instr) {
|
||||
if (trace) {
|
||||
asmCode << "\tpush 0" << std::endl;
|
||||
}
|
||||
}
|
||||
|
||||
void AssemblyGeneratorX86::tracenop(Instruction& instr) {
|
||||
if (trace) {
|
||||
asmCode << "\tpush 0" << std::endl;
|
||||
}
|
||||
}
|
||||
|
||||
void AssemblyGeneratorX86::generateCode(Instruction& instr, int i) {
|
||||
asmCode << "\t; " << instr;
|
||||
auto generator = engine[instr.opcode];
|
||||
(this->*generator)(instr, i);
|
||||
}
|
||||
|
||||
void AssemblyGeneratorX86::genAddressReg(Instruction& instr, const char* reg = "eax") {
|
||||
asmCode << "\tlea " << reg << ", [" << regR32[instr.src] << std::showpos << (int32_t)instr.getImm32() << std::noshowpos << "]" << std::endl;
|
||||
asmCode << "\tand " << reg << ", " << ((instr.getModMem()) ? ScratchpadL1Mask : ScratchpadL2Mask) << std::endl;
|
||||
}
|
||||
|
||||
void AssemblyGeneratorX86::genAddressRegDst(Instruction& instr, int maskAlign = 8) {
|
||||
asmCode << "\tlea eax, [" << regR32[instr.dst] << std::showpos << (int32_t)instr.getImm32() << std::noshowpos << "]" << std::endl;
|
||||
int mask;
|
||||
if (instr.getModCond() < StoreL3Condition) {
|
||||
mask = instr.getModMem() ? ScratchpadL1Mask : ScratchpadL2Mask;
|
||||
}
|
||||
else {
|
||||
mask = ScratchpadL3Mask;
|
||||
}
|
||||
asmCode << "\tand eax" << ", " << (mask & (-maskAlign)) << std::endl;
|
||||
}
|
||||
|
||||
int32_t AssemblyGeneratorX86::genAddressImm(Instruction& instr) {
|
||||
return (int32_t)instr.getImm32() & ScratchpadL3Mask;
|
||||
}
|
||||
|
||||
void AssemblyGeneratorX86::h_IADD_RS(Instruction& instr, int i) {
|
||||
registerUsage[instr.dst] = i;
|
||||
if(instr.dst == RegisterNeedsDisplacement)
|
||||
asmCode << "\tlea " << regR[instr.dst] << ", [" << regR[instr.dst] << "+" << regR[instr.src] << "*" << (1 << (instr.getModShift())) << std::showpos << (int32_t)instr.getImm32() << std::noshowpos << "]" << std::endl;
|
||||
else
|
||||
asmCode << "\tlea " << regR[instr.dst] << ", [" << regR[instr.dst] << "+" << regR[instr.src] << "*" << (1 << (instr.getModShift())) << "]" << std::endl;
|
||||
traceint(instr);
|
||||
}
|
||||
|
||||
void AssemblyGeneratorX86::h_IADD_M(Instruction& instr, int i) {
|
||||
registerUsage[instr.dst] = i;
|
||||
if (instr.src != instr.dst) {
|
||||
genAddressReg(instr);
|
||||
asmCode << "\tadd " << regR[instr.dst] << ", qword ptr [" << regScratchpadAddr << "+rax]" << std::endl;
|
||||
}
|
||||
else {
|
||||
asmCode << "\tadd " << regR[instr.dst] << ", qword ptr [" << regScratchpadAddr << "+" << genAddressImm(instr) << "]" << std::endl;
|
||||
}
|
||||
traceint(instr);
|
||||
}
|
||||
|
||||
void AssemblyGeneratorX86::h_ISUB_R(Instruction& instr, int i) {
|
||||
registerUsage[instr.dst] = i;
|
||||
if (instr.src != instr.dst) {
|
||||
asmCode << "\tsub " << regR[instr.dst] << ", " << regR[instr.src] << std::endl;
|
||||
}
|
||||
else {
|
||||
asmCode << "\tsub " << regR[instr.dst] << ", " << (int32_t)instr.getImm32() << std::endl;
|
||||
}
|
||||
traceint(instr);
|
||||
}
|
||||
|
||||
void AssemblyGeneratorX86::h_ISUB_M(Instruction& instr, int i) {
|
||||
registerUsage[instr.dst] = i;
|
||||
if (instr.src != instr.dst) {
|
||||
genAddressReg(instr);
|
||||
asmCode << "\tsub " << regR[instr.dst] << ", qword ptr [" << regScratchpadAddr << "+rax]" << std::endl;
|
||||
}
|
||||
else {
|
||||
asmCode << "\tsub " << regR[instr.dst] << ", qword ptr [" << regScratchpadAddr << "+" << genAddressImm(instr) << "]" << std::endl;
|
||||
}
|
||||
traceint(instr);
|
||||
}
|
||||
|
||||
void AssemblyGeneratorX86::h_IMUL_R(Instruction& instr, int i) {
|
||||
registerUsage[instr.dst] = i;
|
||||
if (instr.src != instr.dst) {
|
||||
asmCode << "\timul " << regR[instr.dst] << ", " << regR[instr.src] << std::endl;
|
||||
}
|
||||
else {
|
||||
asmCode << "\timul " << regR[instr.dst] << ", " << (int32_t)instr.getImm32() << std::endl;
|
||||
}
|
||||
traceint(instr);
|
||||
}
|
||||
|
||||
void AssemblyGeneratorX86::h_IMUL_M(Instruction& instr, int i) {
|
||||
registerUsage[instr.dst] = i;
|
||||
if (instr.src != instr.dst) {
|
||||
genAddressReg(instr);
|
||||
asmCode << "\timul " << regR[instr.dst] << ", qword ptr [" << regScratchpadAddr << "+rax]" << std::endl;
|
||||
}
|
||||
else {
|
||||
asmCode << "\timul " << regR[instr.dst] << ", qword ptr [" << regScratchpadAddr << "+" << genAddressImm(instr) << "]" << std::endl;
|
||||
}
|
||||
traceint(instr);
|
||||
}
|
||||
|
||||
void AssemblyGeneratorX86::h_IMULH_R(Instruction& instr, int i) {
|
||||
registerUsage[instr.dst] = i;
|
||||
asmCode << "\tmov rax, " << regR[instr.dst] << std::endl;
|
||||
asmCode << "\tmul " << regR[instr.src] << std::endl;
|
||||
asmCode << "\tmov " << regR[instr.dst] << ", rdx" << std::endl;
|
||||
traceint(instr);
|
||||
}
|
||||
|
||||
void AssemblyGeneratorX86::h_IMULH_M(Instruction& instr, int i) {
|
||||
registerUsage[instr.dst] = i;
|
||||
if (instr.src != instr.dst) {
|
||||
genAddressReg(instr, "ecx");
|
||||
asmCode << "\tmov rax, " << regR[instr.dst] << std::endl;
|
||||
asmCode << "\tmul qword ptr [" << regScratchpadAddr << "+rcx]" << std::endl;
|
||||
}
|
||||
else {
|
||||
asmCode << "\tmov rax, " << regR[instr.dst] << std::endl;
|
||||
asmCode << "\tmul qword ptr [" << regScratchpadAddr << "+" << genAddressImm(instr) << "]" << std::endl;
|
||||
}
|
||||
asmCode << "\tmov " << regR[instr.dst] << ", rdx" << std::endl;
|
||||
traceint(instr);
|
||||
}
|
||||
|
||||
void AssemblyGeneratorX86::h_ISMULH_R(Instruction& instr, int i) {
|
||||
registerUsage[instr.dst] = i;
|
||||
asmCode << "\tmov rax, " << regR[instr.dst] << std::endl;
|
||||
asmCode << "\timul " << regR[instr.src] << std::endl;
|
||||
asmCode << "\tmov " << regR[instr.dst] << ", rdx" << std::endl;
|
||||
traceint(instr);
|
||||
}
|
||||
|
||||
void AssemblyGeneratorX86::h_ISMULH_M(Instruction& instr, int i) {
|
||||
registerUsage[instr.dst] = i;
|
||||
if (instr.src != instr.dst) {
|
||||
genAddressReg(instr, "ecx");
|
||||
asmCode << "\tmov rax, " << regR[instr.dst] << std::endl;
|
||||
asmCode << "\timul qword ptr [" << regScratchpadAddr << "+rcx]" << std::endl;
|
||||
}
|
||||
else {
|
||||
asmCode << "\tmov rax, " << regR[instr.dst] << std::endl;
|
||||
asmCode << "\timul qword ptr [" << regScratchpadAddr << "+" << genAddressImm(instr) << "]" << std::endl;
|
||||
}
|
||||
asmCode << "\tmov " << regR[instr.dst] << ", rdx" << std::endl;
|
||||
traceint(instr);
|
||||
}
|
||||
|
||||
void AssemblyGeneratorX86::h_INEG_R(Instruction& instr, int i) {
|
||||
registerUsage[instr.dst] = i;
|
||||
asmCode << "\tneg " << regR[instr.dst] << std::endl;
|
||||
traceint(instr);
|
||||
}
|
||||
|
||||
void AssemblyGeneratorX86::h_IXOR_R(Instruction& instr, int i) {
|
||||
registerUsage[instr.dst] = i;
|
||||
if (instr.src != instr.dst) {
|
||||
asmCode << "\txor " << regR[instr.dst] << ", " << regR[instr.src] << std::endl;
|
||||
}
|
||||
else {
|
||||
asmCode << "\txor " << regR[instr.dst] << ", " << (int32_t)instr.getImm32() << std::endl;
|
||||
}
|
||||
traceint(instr);
|
||||
}
|
||||
|
||||
void AssemblyGeneratorX86::h_IXOR_M(Instruction& instr, int i) {
|
||||
registerUsage[instr.dst] = i;
|
||||
if (instr.src != instr.dst) {
|
||||
genAddressReg(instr);
|
||||
asmCode << "\txor " << regR[instr.dst] << ", qword ptr [" << regScratchpadAddr << "+rax]" << std::endl;
|
||||
}
|
||||
else {
|
||||
asmCode << "\txor " << regR[instr.dst] << ", qword ptr [" << regScratchpadAddr << "+" << genAddressImm(instr) << "]" << std::endl;
|
||||
}
|
||||
traceint(instr);
|
||||
}
|
||||
|
||||
void AssemblyGeneratorX86::h_IROR_R(Instruction& instr, int i) {
|
||||
registerUsage[instr.dst] = i;
|
||||
if (instr.src != instr.dst) {
|
||||
asmCode << "\tmov ecx, " << regR32[instr.src] << std::endl;
|
||||
asmCode << "\tror " << regR[instr.dst] << ", cl" << std::endl;
|
||||
}
|
||||
else {
|
||||
asmCode << "\tror " << regR[instr.dst] << ", " << (instr.getImm32() & 63) << std::endl;
|
||||
}
|
||||
traceint(instr);
|
||||
}
|
||||
|
||||
void AssemblyGeneratorX86::h_IROL_R(Instruction& instr, int i) {
|
||||
registerUsage[instr.dst] = i;
|
||||
if (instr.src != instr.dst) {
|
||||
asmCode << "\tmov ecx, " << regR32[instr.src] << std::endl;
|
||||
asmCode << "\trol " << regR[instr.dst] << ", cl" << std::endl;
|
||||
}
|
||||
else {
|
||||
asmCode << "\trol " << regR[instr.dst] << ", " << (instr.getImm32() & 63) << std::endl;
|
||||
}
|
||||
traceint(instr);
|
||||
}
|
||||
|
||||
void AssemblyGeneratorX86::h_IMUL_RCP(Instruction& instr, int i) {
|
||||
const uint32_t divisor = instr.getImm32();
|
||||
if (!isZeroOrPowerOf2(divisor)) {
|
||||
registerUsage[instr.dst] = i;
|
||||
asmCode << "\tmov rax, " << randomx_reciprocal(divisor) << std::endl;
|
||||
asmCode << "\timul " << regR[instr.dst] << ", rax" << std::endl;
|
||||
traceint(instr);
|
||||
}
|
||||
else {
|
||||
tracenop(instr);
|
||||
}
|
||||
}
|
||||
|
||||
void AssemblyGeneratorX86::h_ISWAP_R(Instruction& instr, int i) {
|
||||
if (instr.src != instr.dst) {
|
||||
registerUsage[instr.dst] = i;
|
||||
registerUsage[instr.src] = i;
|
||||
asmCode << "\txchg " << regR[instr.dst] << ", " << regR[instr.src] << std::endl;
|
||||
traceint(instr);
|
||||
}
|
||||
else {
|
||||
tracenop(instr);
|
||||
}
|
||||
}
|
||||
|
||||
void AssemblyGeneratorX86::h_FSWAP_R(Instruction& instr, int i) {
|
||||
asmCode << "\tshufpd " << regFE[instr.dst] << ", " << regFE[instr.dst] << ", 1" << std::endl;
|
||||
traceflt(instr);
|
||||
}
|
||||
|
||||
void AssemblyGeneratorX86::h_FADD_R(Instruction& instr, int i) {
|
||||
instr.dst %= RegisterCountFlt;
|
||||
instr.src %= RegisterCountFlt;
|
||||
asmCode << "\taddpd " << regF[instr.dst] << ", " << regA[instr.src] << std::endl;
|
||||
traceflt(instr);
|
||||
}
|
||||
|
||||
void AssemblyGeneratorX86::h_FADD_M(Instruction& instr, int i) {
|
||||
instr.dst %= RegisterCountFlt;
|
||||
genAddressReg(instr);
|
||||
asmCode << "\tcvtdq2pd " << tempRegx << ", qword ptr [" << regScratchpadAddr << "+rax]" << std::endl;
|
||||
asmCode << "\taddpd " << regF[instr.dst] << ", " << tempRegx << std::endl;
|
||||
traceflt(instr);
|
||||
}
|
||||
|
||||
void AssemblyGeneratorX86::h_FSUB_R(Instruction& instr, int i) {
|
||||
instr.dst %= RegisterCountFlt;
|
||||
instr.src %= RegisterCountFlt;
|
||||
asmCode << "\tsubpd " << regF[instr.dst] << ", " << regA[instr.src] << std::endl;
|
||||
traceflt(instr);
|
||||
}
|
||||
|
||||
void AssemblyGeneratorX86::h_FSUB_M(Instruction& instr, int i) {
|
||||
instr.dst %= RegisterCountFlt;
|
||||
genAddressReg(instr);
|
||||
asmCode << "\tcvtdq2pd " << tempRegx << ", qword ptr [" << regScratchpadAddr << "+rax]" << std::endl;
|
||||
asmCode << "\tsubpd " << regF[instr.dst] << ", " << tempRegx << std::endl;
|
||||
traceflt(instr);
|
||||
}
|
||||
|
||||
void AssemblyGeneratorX86::h_FSCAL_R(Instruction& instr, int i) {
|
||||
instr.dst %= RegisterCountFlt;
|
||||
asmCode << "\txorps " << regF[instr.dst] << ", " << scaleMaskReg << std::endl;
|
||||
traceflt(instr);
|
||||
}
|
||||
|
||||
void AssemblyGeneratorX86::h_FMUL_R(Instruction& instr, int i) {
|
||||
instr.dst %= RegisterCountFlt;
|
||||
instr.src %= RegisterCountFlt;
|
||||
asmCode << "\tmulpd " << regE[instr.dst] << ", " << regA[instr.src] << std::endl;
|
||||
traceflt(instr);
|
||||
}
|
||||
|
||||
void AssemblyGeneratorX86::h_FDIV_M(Instruction& instr, int i) {
|
||||
instr.dst %= RegisterCountFlt;
|
||||
genAddressReg(instr);
|
||||
asmCode << "\tcvtdq2pd " << tempRegx << ", qword ptr [" << regScratchpadAddr << "+rax]" << std::endl;
|
||||
asmCode << "\tandps " << tempRegx << ", " << mantissaMaskReg << std::endl;
|
||||
asmCode << "\torps " << tempRegx << ", " << exponentMaskReg << std::endl;
|
||||
asmCode << "\tdivpd " << regE[instr.dst] << ", " << tempRegx << std::endl;
|
||||
traceflt(instr);
|
||||
}
|
||||
|
||||
void AssemblyGeneratorX86::h_FSQRT_R(Instruction& instr, int i) {
|
||||
instr.dst %= RegisterCountFlt;
|
||||
asmCode << "\tsqrtpd " << regE[instr.dst] << ", " << regE[instr.dst] << std::endl;
|
||||
traceflt(instr);
|
||||
}
|
||||
|
||||
void AssemblyGeneratorX86::h_CFROUND(Instruction& instr, int i) {
|
||||
asmCode << "\tmov rax, " << regR[instr.src] << std::endl;
|
||||
int rotate = (13 - (instr.getImm32() & 63)) & 63;
|
||||
if (rotate != 0)
|
||||
asmCode << "\trol rax, " << rotate << std::endl;
|
||||
asmCode << "\tand eax, 24576" << std::endl;
|
||||
asmCode << "\tor eax, 40896" << std::endl;
|
||||
asmCode << "\tpush rax" << std::endl;
|
||||
asmCode << "\tldmxcsr dword ptr [rsp]" << std::endl;
|
||||
asmCode << "\tpop rax" << std::endl;
|
||||
tracenop(instr);
|
||||
}
|
||||
|
||||
void AssemblyGeneratorX86::h_CBRANCH(Instruction& instr, int i) {
|
||||
int reg = instr.dst;
|
||||
int target = registerUsage[reg] + 1;
|
||||
int shift = instr.getModCond() + ConditionOffset;
|
||||
int32_t imm = instr.getImm32() | (1L << shift);
|
||||
if (ConditionOffset > 0 || shift > 0)
|
||||
imm &= ~(1L << (shift - 1));
|
||||
asmCode << "\tadd " << regR[reg] << ", " << imm << std::endl;
|
||||
asmCode << "\ttest " << regR[reg] << ", " << (ConditionMask << shift) << std::endl;
|
||||
asmCode << "\tjz randomx_isn_" << target << std::endl;
|
||||
//mark all registers as used
|
||||
for (unsigned j = 0; j < RegistersCount; ++j) {
|
||||
registerUsage[j] = i;
|
||||
}
|
||||
}
|
||||
|
||||
void AssemblyGeneratorX86::h_ISTORE(Instruction& instr, int i) {
|
||||
genAddressRegDst(instr);
|
||||
asmCode << "\tmov qword ptr [" << regScratchpadAddr << "+rax], " << regR[instr.src] << std::endl;
|
||||
tracenop(instr);
|
||||
}
|
||||
|
||||
void AssemblyGeneratorX86::h_NOP(Instruction& instr, int i) {
|
||||
asmCode << "\tnop" << std::endl;
|
||||
tracenop(instr);
|
||||
}
|
||||
|
||||
#include "instruction_weights.hpp"
|
||||
#define INST_HANDLE(x) REPN(&AssemblyGeneratorX86::h_##x, WT(x))
|
||||
|
||||
InstructionGenerator AssemblyGeneratorX86::engine[256] = {
|
||||
INST_HANDLE(IADD_RS)
|
||||
INST_HANDLE(IADD_M)
|
||||
INST_HANDLE(ISUB_R)
|
||||
INST_HANDLE(ISUB_M)
|
||||
INST_HANDLE(IMUL_R)
|
||||
INST_HANDLE(IMUL_M)
|
||||
INST_HANDLE(IMULH_R)
|
||||
INST_HANDLE(IMULH_M)
|
||||
INST_HANDLE(ISMULH_R)
|
||||
INST_HANDLE(ISMULH_M)
|
||||
INST_HANDLE(IMUL_RCP)
|
||||
INST_HANDLE(INEG_R)
|
||||
INST_HANDLE(IXOR_R)
|
||||
INST_HANDLE(IXOR_M)
|
||||
INST_HANDLE(IROR_R)
|
||||
INST_HANDLE(IROL_R)
|
||||
INST_HANDLE(ISWAP_R)
|
||||
INST_HANDLE(FSWAP_R)
|
||||
INST_HANDLE(FADD_R)
|
||||
INST_HANDLE(FADD_M)
|
||||
INST_HANDLE(FSUB_R)
|
||||
INST_HANDLE(FSUB_M)
|
||||
INST_HANDLE(FSCAL_R)
|
||||
INST_HANDLE(FMUL_R)
|
||||
INST_HANDLE(FDIV_M)
|
||||
INST_HANDLE(FSQRT_R)
|
||||
INST_HANDLE(CBRANCH)
|
||||
INST_HANDLE(CFROUND)
|
||||
INST_HANDLE(ISTORE)
|
||||
INST_HANDLE(NOP)
|
||||
};
|
||||
}
|
||||
94
app/src/main/cpp/randomx/src/assembly_generator_x86.hpp
Normal file
94
app/src/main/cpp/randomx/src/assembly_generator_x86.hpp
Normal file
|
|
@ -0,0 +1,94 @@
|
|||
/*
|
||||
Copyright (c) 2018-2019, tevador <tevador@gmail.com>
|
||||
|
||||
All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted provided that the following conditions are met:
|
||||
* Redistributions of source code must retain the above copyright
|
||||
notice, this list of conditions and the following disclaimer.
|
||||
* Redistributions in binary form must reproduce the above copyright
|
||||
notice, this list of conditions and the following disclaimer in the
|
||||
documentation and/or other materials provided with the distribution.
|
||||
* Neither the name of the copyright holder nor the
|
||||
names of its contributors may be used to endorse or promote products
|
||||
derived from this software without specific prior written permission.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
|
||||
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
|
||||
FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
||||
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
|
||||
CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
|
||||
OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "common.hpp"
|
||||
#include <sstream>
|
||||
|
||||
namespace randomx {
|
||||
|
||||
class Program;
|
||||
class SuperscalarProgram;
|
||||
class AssemblyGeneratorX86;
|
||||
class Instruction;
|
||||
|
||||
typedef void(AssemblyGeneratorX86::*InstructionGenerator)(Instruction&, int);
|
||||
|
||||
class AssemblyGeneratorX86 {
|
||||
public:
|
||||
void generateProgram(Program& prog);
|
||||
void generateAsm(SuperscalarProgram& prog);
|
||||
void generateC(SuperscalarProgram& prog);
|
||||
void printCode(std::ostream& os) {
|
||||
os << asmCode.rdbuf();
|
||||
}
|
||||
private:
|
||||
void genAddressReg(Instruction&, const char*);
|
||||
void genAddressRegDst(Instruction&, int);
|
||||
int32_t genAddressImm(Instruction&);
|
||||
void generateCode(Instruction&, int);
|
||||
void traceint(Instruction&);
|
||||
void traceflt(Instruction&);
|
||||
void tracenop(Instruction&);
|
||||
void h_IADD_RS(Instruction&, int);
|
||||
void h_IADD_M(Instruction&, int);
|
||||
void h_ISUB_R(Instruction&, int);
|
||||
void h_ISUB_M(Instruction&, int);
|
||||
void h_IMUL_R(Instruction&, int);
|
||||
void h_IMUL_M(Instruction&, int);
|
||||
void h_IMULH_R(Instruction&, int);
|
||||
void h_IMULH_M(Instruction&, int);
|
||||
void h_ISMULH_R(Instruction&, int);
|
||||
void h_ISMULH_M(Instruction&, int);
|
||||
void h_IMUL_RCP(Instruction&, int);
|
||||
void h_INEG_R(Instruction&, int);
|
||||
void h_IXOR_R(Instruction&, int);
|
||||
void h_IXOR_M(Instruction&, int);
|
||||
void h_IROR_R(Instruction&, int);
|
||||
void h_IROL_R(Instruction&, int);
|
||||
void h_ISWAP_R(Instruction&, int);
|
||||
void h_FSWAP_R(Instruction&, int);
|
||||
void h_FADD_R(Instruction&, int);
|
||||
void h_FADD_M(Instruction&, int);
|
||||
void h_FSUB_R(Instruction&, int);
|
||||
void h_FSUB_M(Instruction&, int);
|
||||
void h_FSCAL_R(Instruction&, int);
|
||||
void h_FMUL_R(Instruction&, int);
|
||||
void h_FDIV_M(Instruction&, int);
|
||||
void h_FSQRT_R(Instruction&, int);
|
||||
void h_CBRANCH(Instruction&, int);
|
||||
void h_CFROUND(Instruction&, int);
|
||||
void h_ISTORE(Instruction&, int);
|
||||
void h_NOP(Instruction&, int);
|
||||
|
||||
static InstructionGenerator engine[256];
|
||||
std::stringstream asmCode;
|
||||
int registerUsage[RegistersCount];
|
||||
};
|
||||
}
|
||||
76
app/src/main/cpp/randomx/src/blake2/blake2-impl.h
Normal file
76
app/src/main/cpp/randomx/src/blake2/blake2-impl.h
Normal file
|
|
@ -0,0 +1,76 @@
|
|||
/*
|
||||
Copyright (c) 2018-2019, tevador <tevador@gmail.com>
|
||||
|
||||
All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted provided that the following conditions are met:
|
||||
* Redistributions of source code must retain the above copyright
|
||||
notice, this list of conditions and the following disclaimer.
|
||||
* Redistributions in binary form must reproduce the above copyright
|
||||
notice, this list of conditions and the following disclaimer in the
|
||||
documentation and/or other materials provided with the distribution.
|
||||
* Neither the name of the copyright holder nor the
|
||||
names of its contributors may be used to endorse or promote products
|
||||
derived from this software without specific prior written permission.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
|
||||
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
|
||||
FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
||||
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
|
||||
CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
|
||||
OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
/* Original code from Argon2 reference source code package used under CC0 Licence
|
||||
* https://github.com/P-H-C/phc-winner-argon2
|
||||
* Copyright 2015
|
||||
* Daniel Dinu, Dmitry Khovratovich, Jean-Philippe Aumasson, and Samuel Neves
|
||||
*/
|
||||
|
||||
#ifndef PORTABLE_BLAKE2_IMPL_H
|
||||
#define PORTABLE_BLAKE2_IMPL_H
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
#include "endian.h"
|
||||
|
||||
static FORCE_INLINE uint64_t load48(const void *src) {
|
||||
const uint8_t *p = (const uint8_t *)src;
|
||||
uint64_t w = *p++;
|
||||
w |= (uint64_t)(*p++) << 8;
|
||||
w |= (uint64_t)(*p++) << 16;
|
||||
w |= (uint64_t)(*p++) << 24;
|
||||
w |= (uint64_t)(*p++) << 32;
|
||||
w |= (uint64_t)(*p++) << 40;
|
||||
return w;
|
||||
}
|
||||
|
||||
static FORCE_INLINE void store48(void *dst, uint64_t w) {
|
||||
uint8_t *p = (uint8_t *)dst;
|
||||
*p++ = (uint8_t)w;
|
||||
w >>= 8;
|
||||
*p++ = (uint8_t)w;
|
||||
w >>= 8;
|
||||
*p++ = (uint8_t)w;
|
||||
w >>= 8;
|
||||
*p++ = (uint8_t)w;
|
||||
w >>= 8;
|
||||
*p++ = (uint8_t)w;
|
||||
w >>= 8;
|
||||
*p++ = (uint8_t)w;
|
||||
}
|
||||
|
||||
static FORCE_INLINE uint32_t rotr32(const uint32_t w, const unsigned c) {
|
||||
return (w >> c) | (w << (32 - c));
|
||||
}
|
||||
|
||||
static FORCE_INLINE uint64_t rotr64(const uint64_t w, const unsigned c) {
|
||||
return (w >> c) | (w << (64 - c));
|
||||
}
|
||||
|
||||
#endif
|
||||
116
app/src/main/cpp/randomx/src/blake2/blake2.h
Normal file
116
app/src/main/cpp/randomx/src/blake2/blake2.h
Normal file
|
|
@ -0,0 +1,116 @@
|
|||
/*
|
||||
Copyright (c) 2018-2019, tevador <tevador@gmail.com>
|
||||
|
||||
All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted provided that the following conditions are met:
|
||||
* Redistributions of source code must retain the above copyright
|
||||
notice, this list of conditions and the following disclaimer.
|
||||
* Redistributions in binary form must reproduce the above copyright
|
||||
notice, this list of conditions and the following disclaimer in the
|
||||
documentation and/or other materials provided with the distribution.
|
||||
* Neither the name of the copyright holder nor the
|
||||
names of its contributors may be used to endorse or promote products
|
||||
derived from this software without specific prior written permission.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
|
||||
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
|
||||
FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
||||
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
|
||||
CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
|
||||
OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
/* Original code from Argon2 reference source code package used under CC0 Licence
|
||||
* https://github.com/P-H-C/phc-winner-argon2
|
||||
* Copyright 2015
|
||||
* Daniel Dinu, Dmitry Khovratovich, Jean-Philippe Aumasson, and Samuel Neves
|
||||
*/
|
||||
|
||||
#ifndef PORTABLE_BLAKE2_H
|
||||
#define PORTABLE_BLAKE2_H
|
||||
|
||||
#include <stdint.h>
|
||||
#include <limits.h>
|
||||
|
||||
#if defined(__cplusplus)
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
enum blake2b_constant {
|
||||
BLAKE2B_BLOCKBYTES = 128,
|
||||
BLAKE2B_OUTBYTES = 64,
|
||||
BLAKE2B_KEYBYTES = 64,
|
||||
BLAKE2B_SALTBYTES = 16,
|
||||
BLAKE2B_PERSONALBYTES = 16
|
||||
};
|
||||
|
||||
#pragma pack(push, 1)
|
||||
typedef struct __blake2b_param {
|
||||
uint8_t digest_length; /* 1 */
|
||||
uint8_t key_length; /* 2 */
|
||||
uint8_t fanout; /* 3 */
|
||||
uint8_t depth; /* 4 */
|
||||
uint32_t leaf_length; /* 8 */
|
||||
uint64_t node_offset; /* 16 */
|
||||
uint8_t node_depth; /* 17 */
|
||||
uint8_t inner_length; /* 18 */
|
||||
uint8_t reserved[14]; /* 32 */
|
||||
uint8_t salt[BLAKE2B_SALTBYTES]; /* 48 */
|
||||
uint8_t personal[BLAKE2B_PERSONALBYTES]; /* 64 */
|
||||
} blake2b_param;
|
||||
#pragma pack(pop)
|
||||
|
||||
typedef struct __blake2b_state {
|
||||
uint64_t h[8];
|
||||
uint64_t t[2];
|
||||
uint64_t f[2];
|
||||
uint8_t buf[BLAKE2B_BLOCKBYTES];
|
||||
unsigned buflen;
|
||||
unsigned outlen;
|
||||
uint8_t last_node;
|
||||
} blake2b_state;
|
||||
|
||||
/* Ensure param structs have not been wrongly padded */
|
||||
/* Poor man's static_assert */
|
||||
enum {
|
||||
blake2_size_check_0 = 1 / !!(CHAR_BIT == 8),
|
||||
blake2_size_check_2 =
|
||||
1 / !!(sizeof(blake2b_param) == sizeof(uint64_t) * CHAR_BIT)
|
||||
};
|
||||
|
||||
//randomx namespace
|
||||
#define blake2b_init randomx_blake2b_init
|
||||
#define blake2b_init_key randomx_blake2b_init_key
|
||||
#define blake2b_init_param randomx_blake2b_init_param
|
||||
#define blake2b_update randomx_blake2b_update
|
||||
#define blake2b_final randomx_blake2b_final
|
||||
#define blake2b randomx_blake2b
|
||||
#define blake2b_long randomx_blake2b_long
|
||||
|
||||
/* Streaming API */
|
||||
int blake2b_init(blake2b_state *S, size_t outlen);
|
||||
int blake2b_init_key(blake2b_state *S, size_t outlen, const void *key,
|
||||
size_t keylen);
|
||||
int blake2b_init_param(blake2b_state *S, const blake2b_param *P);
|
||||
int blake2b_update(blake2b_state *S, const void *in, size_t inlen);
|
||||
int blake2b_final(blake2b_state *S, void *out, size_t outlen);
|
||||
|
||||
/* Simple API */
|
||||
int blake2b(void *out, size_t outlen, const void *in, size_t inlen,
|
||||
const void *key, size_t keylen);
|
||||
|
||||
/* Argon2 Team - Begin Code */
|
||||
int blake2b_long(void *out, size_t outlen, const void *in, size_t inlen);
|
||||
/* Argon2 Team - End Code */
|
||||
|
||||
#if defined(__cplusplus)
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif
|
||||
409
app/src/main/cpp/randomx/src/blake2/blake2b.c
Normal file
409
app/src/main/cpp/randomx/src/blake2/blake2b.c
Normal file
|
|
@ -0,0 +1,409 @@
|
|||
/*
|
||||
Copyright (c) 2018-2019, tevador <tevador@gmail.com>
|
||||
|
||||
All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted provided that the following conditions are met:
|
||||
* Redistributions of source code must retain the above copyright
|
||||
notice, this list of conditions and the following disclaimer.
|
||||
* Redistributions in binary form must reproduce the above copyright
|
||||
notice, this list of conditions and the following disclaimer in the
|
||||
documentation and/or other materials provided with the distribution.
|
||||
* Neither the name of the copyright holder nor the
|
||||
names of its contributors may be used to endorse or promote products
|
||||
derived from this software without specific prior written permission.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
|
||||
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
|
||||
FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
||||
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
|
||||
CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
|
||||
OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
/* Original code from Argon2 reference source code package used under CC0 Licence
|
||||
* https://github.com/P-H-C/phc-winner-argon2
|
||||
* Copyright 2015
|
||||
* Daniel Dinu, Dmitry Khovratovich, Jean-Philippe Aumasson, and Samuel Neves
|
||||
*/
|
||||
|
||||
#include <stdint.h>
|
||||
#include <string.h>
|
||||
#include <stdio.h>
|
||||
|
||||
#include "blake2.h"
|
||||
#include "blake2-impl.h"
|
||||
|
||||
static const uint64_t blake2b_IV[8] = {
|
||||
UINT64_C(0x6a09e667f3bcc908), UINT64_C(0xbb67ae8584caa73b),
|
||||
UINT64_C(0x3c6ef372fe94f82b), UINT64_C(0xa54ff53a5f1d36f1),
|
||||
UINT64_C(0x510e527fade682d1), UINT64_C(0x9b05688c2b3e6c1f),
|
||||
UINT64_C(0x1f83d9abfb41bd6b), UINT64_C(0x5be0cd19137e2179) };
|
||||
|
||||
static const unsigned int blake2b_sigma[12][16] = {
|
||||
{0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15},
|
||||
{14, 10, 4, 8, 9, 15, 13, 6, 1, 12, 0, 2, 11, 7, 5, 3},
|
||||
{11, 8, 12, 0, 5, 2, 15, 13, 10, 14, 3, 6, 7, 1, 9, 4},
|
||||
{7, 9, 3, 1, 13, 12, 11, 14, 2, 6, 5, 10, 4, 0, 15, 8},
|
||||
{9, 0, 5, 7, 2, 4, 10, 15, 14, 1, 11, 12, 6, 8, 3, 13},
|
||||
{2, 12, 6, 10, 0, 11, 8, 3, 4, 13, 7, 5, 15, 14, 1, 9},
|
||||
{12, 5, 1, 15, 14, 13, 4, 10, 0, 7, 6, 3, 9, 2, 8, 11},
|
||||
{13, 11, 7, 14, 12, 1, 3, 9, 5, 0, 15, 4, 8, 6, 2, 10},
|
||||
{6, 15, 14, 9, 11, 3, 0, 8, 12, 2, 13, 7, 1, 4, 10, 5},
|
||||
{10, 2, 8, 4, 7, 6, 1, 5, 15, 11, 9, 14, 3, 12, 13, 0},
|
||||
{0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15},
|
||||
{14, 10, 4, 8, 9, 15, 13, 6, 1, 12, 0, 2, 11, 7, 5, 3},
|
||||
};
|
||||
|
||||
static FORCE_INLINE void blake2b_set_lastnode(blake2b_state *S) {
|
||||
S->f[1] = (uint64_t)-1;
|
||||
}
|
||||
|
||||
static FORCE_INLINE void blake2b_set_lastblock(blake2b_state *S) {
|
||||
if (S->last_node) {
|
||||
blake2b_set_lastnode(S);
|
||||
}
|
||||
S->f[0] = (uint64_t)-1;
|
||||
}
|
||||
|
||||
static FORCE_INLINE void blake2b_increment_counter(blake2b_state *S,
|
||||
uint64_t inc) {
|
||||
S->t[0] += inc;
|
||||
S->t[1] += (S->t[0] < inc);
|
||||
}
|
||||
|
||||
static FORCE_INLINE void blake2b_invalidate_state(blake2b_state *S) {
|
||||
//clear_internal_memory(S, sizeof(*S)); /* wipe */
|
||||
blake2b_set_lastblock(S); /* invalidate for further use */
|
||||
}
|
||||
|
||||
static FORCE_INLINE void blake2b_init0(blake2b_state *S) {
|
||||
memset(S, 0, sizeof(*S));
|
||||
memcpy(S->h, blake2b_IV, sizeof(S->h));
|
||||
}
|
||||
|
||||
int blake2b_init_param(blake2b_state *S, const blake2b_param *P) {
|
||||
const unsigned char *p = (const unsigned char *)P;
|
||||
unsigned int i;
|
||||
|
||||
if (NULL == P || NULL == S) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
blake2b_init0(S);
|
||||
/* IV XOR Parameter Block */
|
||||
for (i = 0; i < 8; ++i) {
|
||||
S->h[i] ^= load64(&p[i * sizeof(S->h[i])]);
|
||||
}
|
||||
S->outlen = P->digest_length;
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Sequential blake2b initialization */
|
||||
int blake2b_init(blake2b_state *S, size_t outlen) {
|
||||
blake2b_param P;
|
||||
|
||||
if (S == NULL) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
if ((outlen == 0) || (outlen > BLAKE2B_OUTBYTES)) {
|
||||
blake2b_invalidate_state(S);
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* Setup Parameter Block for unkeyed BLAKE2 */
|
||||
P.digest_length = (uint8_t)outlen;
|
||||
P.key_length = 0;
|
||||
P.fanout = 1;
|
||||
P.depth = 1;
|
||||
P.leaf_length = 0;
|
||||
P.node_offset = 0;
|
||||
P.node_depth = 0;
|
||||
P.inner_length = 0;
|
||||
memset(P.reserved, 0, sizeof(P.reserved));
|
||||
memset(P.salt, 0, sizeof(P.salt));
|
||||
memset(P.personal, 0, sizeof(P.personal));
|
||||
|
||||
return blake2b_init_param(S, &P);
|
||||
}
|
||||
|
||||
int blake2b_init_key(blake2b_state *S, size_t outlen, const void *key, size_t keylen) {
|
||||
blake2b_param P;
|
||||
|
||||
if (S == NULL) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
if ((outlen == 0) || (outlen > BLAKE2B_OUTBYTES)) {
|
||||
blake2b_invalidate_state(S);
|
||||
return -1;
|
||||
}
|
||||
|
||||
if ((key == 0) || (keylen == 0) || (keylen > BLAKE2B_KEYBYTES)) {
|
||||
blake2b_invalidate_state(S);
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* Setup Parameter Block for keyed BLAKE2 */
|
||||
P.digest_length = (uint8_t)outlen;
|
||||
P.key_length = (uint8_t)keylen;
|
||||
P.fanout = 1;
|
||||
P.depth = 1;
|
||||
P.leaf_length = 0;
|
||||
P.node_offset = 0;
|
||||
P.node_depth = 0;
|
||||
P.inner_length = 0;
|
||||
memset(P.reserved, 0, sizeof(P.reserved));
|
||||
memset(P.salt, 0, sizeof(P.salt));
|
||||
memset(P.personal, 0, sizeof(P.personal));
|
||||
|
||||
if (blake2b_init_param(S, &P) < 0) {
|
||||
blake2b_invalidate_state(S);
|
||||
return -1;
|
||||
}
|
||||
|
||||
{
|
||||
uint8_t block[BLAKE2B_BLOCKBYTES];
|
||||
memset(block, 0, BLAKE2B_BLOCKBYTES);
|
||||
memcpy(block, key, keylen);
|
||||
blake2b_update(S, block, BLAKE2B_BLOCKBYTES);
|
||||
/* Burn the key from stack */
|
||||
//clear_internal_memory(block, BLAKE2B_BLOCKBYTES);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void blake2b_compress(blake2b_state *S, const uint8_t *block) {
|
||||
uint64_t m[16];
|
||||
uint64_t v[16];
|
||||
unsigned int i, r;
|
||||
|
||||
for (i = 0; i < 16; ++i) {
|
||||
m[i] = load64(block + i * sizeof(m[i]));
|
||||
}
|
||||
|
||||
for (i = 0; i < 8; ++i) {
|
||||
v[i] = S->h[i];
|
||||
}
|
||||
|
||||
v[8] = blake2b_IV[0];
|
||||
v[9] = blake2b_IV[1];
|
||||
v[10] = blake2b_IV[2];
|
||||
v[11] = blake2b_IV[3];
|
||||
v[12] = blake2b_IV[4] ^ S->t[0];
|
||||
v[13] = blake2b_IV[5] ^ S->t[1];
|
||||
v[14] = blake2b_IV[6] ^ S->f[0];
|
||||
v[15] = blake2b_IV[7] ^ S->f[1];
|
||||
|
||||
#define G(r, i, a, b, c, d) \
|
||||
do { \
|
||||
a = a + b + m[blake2b_sigma[r][2 * i + 0]]; \
|
||||
d = rotr64(d ^ a, 32); \
|
||||
c = c + d; \
|
||||
b = rotr64(b ^ c, 24); \
|
||||
a = a + b + m[blake2b_sigma[r][2 * i + 1]]; \
|
||||
d = rotr64(d ^ a, 16); \
|
||||
c = c + d; \
|
||||
b = rotr64(b ^ c, 63); \
|
||||
} while ((void)0, 0)
|
||||
|
||||
#define ROUND(r) \
|
||||
do { \
|
||||
G(r, 0, v[0], v[4], v[8], v[12]); \
|
||||
G(r, 1, v[1], v[5], v[9], v[13]); \
|
||||
G(r, 2, v[2], v[6], v[10], v[14]); \
|
||||
G(r, 3, v[3], v[7], v[11], v[15]); \
|
||||
G(r, 4, v[0], v[5], v[10], v[15]); \
|
||||
G(r, 5, v[1], v[6], v[11], v[12]); \
|
||||
G(r, 6, v[2], v[7], v[8], v[13]); \
|
||||
G(r, 7, v[3], v[4], v[9], v[14]); \
|
||||
} while ((void)0, 0)
|
||||
|
||||
for (r = 0; r < 12; ++r) {
|
||||
ROUND(r);
|
||||
}
|
||||
|
||||
for (i = 0; i < 8; ++i) {
|
||||
S->h[i] = S->h[i] ^ v[i] ^ v[i + 8];
|
||||
}
|
||||
|
||||
#undef G
|
||||
#undef ROUND
|
||||
}
|
||||
|
||||
int blake2b_update(blake2b_state *S, const void *in, size_t inlen) {
|
||||
const uint8_t *pin = (const uint8_t *)in;
|
||||
|
||||
if (inlen == 0) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Sanity check */
|
||||
if (S == NULL || in == NULL) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* Is this a reused state? */
|
||||
if (S->f[0] != 0) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (S->buflen + inlen > BLAKE2B_BLOCKBYTES) {
|
||||
/* Complete current block */
|
||||
size_t left = S->buflen;
|
||||
size_t fill = BLAKE2B_BLOCKBYTES - left;
|
||||
memcpy(&S->buf[left], pin, fill);
|
||||
blake2b_increment_counter(S, BLAKE2B_BLOCKBYTES);
|
||||
blake2b_compress(S, S->buf);
|
||||
S->buflen = 0;
|
||||
inlen -= fill;
|
||||
pin += fill;
|
||||
/* Avoid buffer copies when possible */
|
||||
while (inlen > BLAKE2B_BLOCKBYTES) {
|
||||
blake2b_increment_counter(S, BLAKE2B_BLOCKBYTES);
|
||||
blake2b_compress(S, pin);
|
||||
inlen -= BLAKE2B_BLOCKBYTES;
|
||||
pin += BLAKE2B_BLOCKBYTES;
|
||||
}
|
||||
}
|
||||
memcpy(&S->buf[S->buflen], pin, inlen);
|
||||
S->buflen += (unsigned int)inlen;
|
||||
return 0;
|
||||
}
|
||||
|
||||
int blake2b_final(blake2b_state *S, void *out, size_t outlen) {
|
||||
uint8_t buffer[BLAKE2B_OUTBYTES] = { 0 };
|
||||
unsigned int i;
|
||||
|
||||
/* Sanity checks */
|
||||
if (S == NULL || out == NULL || outlen < S->outlen) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* Is this a reused state? */
|
||||
if (S->f[0] != 0) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
blake2b_increment_counter(S, S->buflen);
|
||||
blake2b_set_lastblock(S);
|
||||
memset(&S->buf[S->buflen], 0, BLAKE2B_BLOCKBYTES - S->buflen); /* Padding */
|
||||
blake2b_compress(S, S->buf);
|
||||
|
||||
for (i = 0; i < 8; ++i) { /* Output full hash to temp buffer */
|
||||
store64(buffer + sizeof(S->h[i]) * i, S->h[i]);
|
||||
}
|
||||
|
||||
memcpy(out, buffer, S->outlen);
|
||||
//clear_internal_memory(buffer, sizeof(buffer));
|
||||
//clear_internal_memory(S->buf, sizeof(S->buf));
|
||||
//clear_internal_memory(S->h, sizeof(S->h));
|
||||
return 0;
|
||||
}
|
||||
|
||||
int blake2b(void *out, size_t outlen, const void *in, size_t inlen,
|
||||
const void *key, size_t keylen) {
|
||||
blake2b_state S;
|
||||
int ret = -1;
|
||||
|
||||
/* Verify parameters */
|
||||
if (NULL == in && inlen > 0) {
|
||||
goto fail;
|
||||
}
|
||||
|
||||
if (NULL == out || outlen == 0 || outlen > BLAKE2B_OUTBYTES) {
|
||||
goto fail;
|
||||
}
|
||||
|
||||
if ((NULL == key && keylen > 0) || keylen > BLAKE2B_KEYBYTES) {
|
||||
goto fail;
|
||||
}
|
||||
|
||||
if (keylen > 0) {
|
||||
if (blake2b_init_key(&S, outlen, key, keylen) < 0) {
|
||||
goto fail;
|
||||
}
|
||||
}
|
||||
else {
|
||||
if (blake2b_init(&S, outlen) < 0) {
|
||||
goto fail;
|
||||
}
|
||||
}
|
||||
|
||||
if (blake2b_update(&S, in, inlen) < 0) {
|
||||
goto fail;
|
||||
}
|
||||
ret = blake2b_final(&S, out, outlen);
|
||||
|
||||
fail:
|
||||
//clear_internal_memory(&S, sizeof(S));
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* Argon2 Team - Begin Code */
|
||||
int blake2b_long(void *pout, size_t outlen, const void *in, size_t inlen) {
|
||||
uint8_t *out = (uint8_t *)pout;
|
||||
blake2b_state blake_state;
|
||||
uint8_t outlen_bytes[sizeof(uint32_t)] = { 0 };
|
||||
int ret = -1;
|
||||
|
||||
if (outlen > UINT32_MAX) {
|
||||
goto fail;
|
||||
}
|
||||
|
||||
/* Ensure little-endian byte order! */
|
||||
store32(outlen_bytes, (uint32_t)outlen);
|
||||
|
||||
#define TRY(statement) \
|
||||
do { \
|
||||
ret = statement; \
|
||||
if (ret < 0) { \
|
||||
goto fail; \
|
||||
} \
|
||||
} while ((void)0, 0)
|
||||
|
||||
if (outlen <= BLAKE2B_OUTBYTES) {
|
||||
TRY(blake2b_init(&blake_state, outlen));
|
||||
TRY(blake2b_update(&blake_state, outlen_bytes, sizeof(outlen_bytes)));
|
||||
TRY(blake2b_update(&blake_state, in, inlen));
|
||||
TRY(blake2b_final(&blake_state, out, outlen));
|
||||
}
|
||||
else {
|
||||
uint32_t toproduce;
|
||||
uint8_t out_buffer[BLAKE2B_OUTBYTES];
|
||||
uint8_t in_buffer[BLAKE2B_OUTBYTES];
|
||||
TRY(blake2b_init(&blake_state, BLAKE2B_OUTBYTES));
|
||||
TRY(blake2b_update(&blake_state, outlen_bytes, sizeof(outlen_bytes)));
|
||||
TRY(blake2b_update(&blake_state, in, inlen));
|
||||
TRY(blake2b_final(&blake_state, out_buffer, BLAKE2B_OUTBYTES));
|
||||
memcpy(out, out_buffer, BLAKE2B_OUTBYTES / 2);
|
||||
out += BLAKE2B_OUTBYTES / 2;
|
||||
toproduce = (uint32_t)outlen - BLAKE2B_OUTBYTES / 2;
|
||||
|
||||
while (toproduce > BLAKE2B_OUTBYTES) {
|
||||
memcpy(in_buffer, out_buffer, BLAKE2B_OUTBYTES);
|
||||
TRY(blake2b(out_buffer, BLAKE2B_OUTBYTES, in_buffer,
|
||||
BLAKE2B_OUTBYTES, NULL, 0));
|
||||
memcpy(out, out_buffer, BLAKE2B_OUTBYTES / 2);
|
||||
out += BLAKE2B_OUTBYTES / 2;
|
||||
toproduce -= BLAKE2B_OUTBYTES / 2;
|
||||
}
|
||||
|
||||
memcpy(in_buffer, out_buffer, BLAKE2B_OUTBYTES);
|
||||
TRY(blake2b(out_buffer, toproduce, in_buffer, BLAKE2B_OUTBYTES, NULL,
|
||||
0));
|
||||
memcpy(out, out_buffer, toproduce);
|
||||
}
|
||||
fail:
|
||||
//clear_internal_memory(&blake_state, sizeof(blake_state));
|
||||
return ret;
|
||||
#undef TRY
|
||||
}
|
||||
/* Argon2 Team - End Code */
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show more
Loading…
Reference in a new issue