From 6311675119e9b6f22c49ad8aa6f38c9bf9617aa4 Mon Sep 17 00:00:00 2001 From: Tomas Krnak Date: Wed, 16 Nov 2022 19:49:59 +0700 Subject: [PATCH] feat(core): add Zcash Rust primitives --- core/.changelog.d/2510.added | 1 + core/SConscript.firmware | 9 +- core/SConscript.unix | 9 +- .../extmod/modtrezorutils/modtrezorutils.c | 6 + .../rustmods/modtrezorzcashprimitives.c | 32 + core/embed/firmware/mpconfigport.h | 2 + core/embed/rust/Cargo.lock | 70 + core/embed/rust/Cargo.toml | 22 +- core/embed/rust/blake2b_hal/Cargo.lock | 16 + core/embed/rust/blake2b_hal/Cargo.toml | 9 + core/embed/rust/blake2b_hal/src/ffi.rs | 43 + core/embed/rust/blake2b_hal/src/lib.rs | 144 ++ core/embed/rust/librust.h | 5 + core/embed/rust/librust_qstr.h | 26 + core/embed/rust/src/error.rs | 7 + core/embed/rust/src/lib.rs | 3 + core/embed/rust/src/micropython/macros.rs | 24 +- core/embed/rust/src/micropython/mod.rs | 2 + core/embed/rust/src/micropython/obj.rs | 19 +- core/embed/rust/src/micropython/typ.rs | 9 + core/embed/rust/src/micropython/wrap.rs | 76 + .../embed/rust/src/trezorhal/blake2b_tests.rs | 62 + core/embed/rust/src/trezorhal/mod.rs | 2 + core/embed/rust/src/zcash_primitives/mod.rs | 2 + .../src/zcash_primitives/pallas/common.rs | 27 + .../rust/src/zcash_primitives/pallas/fp.rs | 63 + .../src/zcash_primitives/pallas/generators.rs | 155 ++ .../rust/src/zcash_primitives/pallas/mod.rs | 88 ++ .../rust/src/zcash_primitives/pallas/point.rs | 143 ++ .../src/zcash_primitives/pallas/scalar.rs | 100 ++ .../zcash_primitives/poseidon/constants.rs | 1350 +++++++++++++++++ .../poseidon/constants_gen.rs | 113 ++ .../rust/src/zcash_primitives/poseidon/mod.rs | 109 ++ .../src/zcash_primitives/poseidon/tests.rs | 662 ++++++++ core/embed/unix/mpconfigport.h | 2 + core/mocks/generated/trezorpallas.pyi | 79 + core/mocks/generated/trezorposeidon.pyi | 6 + core/mocks/generated/trezorutils.pyi | 1 + .../site_tools/micropython/__init__.py | 3 + core/src/all_modules.py | 2 + core/src/trezor/crypto/hashlib.py | 5 + core/src/trezor/crypto/pallas.py | 14 + core/src/trezor/utils.py | 1 + 43 files changed, 3516 insertions(+), 7 deletions(-) create mode 100644 core/.changelog.d/2510.added create mode 100644 core/embed/extmod/rustmods/modtrezorzcashprimitives.c create mode 100644 core/embed/rust/blake2b_hal/Cargo.lock create mode 100644 core/embed/rust/blake2b_hal/Cargo.toml create mode 100644 core/embed/rust/blake2b_hal/src/ffi.rs create mode 100644 core/embed/rust/blake2b_hal/src/lib.rs create mode 100644 core/embed/rust/src/micropython/wrap.rs create mode 100644 core/embed/rust/src/trezorhal/blake2b_tests.rs create mode 100644 core/embed/rust/src/zcash_primitives/mod.rs create mode 100644 core/embed/rust/src/zcash_primitives/pallas/common.rs create mode 100644 core/embed/rust/src/zcash_primitives/pallas/fp.rs create mode 100644 core/embed/rust/src/zcash_primitives/pallas/generators.rs create mode 100644 core/embed/rust/src/zcash_primitives/pallas/mod.rs create mode 100644 core/embed/rust/src/zcash_primitives/pallas/point.rs create mode 100644 core/embed/rust/src/zcash_primitives/pallas/scalar.rs create mode 100644 core/embed/rust/src/zcash_primitives/poseidon/constants.rs create mode 100644 core/embed/rust/src/zcash_primitives/poseidon/constants_gen.rs create mode 100644 core/embed/rust/src/zcash_primitives/poseidon/mod.rs create mode 100644 core/embed/rust/src/zcash_primitives/poseidon/tests.rs create mode 100644 core/mocks/generated/trezorpallas.pyi create mode 100644 core/mocks/generated/trezorposeidon.pyi create mode 100644 core/src/trezor/crypto/pallas.py diff --git a/core/.changelog.d/2510.added b/core/.changelog.d/2510.added new file mode 100644 index 000000000..4b3fa0852 --- /dev/null +++ b/core/.changelog.d/2510.added @@ -0,0 +1 @@ +Add Zcash Rust primitives diff --git a/core/SConscript.firmware b/core/SConscript.firmware index 96ffec17d..a8f5a8cc3 100644 --- a/core/SConscript.firmware +++ b/core/SConscript.firmware @@ -12,6 +12,7 @@ FEATURE_FLAGS = { "RDI": True, "SECP256K1_ZKP": True, # required for trezor.crypto.curve.bip340 (BIP340/Taproot) "SYSTEM_VIEW": False, + "ZCASH_SHIELDED": False, } CCFLAGS_MOD = '' @@ -202,6 +203,10 @@ if UI2: SOURCE_MOD += [ 'embed/extmod/rustmods/modtrezorui2.c', ] +if FEATURE_FLAGS["ZCASH_SHIELDED"]: + SOURCE_MOD += [ + 'embed/extmod/rustmods/modtrezorzcashprimitives.c' + ] # modutime SOURCE_MOD += [ @@ -678,7 +683,7 @@ if FROZEN: SOURCE_PY.extend(Glob(SOURCE_PY_DIR + 'apps/bitcoin/sign_tx/zcash_v4.py')) SOURCE_PY.extend(Glob(SOURCE_PY_DIR + 'trezor/enums/Zcash*.py')) - source_mpy = env.FrozenModule(source=SOURCE_PY, source_dir=SOURCE_PY_DIR, bitcoin_only=BITCOIN_ONLY) + source_mpy = env.FrozenModule(source=SOURCE_PY, source_dir=SOURCE_PY_DIR, bitcoin_only=BITCOIN_ONLY, zcash_shielded=FEATURE_FLAGS['ZCASH_SHIELDED']) source_mpyc = env.FrozenCFile( target='frozen_mpy.c', source=source_mpy, qstr_header=qstr_preprocessed) @@ -720,6 +725,8 @@ def cargo_build(): features.append('ui') if PYOPT == '0': features.append('ui_debug') + if FEATURE_FLAGS["ZCASH_SHIELDED"]: + features.append("zcash_shielded") cargo_opts = [ f'--target={RUST_TARGET}', diff --git a/core/SConscript.unix b/core/SConscript.unix index d129adcd8..edcbaf2d4 100644 --- a/core/SConscript.unix +++ b/core/SConscript.unix @@ -10,6 +10,7 @@ UI2 = ARGUMENTS.get('UI2', '0') == '1' or TREZOR_MODEL in ('1', 'R') FEATURE_FLAGS = { "SECP256K1_ZKP": True, # required for trezor.crypto.curve.bip340 (BIP340/Taproot) + "ZCASH_SHIELDED": EVERYTHING, } CCFLAGS_MOD = '' @@ -202,6 +203,10 @@ if UI2: SOURCE_MOD += [ 'embed/extmod/rustmods/modtrezorui2.c', ] +if FEATURE_FLAGS["ZCASH_SHIELDED"]: + SOURCE_MOD += [ + 'embed/extmod/rustmods/modtrezorzcashprimitives.c' + ] # modutime SOURCE_MOD += [ @@ -634,7 +639,7 @@ if FROZEN: SOURCE_PY.extend(Glob(SOURCE_PY_DIR + 'apps/bitcoin/sign_tx/zcash_v4.py')) SOURCE_PY.extend(Glob(SOURCE_PY_DIR + 'trezor/enums/Zcash*.py')) - source_mpy = env.FrozenModule(source=SOURCE_PY, source_dir=SOURCE_PY_DIR, bitcoin_only=BITCOIN_ONLY) + source_mpy = env.FrozenModule(source=SOURCE_PY, source_dir=SOURCE_PY_DIR, bitcoin_only=BITCOIN_ONLY, zcash_shielded=FEATURE_FLAGS['ZCASH_SHIELDED']) source_mpyc = env.FrozenCFile( target='frozen_mpy.c', source=source_mpy, qstr_header=qstr_preprocessed) @@ -674,6 +679,8 @@ def cargo_build(): features.append('ui') if PYOPT == '0': features.append('debug') + if FEATURE_FLAGS["ZCASH_SHIELDED"]: + features.append('zcash_shielded') return f'cd embed/rust; cargo build --profile {RUST_PROFILE} --target-dir=../../build/unix/rust --no-default-features --features "{" ".join(features)}"' diff --git a/core/embed/extmod/modtrezorutils/modtrezorutils.c b/core/embed/extmod/modtrezorutils/modtrezorutils.c index 402125577..1656d144d 100644 --- a/core/embed/extmod/modtrezorutils/modtrezorutils.c +++ b/core/embed/extmod/modtrezorutils/modtrezorutils.c @@ -243,6 +243,7 @@ STATIC mp_obj_str_t mod_trezorutils_revision_obj = { /// MODEL: str /// EMULATOR: bool /// BITCOIN_ONLY: bool +/// ZCASH_SHIELDED: bool STATIC const mp_rom_map_elem_t mp_module_trezorutils_globals_table[] = { {MP_ROM_QSTR(MP_QSTR___name__), MP_ROM_QSTR(MP_QSTR_trezorutils)}, @@ -283,6 +284,11 @@ STATIC const mp_rom_map_elem_t mp_module_trezorutils_globals_table[] = { #else {MP_ROM_QSTR(MP_QSTR_BITCOIN_ONLY), mp_const_false}, #endif +#ifdef ZCASH_SHIELDED + {MP_ROM_QSTR(MP_QSTR_ZCASH_SHIELDED), mp_const_true}, +#elif TREZOR_EMULATOR + {MP_ROM_QSTR(MP_QSTR_ZCASH_SHIELDED), mp_const_false}, +#endif }; STATIC MP_DEFINE_CONST_DICT(mp_module_trezorutils_globals, diff --git a/core/embed/extmod/rustmods/modtrezorzcashprimitives.c b/core/embed/extmod/rustmods/modtrezorzcashprimitives.c new file mode 100644 index 000000000..030e7b207 --- /dev/null +++ b/core/embed/extmod/rustmods/modtrezorzcashprimitives.c @@ -0,0 +1,32 @@ +/* + * This file is part of the Trezor project, https://trezor.io/ + * + * Copyright (c) SatoshiLabs + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#include "py/runtime.h" + +#include "librust.h" + +#if MICROPY_PY_TREZORPALLAS +MP_REGISTER_MODULE(MP_QSTR_trezorpallas, mp_module_trezorpallas, + MICROPY_PY_TREZORPALLAS); +#endif // MICROPY_PY_TREZORPALLAS + +#if MICROPY_PY_TREZORPOSEIDON +MP_REGISTER_MODULE(MP_QSTR_trezorposeidon, mp_module_trezorposeidon, + MICROPY_PY_TREZORPOSEIDON); +#endif // MICROPY_PY_TREZORPOSEIDON diff --git a/core/embed/firmware/mpconfigport.h b/core/embed/firmware/mpconfigport.h index c123c91a0..495460408 100644 --- a/core/embed/firmware/mpconfigport.h +++ b/core/embed/firmware/mpconfigport.h @@ -160,6 +160,8 @@ #define MICROPY_PY_TREZORUTILS (1) #define MICROPY_PY_TREZORPROTO (1) #define MICROPY_PY_TREZORUI2 (1) +#define MICROPY_PY_TREZORPALLAS (1) +#define MICROPY_PY_TREZORPOSEIDON (1) #ifdef SYSTEM_VIEW #define MP_PLAT_PRINT_STRN(str, len) segger_print(str, len) diff --git a/core/embed/rust/Cargo.lock b/core/embed/rust/Cargo.lock index abcb1132b..5e548950e 100644 --- a/core/embed/rust/Cargo.lock +++ b/core/embed/rust/Cargo.lock @@ -27,6 +27,13 @@ version = "1.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" +[[package]] +name = "blake2b_simd" +version = "1.0.0" +dependencies = [ + "cty", +] + [[package]] name = "byteorder" version = "1.4.3" @@ -81,12 +88,33 @@ version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b365fabc795046672053e29c954733ec3b05e4be654ab130fe8f1f94d7051f35" +[[package]] +name = "ff" +version = "0.12.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "df689201f395c6b90dfe87127685f8dbfc083a5e779e613575d8bd7314300c3e" +dependencies = [ + "rand_core", + "subtle", +] + [[package]] name = "glob" version = "0.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9b919933a397b79c37e33b77bb2aa3dc8eb6e165ad809e58ff75bc7db2e34574" +[[package]] +name = "group" +version = "0.12.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7391856def869c1c81063a03457c676fbcd419709c3dfb33d8d319de484b154d" +dependencies = [ + "ff", + "rand_core", + "subtle", +] + [[package]] name = "hash32" version = "0.2.1" @@ -166,6 +194,19 @@ dependencies = [ "minimal-lexical", ] +[[package]] +name = "pasta_curves" +version = "0.4.0" +source = "git+https://github.com/jarys/pasta_curves?rev=a4f755013aad344982383c9f5af362697d928325#a4f755013aad344982383c9f5af362697d928325" +dependencies = [ + "blake2b_simd", + "ff", + "group", + "rand", + "static_assertions", + "subtle", +] + [[package]] name = "peeking_take_while" version = "0.1.2" @@ -190,6 +231,21 @@ dependencies = [ "proc-macro2", ] +[[package]] +name = "rand" +version = "0.8.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "34af8d1a0e25924bc5b7c43c079c942339d8f0a8b57c39049bef581b46327404" +dependencies = [ + "rand_core", +] + +[[package]] +name = "rand_core" +version = "0.6.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d34f1408f55294453790c48b2f1ebbb1c5b4b7563eb1f418bcfcfdbb06ebb4e7" + [[package]] name = "regex" version = "1.5.6" @@ -238,16 +294,30 @@ version = "1.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a8f112729512f8e442d81f95a8a7ddf2b7c6b8a1a6f509a95864142b30cab2d3" +[[package]] +name = "static_assertions" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a2eb9349b6444b326872e140eb1cf5e7c522154d69e7a0ffb0fb81c06b37543f" + +[[package]] +name = "subtle" +version = "2.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6bdef32e8150c2a081110b42772ffe7d7c9032b606bc226c8260fd97e0976601" + [[package]] name = "trezor_lib" version = "0.1.0" dependencies = [ "bindgen", + "blake2b_simd", "cc", "cstr_core", "cty", "glob", "heapless", + "pasta_curves", ] [[package]] diff --git a/core/embed/rust/Cargo.toml b/core/embed/rust/Cargo.toml index 1f6cdd4b5..e5410fc5a 100644 --- a/core/embed/rust/Cargo.toml +++ b/core/embed/rust/Cargo.toml @@ -8,6 +8,9 @@ build = "build.rs" [features] default = ["model_tt"] bitcoin_only = [] +zcash_shielded = [ + "micropython", "pasta_curves", "blake2b_simd", +] model_tt = ["touch"] model_t1 = ["buttons"] model_tr = ["buttons"] @@ -19,7 +22,7 @@ buttons = [] touch = [] clippy = [] debug = ["ui_debug"] -test = ["cc", "glob", "micropython", "protobuf", "ui", "ui_debug"] +test = ["cc", "glob", "micropython", "protobuf", "ui", "ui_debug", "zcash_shielded"] [lib] crate-type = ["staticlib"] @@ -53,6 +56,16 @@ default_features = false version = "0.2.4" default_features = false +[dependencies.blake2b_simd] +optional = true +version = "1" +default_features = false + +[dependencies.pasta_curves] +optional = true +version = "0.4.0" +default-features = false + # Build dependencies [build-dependencies.bindgen] @@ -69,3 +82,10 @@ version = "1.0.69" [build-dependencies.glob] optional = true version = "0.3.0" + +[patch.crates-io.blake2b_simd] +path = "./blake2b_hal" + +[patch.crates-io.pasta_curves] +git = "https://github.com/jarys/pasta_curves" +rev = "a4f755013aad344982383c9f5af362697d928325" diff --git a/core/embed/rust/blake2b_hal/Cargo.lock b/core/embed/rust/blake2b_hal/Cargo.lock new file mode 100644 index 000000000..0c911cdda --- /dev/null +++ b/core/embed/rust/blake2b_hal/Cargo.lock @@ -0,0 +1,16 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +version = 3 + +[[package]] +name = "blake2b_simd" +version = "0.1.0" +dependencies = [ + "cty", +] + +[[package]] +name = "cty" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b365fabc795046672053e29c954733ec3b05e4be654ab130fe8f1f94d7051f35" diff --git a/core/embed/rust/blake2b_hal/Cargo.toml b/core/embed/rust/blake2b_hal/Cargo.toml new file mode 100644 index 000000000..be430975a --- /dev/null +++ b/core/embed/rust/blake2b_hal/Cargo.toml @@ -0,0 +1,9 @@ +[package] +name = "blake2b_simd" +version = "1.0.0" +edition = "2021" + +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html + +[dependencies] +cty = "0.2.2" diff --git a/core/embed/rust/blake2b_hal/src/ffi.rs b/core/embed/rust/blake2b_hal/src/ffi.rs new file mode 100644 index 000000000..d1e720e6a --- /dev/null +++ b/core/embed/rust/blake2b_hal/src/ffi.rs @@ -0,0 +1,43 @@ +#[repr(C)] +#[derive(Debug, Copy, Clone)] +pub struct __blake2b_state { + pub h: [u64; 8usize], + pub t: [u64; 2usize], + pub f: [u64; 2usize], + pub buf: [u8; 128usize], + pub buflen: usize, + pub outlen: usize, + pub last_node: u8, +} +#[allow(non_camel_case_types)] +pub type blake2b_state = __blake2b_state; +extern "C" { + pub fn blake2b_Init(S: *mut blake2b_state, outlen: usize) -> cty::c_int; +} +extern "C" { + pub fn blake2b_InitKey( + S: *mut blake2b_state, + outlen: usize, + key: *const cty::c_void, + keylen: usize, + ) -> cty::c_int; +} +extern "C" { + pub fn blake2b_InitPersonal( + S: *mut blake2b_state, + outlen: usize, + personal: *const cty::c_void, + personal_len: usize, + ) -> cty::c_int; +} +extern "C" { + pub fn blake2b_Update( + S: *mut blake2b_state, + pin: *const cty::c_void, + inlen: usize, + ) -> cty::c_int; +} +extern "C" { + pub fn blake2b_Final(S: *mut blake2b_state, out: *mut cty::c_void, outlen: usize) + -> cty::c_int; +} diff --git a/core/embed/rust/blake2b_hal/src/lib.rs b/core/embed/rust/blake2b_hal/src/lib.rs new file mode 100644 index 000000000..3654afc02 --- /dev/null +++ b/core/embed/rust/blake2b_hal/src/lib.rs @@ -0,0 +1,144 @@ +//! This crate is a wrapper of blake2b.c. +//! Purpose of this crate is to replace blake2_simd crate +//! required by some Rust dependencies. + +#![no_std] + +use cty::c_void; + +mod ffi; + +pub const BLOCKBYTES: usize = 128; +pub const KEYBYTES: usize = 64; +pub const OUTBYTES: usize = 64; +pub const PERSONALBYTES: usize = 16; +pub const SALTBYTES: usize = 16; + +#[derive(Clone, Copy)] +pub struct Hash { + bytes: [u8; OUTBYTES], + len: u8, +} + +impl Hash { + pub fn as_bytes(&self) -> &[u8] { + &self.bytes[..self.len as usize] + } + + #[inline] + pub fn as_array(&self) -> &[u8; OUTBYTES] { + &self.bytes + } +} + +#[derive(Clone, Copy)] +pub struct Params<'a, 'b> { + key: Option<&'a [u8]>, + personal: Option<&'b [u8]>, + outlen: usize, +} + +impl<'a, 'b> Params<'a, 'b> { + pub fn new() -> Self { + Params { + key: None, + personal: None, + outlen: OUTBYTES, + } + } + + pub fn hash_length(&mut self, length: usize) -> &mut Self { + self.outlen = length; + self + } + + pub fn key(&mut self, key: &'a [u8]) -> &mut Self { + self.key = Some(key); + self + } + + pub fn personal(&mut self, personal: &'b [u8]) -> &mut Self { + self.personal = Some(personal); + self + } + + pub fn to_state(self) -> State { + assert!(self.key.is_none() || self.personal.is_none()); + let mut ctx = ffi::__blake2b_state { + h: [0u64; 8usize], + t: [0u64; 2usize], + f: [0u64; 2usize], + buf: [0u8; 128usize], + buflen: 0usize, + outlen: 0usize, + last_node: 0u8, + }; + let res = unsafe { + match (self.key, self.personal) { + (None, None) => ffi::blake2b_Init(&mut ctx, self.outlen), + (Some(key), None) => ffi::blake2b_InitKey( + &mut ctx, + self.outlen, + key.as_ptr() as *const c_void, + key.len(), + ), + (None, Some(personal)) => ffi::blake2b_InitPersonal( + &mut ctx, + self.outlen, + personal.as_ptr() as *const c_void, + personal.len(), + ), + (Some(_), Some(_)) => { + panic!("Using key and personalization simultaniously not implemented.") + } + } + }; + if res < 0 { + panic!("Blake2b initialization failed.") + } + State { ctx } + } + + pub fn hash(self, data: &[u8]) -> Hash { + self.to_state().update(data).finalize() + } +} + +#[derive(Clone)] +pub struct State { + ctx: ffi::blake2b_state, +} + +impl State { + pub fn new() -> Self { + Params::new().to_state() + } + + pub fn update(&mut self, data: &[u8]) -> &mut Self { + unsafe { + ffi::blake2b_Update(&mut self.ctx, data.as_ptr() as *const c_void, data.len()); + } + self + } + + pub fn finalize(&self) -> Hash { + // Method `finalize` takes imutable reference to the self + // to mirror `blake2b_simd` API. + let mut bytes = [0u8; OUTBYTES]; + let ptr = bytes.as_mut_ptr() as *mut c_void; + // clone `ctx` to get a mutable reference required by `ffi::blake2b_Final` + let mut ctx = self.ctx.clone(); + let res = unsafe { ffi::blake2b_Final(&mut ctx, ptr, OUTBYTES) }; + if res < 0 { + panic!("Blake2b hash finalization failed.") + } + Hash { + bytes, + len: self.ctx.outlen as u8, + } + } +} + +pub fn blake2b(data: &[u8]) -> Hash { + State::new().update(data).finalize() +} diff --git a/core/embed/rust/librust.h b/core/embed/rust/librust.h index a3d943e20..3969d09cc 100644 --- a/core/embed/rust/librust.h +++ b/core/embed/rust/librust.h @@ -17,3 +17,8 @@ extern mp_obj_module_t mp_module_trezorui2; #ifdef TREZOR_EMULATOR mp_obj_t ui_debug_layout_type(); #endif + +#ifdef ZCASH_SHIELDED +extern mp_obj_module_t mp_module_trezorpallas; +extern mp_obj_module_t mp_module_trezorposeidon; +#endif diff --git a/core/embed/rust/librust_qstr.h b/core/embed/rust/librust_qstr.h index 81c22d3c6..8004ff7a1 100644 --- a/core/embed/rust/librust_qstr.h +++ b/core/embed/rust/librust_qstr.h @@ -68,4 +68,30 @@ static void _librust_qstrs(void) { MP_QSTR_total_amount; MP_QSTR_total_fee_new; MP_QSTR_user_fee_change; + + // pallas + MP_QSTR_trezorpallas; + MP_QSTR_Fp; + MP_QSTR_Scalar; + MP_QSTR_Point; + MP_QSTR_to_bytes; + MP_QSTR_extract; + MP_QSTR_is_identity; + MP_QSTR_to_base; + MP_QSTR_to_scalar; + MP_QSTR_group_hash; + MP_QSTR_scalar_from_i64; + MP_QSTR_generators; + MP_QSTR_SPENDING_KEY_BASE; + MP_QSTR_NULLIFIER_K_BASE; + MP_QSTR_VALUE_COMMITMENT_VALUE_BASE; + MP_QSTR_VALUE_COMMITMENT_RANDOMNESS_BASE; + MP_QSTR_NOTE_COMMITMENT_BASE; + MP_QSTR_NOTE_COMMITMENT_Q; + MP_QSTR_IVK_COMMITMENT_BASE; + MP_QSTR_IVK_COMMITMENT_Q; + + // poseidon + MP_QSTR_trezorposeidon; + MP_QSTR_poseidon; } diff --git a/core/embed/rust/src/error.rs b/core/embed/rust/src/error.rs index 13e278603..6e9910fba 100644 --- a/core/embed/rust/src/error.rs +++ b/core/embed/rust/src/error.rs @@ -1,4 +1,5 @@ use core::{ + array::TryFromSliceError, convert::{Infallible, TryInto}, num::TryFromIntError, }; @@ -83,3 +84,9 @@ impl From for Error { Self::OutOfRange } } + +impl From for Error { + fn from(_e: TryFromSliceError) -> Error { + Error::OutOfRange + } +} diff --git a/core/embed/rust/src/lib.rs b/core/embed/rust/src/lib.rs index 2f63df12b..5346b97d0 100644 --- a/core/embed/rust/src/lib.rs +++ b/core/embed/rust/src/lib.rs @@ -17,6 +17,9 @@ mod time; #[cfg(feature = "ui_debug")] mod trace; +#[cfg(feature = "zcash_shielded")] +mod zcash_primitives; + #[cfg(feature = "ui")] #[macro_use] pub mod ui; diff --git a/core/embed/rust/src/micropython/macros.rs b/core/embed/rust/src/micropython/macros.rs index 557666c8e..71406d1db 100644 --- a/core/embed/rust/src/micropython/macros.rs +++ b/core/embed/rust/src/micropython/macros.rs @@ -121,7 +121,10 @@ macro_rules! obj_type { (name: $name:expr, $(locals: $locals:expr,)? $(attr_fn: $attr_fn:ident,)? + $(make_new_fn: $make_new_fn:expr,)? $(call_fn: $call_fn:ident,)? + $(unary_op_fn: $unary_op_fn:ident,)? + $(binary_op_fn: $binary_op_fn:ident,)? ) => {{ #[allow(unused_unsafe)] unsafe { @@ -134,11 +137,26 @@ macro_rules! obj_type { let mut attr: ffi::mp_attr_fun_t = None; $(attr = Some($attr_fn);)? + #[allow(unused_mut)] + #[allow(unused_assignments)] + let mut make_new: ffi::mp_make_new_fun_t = None; + $(make_new = Some($make_new_fn);)? + #[allow(unused_mut)] #[allow(unused_assignments)] let mut call: ffi::mp_call_fun_t = None; $(call = Some($call_fn);)? + #[allow(unused_mut)] + #[allow(unused_assignments)] + let mut unary_op: ffi::mp_unary_op_fun_t = None; + $(unary_op = Some($unary_op_fn);)? + + #[allow(unused_mut)] + #[allow(unused_assignments)] + let mut binary_op: ffi::mp_binary_op_fun_t = None; + $(binary_op = Some($binary_op_fn);)? + // TODO: This is safe only if we pass in `Dict` with fixed `Map` (created by // `Map::fixed()`, usually through `obj_map!`), because only then will // MicroPython treat `locals_dict` as immutable, and make the mutable cast safe. @@ -154,10 +172,10 @@ macro_rules! obj_type { flags: 0, name, print: None, - make_new: None, + make_new, call, - unary_op: None, - binary_op: None, + unary_op, + binary_op, attr, subscr: None, getiter: None, diff --git a/core/embed/rust/src/micropython/mod.rs b/core/embed/rust/src/micropython/mod.rs index 0ebe3afae..dbf5048de 100644 --- a/core/embed/rust/src/micropython/mod.rs +++ b/core/embed/rust/src/micropython/mod.rs @@ -18,6 +18,8 @@ pub mod runtime; pub mod time; pub mod typ; pub mod util; +#[cfg(feature = "zcash_shielded")] +pub mod wrap; #[cfg(test)] pub mod testutil; diff --git a/core/embed/rust/src/micropython/obj.rs b/core/embed/rust/src/micropython/obj.rs index 1b0a09d72..6e2cd633c 100644 --- a/core/embed/rust/src/micropython/obj.rs +++ b/core/embed/rust/src/micropython/obj.rs @@ -2,7 +2,7 @@ use core::convert::{TryFrom, TryInto}; use cstr_core::CStr; -use crate::error::Error; +use crate::{error::Error, micropython::buffer::Buffer}; use super::{ffi, runtime::catch_exception}; @@ -382,6 +382,23 @@ impl TryFrom for usize { } } +impl TryFrom<[u8; N]> for Obj { + type Error = Error; + + fn try_from(array: [u8; N]) -> Result { + Ok((&array[..]).try_into()?) + } +} + +impl TryFrom for [u8; N] { + type Error = Error; + + fn try_from(obj: Obj) -> Result<[u8; N], Error> { + let buffer: Buffer = obj.try_into()?; + Ok(buffer.as_ref().try_into()?) + } +} + impl From> for Obj where T: Into, diff --git a/core/embed/rust/src/micropython/typ.rs b/core/embed/rust/src/micropython/typ.rs index 700cf71a5..61c78656a 100644 --- a/core/embed/rust/src/micropython/typ.rs +++ b/core/embed/rust/src/micropython/typ.rs @@ -22,6 +22,15 @@ impl Type { pub const fn as_base(&'static self) -> ObjBase { ObjBase { type_: self } } + + /// Convert a "static const" Type to a MicroPython object. + pub const fn as_obj(&'static self) -> Obj { + // SAFETY: + // - We are an object struct with a base and a type. + // - 'static lifetime holds us in place. + // - MicroPython is smart enough not to mutate `mp_obj_type_t` objects. + unsafe { Obj::from_ptr(self as *const _ as *mut _) } + } } // SAFETY: We are in a single-threaded environment. diff --git a/core/embed/rust/src/micropython/wrap.rs b/core/embed/rust/src/micropython/wrap.rs new file mode 100644 index 000000000..e096eb952 --- /dev/null +++ b/core/embed/rust/src/micropython/wrap.rs @@ -0,0 +1,76 @@ +//! Tiny module for wrapping Rust structs into Python objects +//! +//! # Example: +//! ``` +//! impl Wrappable for Foo { +//! fn obj_type() -> &'static Type { ... } +//! } +//! +//! fn bar(obj: Obj) -> Obj { +//! let foo: Gc> = obj.try_into()?; +//! let foo: Foo = foo.deref().inner(); +//! let result: Foo = baz(foo); +//! result.wrap() +//! } +//! ``` + +use crate::{ + error::Error, + micropython::{ + gc::Gc, + obj::{Obj, ObjBase}, + typ::Type, + }, +}; + +pub trait Wrappable: Sized { + fn obj_type() -> &'static Type; + + fn alloc(inner: Self) -> Result>, Error> { + Gc::new(Wrapped { + base: Self::obj_type().as_base(), + inner, + }) + } + + fn wrap(self) -> Result { + let value: Gc> = Self::alloc(self)?; + // SAFETY: + // - `value` is GC-allocated. + // - `value` is `repr(C)`. + // - `value` has a `base` as the first field with the correct type. + let obj = unsafe { Obj::from_ptr(Gc::into_raw(value).cast()) }; + Ok(obj) + } +} + +#[repr(C)] +pub struct Wrapped { + base: ObjBase, + inner: T, +} + +impl Wrapped { + pub fn inner(&self) -> &T { + &self.inner + } + + pub fn inner_mut(&mut self) -> &mut T { + &mut self.inner + } +} + +impl TryFrom for Gc> { + type Error = Error; + + fn try_from(obj: Obj) -> Result>, Error> { + if T::obj_type().is_type_of(obj) { + // SAFETY: We assume that if `obj` is an object pointer with the correct type, + // it is always GC-allocated. + let this = unsafe { Gc::from_raw(obj.as_ptr().cast()) }; + Ok(this) + } else { + Err(Error::TypeError) + } + } +} diff --git a/core/embed/rust/src/trezorhal/blake2b_tests.rs b/core/embed/rust/src/trezorhal/blake2b_tests.rs new file mode 100644 index 000000000..94f07c9e8 --- /dev/null +++ b/core/embed/rust/src/trezorhal/blake2b_tests.rs @@ -0,0 +1,62 @@ +//! Test are placed here to be executed by `make test_rust`. + +use blake2b_simd::*; + +const EMPTY_HASH: [u8; 64] = [ + 120, 106, 2, 247, 66, 1, 89, 3, 198, 198, 253, 133, 37, 82, 210, 114, 145, 47, 71, 64, 225, 88, + 71, 97, 138, 134, 226, 23, 247, 31, 84, 25, 210, 94, 16, 49, 175, 238, 88, 83, 19, 137, 100, + 68, 147, 78, 176, 75, 144, 58, 104, 91, 20, 72, 183, 85, 213, 111, 112, 26, 254, 155, 226, 206, +]; +const ABC_HASH: [u8; 64] = [ + 186, 128, 165, 63, 152, 28, 77, 13, 106, 39, 151, 182, 159, 18, 246, 233, 76, 33, 47, 20, 104, + 90, 196, 183, 75, 18, 187, 111, 219, 255, 162, 209, 125, 135, 197, 57, 42, 171, 121, 45, 194, + 82, 213, 222, 69, 51, 204, 149, 24, 211, 138, 168, 219, 241, 146, 90, 185, 35, 134, 237, 212, + 0, 153, 35, +]; +const ONE_BLOCK_HASH: [u8; 64] = [ + 134, 89, 57, 225, 32, 230, 128, 84, 56, 71, 136, 65, 175, 183, 57, 174, 66, 80, 207, 55, 38, + 83, 7, 138, 6, 92, 220, 255, 252, 164, 202, 247, 152, 230, 212, 98, 182, 93, 101, 143, 193, + 101, 120, 38, 64, 237, 237, 112, 150, 52, 73, 174, 21, 0, 251, 15, 36, 152, 29, 119, 39, 226, + 44, 65, +]; +const THOUSAND_HASH: [u8; 64] = [ + 30, 228, 229, 30, 202, 181, 33, 10, 81, 143, 38, 21, 14, 136, 38, 39, 236, 131, 153, 103, 241, + 157, 118, 62, 21, 8, 177, 44, 254, 254, 209, 72, 88, 246, 161, 201, 209, 249, 105, 188, 34, 77, + 201, 68, 15, 90, 105, 85, 39, 126, 117, 91, 156, 81, 63, 155, 164, 66, 28, 94, 80, 200, 215, + 135, +]; + +#[test] +fn test_update_state() { + let io = &[ + (&b""[..], EMPTY_HASH), + (&b"abc"[..], ABC_HASH), + (&[0; 128], ONE_BLOCK_HASH), + (&[0; 1000], THOUSAND_HASH), + ]; + // Test each input all at once. + for &(input, output) in io { + let hash = blake2b(input); + assert_eq!(hash.as_bytes(), &output, "hash mismatch"); + } + // Now in two chunks. This is especially important for the ONE_BLOCK case, + // because it would be a mistake for update() to call compress, even though + // the buffer is full. + for &(input, output) in io { + let mut state = State::new(); + let split = input.len() / 2; + state.update(&input[..split]); + state.update(&input[split..]); + let hash = state.finalize(); + assert_eq!(hash.as_bytes(), &output, "hash mismatch"); + } + // Now one byte at a time. + for &(input, output) in io { + let mut state = State::new(); + for &b in input { + state.update(&[b]); + } + let hash = state.finalize(); + assert_eq!(hash.as_bytes(), &output, "hash mismatch"); + } +} diff --git a/core/embed/rust/src/trezorhal/mod.rs b/core/embed/rust/src/trezorhal/mod.rs index ee23c3997..e60c76e6e 100644 --- a/core/embed/rust/src/trezorhal/mod.rs +++ b/core/embed/rust/src/trezorhal/mod.rs @@ -1,4 +1,6 @@ pub mod bip39; +#[cfg(all(test, feature = "zcash_shielded"))] +mod blake2b_tests; #[macro_use] #[allow(unused_macros)] pub mod common; diff --git a/core/embed/rust/src/zcash_primitives/mod.rs b/core/embed/rust/src/zcash_primitives/mod.rs new file mode 100644 index 000000000..77e5b4bd2 --- /dev/null +++ b/core/embed/rust/src/zcash_primitives/mod.rs @@ -0,0 +1,2 @@ +pub mod pallas; +pub mod poseidon; diff --git a/core/embed/rust/src/zcash_primitives/pallas/common.rs b/core/embed/rust/src/zcash_primitives/pallas/common.rs new file mode 100644 index 000000000..b24de3f12 --- /dev/null +++ b/core/embed/rust/src/zcash_primitives/pallas/common.rs @@ -0,0 +1,27 @@ +use cstr_core::cstr; +use pasta_curves::group::ff; + +use crate::{ + error::Error, + micropython::{map::Map, obj::Obj, typ::Type, util, wrap::Wrappable}, +}; + +pub unsafe extern "C" fn ff_from_bytes>( + _type_: *const Type, + n_args: usize, + n_kw: usize, + args: *const Obj, +) -> Obj { + let block = |args: &[Obj], _kwargs: &Map| { + if args.len() != 1 { + return Err(Error::TypeError); + } + let bytes: [u8; 32] = args[0].try_into()?; + let elem = F::from_repr(bytes); + match Option::::from(elem) { + Some(e) => e.wrap(), + None => Err(Error::ValueError(cstr!("Conversion failed"))), + } + }; + unsafe { util::try_with_args_and_kwargs_inline(n_args, n_kw, args, block) } +} diff --git a/core/embed/rust/src/zcash_primitives/pallas/fp.rs b/core/embed/rust/src/zcash_primitives/pallas/fp.rs new file mode 100644 index 000000000..a2977d16e --- /dev/null +++ b/core/embed/rust/src/zcash_primitives/pallas/fp.rs @@ -0,0 +1,63 @@ +use core::ops::Deref; +use pasta_curves::{arithmetic::FieldExt, group::ff::PrimeField, Fp}; + +use crate::micropython::{ + ffi, + gc::Gc, + map::Map, + obj::Obj, + qstr::Qstr, + typ::Type, + util, + wrap::{Wrappable, Wrapped}, +}; + +pub static FP_TYPE: Type = obj_type! { + name: Qstr::MP_QSTR_Fp, + locals: &obj_dict!(obj_map! { + Qstr::MP_QSTR_to_bytes => obj_fn_1!(fp_to_bytes).as_obj(), + }), + make_new_fn: super::common::ff_from_bytes::, + binary_op_fn: fp_binary_op, +}; + +impl Wrappable for Fp { + fn obj_type() -> &'static Type { + &FP_TYPE + } +} + +unsafe extern "C" fn fp_to_bytes(self_in: Obj) -> Obj { + let block = || { + let this: Gc> = self_in.try_into()?; + let bytes: [u8; 32] = this.deref().inner().to_repr(); + bytes.try_into() + }; + unsafe { util::try_or_raise(block) } +} + +unsafe extern "C" fn fp_binary_op(op: ffi::mp_binary_op_t, this: Obj, other: Obj) -> Obj { + let block = || { + let this = Gc::>::try_from(this)?; + let this = this.deref().inner(); + match op { + ffi::mp_binary_op_t_MP_BINARY_OP_ADD => { + let other = Gc::>::try_from(other)?; + let other = other.deref().inner(); + (this + other).wrap() + } + _ => Ok(Obj::const_null()), + } + }; + unsafe { util::try_or_raise(block) } +} + +#[no_mangle] +pub unsafe extern "C" fn to_base(bytes: Obj) -> Obj { + let block = || { + let bytes: [u8; 64] = bytes.try_into()?; + let elem = Fp::from_bytes_wide(&bytes); + elem.wrap() + }; + unsafe { util::try_or_raise(block) } +} diff --git a/core/embed/rust/src/zcash_primitives/pallas/generators.rs b/core/embed/rust/src/zcash_primitives/pallas/generators.rs new file mode 100644 index 000000000..e8b376487 --- /dev/null +++ b/core/embed/rust/src/zcash_primitives/pallas/generators.rs @@ -0,0 +1,155 @@ +//! Precomputed Orchard generators. + +use crate::{ + error::Error, + micropython::{ffi, obj::Obj, qstr::Qstr, typ::Type, util, wrap::Wrappable}, +}; +use pasta_curves::{ + arithmetic::CurveAffine, + pallas::{Affine, Point}, + Fp, +}; + +pub static GENERATORS_TYPE: Type = obj_type! { + name: Qstr::MP_QSTR_generators, + attr_fn: attr_fn, +}; + +unsafe extern "C" fn attr_fn(_self_in: Obj, attr: ffi::qstr, dest: *mut Obj) { + let block = || { + let attr = Qstr::from_u16(attr as _); + let (x, y) = get_by_name(attr)?; + let affine_point: Affine = unwrap!(Option::from(Affine::from_xy(x, y))); + let point: Point = affine_point.into(); + unsafe { + if dest.read().is_null() { + // Load attribute. + dest.write(point.wrap()?); + Ok(()) + } else { + // Set attribute. + Err(Error::TypeError) + } + } + }; + unsafe { util::try_or_raise(block) } +} + +#[inline] +fn get_by_name(name: Qstr) -> Result<(Fp, Fp), Error> { + match name { + Qstr::MP_QSTR_SPENDING_KEY_BASE => Ok(( + Fp::from_raw([ + 0x8d1a7284b875c963, + 0xc7f0ce37b70a10c, + 0x3b8d187c3e5f445f, + 0x375523b328f1d606, + ]), + Fp::from_raw([ + 0x4ce33e817b0c3bc9, + 0xdfc914fec005bdd8, + 0x7b10bcfcfed624fb, + 0x1ad0357fdf1a66db, + ]), + )), + Qstr::MP_QSTR_NULLIFIER_K_BASE => Ok(( + Fp::from_raw([ + 0xd36f6aa7e447ca75, + 0x5e7eb192ccb5db9b, + 0x2e375571faf4c9cf, + 0x25e7aa169ca8198d, + ]), + Fp::from_raw([ + 0x2db8dce21ae001cc, + 0x5fe0a49ec1c1b433, + 0x880473442008ff75, + 0x155c1f851b1a3384, + ]), + )), + Qstr::MP_QSTR_VALUE_COMMITMENT_VALUE_BASE => Ok(( + Fp::from_raw([ + 0x2aa7bd6e3af94367, + 0xfe04a37f2b5a7c8c, + 0xf7a86a704f9bb232, + 0x2f70597a8e3d0f42, + ]), + Fp::from_raw([ + 0xa413c47eaf5af28e, + 0x1d9ea766a7ffe3db, + 0x1e917f63136d6c42, + 0x2d0e5169311919af, + ]), + )), + Qstr::MP_QSTR_VALUE_COMMITMENT_RANDOMNESS_BASE => Ok(( + Fp::from_raw([ + 0xec3c668883c5a91, + 0x406ed745ee90802f, + 0x4f66235bea8d2048, + 0x7f444550fa409bb, + ]), + Fp::from_raw([ + 0xe2f46c8a772d9ca, + 0x279229f1f3928146, + 0x21562cc9e46fb7c2, + 0x24136777af26628c, + ]), + )), + Qstr::MP_QSTR_NOTE_COMMITMENT_BASE => Ok(( + Fp::from_raw([ + 0x2c022c480ffc6e13, + 0x239ec55cfc14a47c, + 0xcd239fab936f3df2, + 0x26b206c328a94533, + ]), + Fp::from_raw([ + 0xc34153c622cf37e3, + 0x79c7c07823e0dcf6, + 0xa9541a2f2366e1c6, + 0x3cde564b686dc04c, + ]), + )), + Qstr::MP_QSTR_NOTE_COMMITMENT_Q => Ok(( + Fp::from_raw([ + 0x320eba0940a8745d, + 0xc5960f5afd46dd2a, + 0xf79ff2b479b0ed5d, + 0x178007a056fbcd0d, + ]), + Fp::from_raw([ + 0x87270a5a7349ac63, + 0x8822128881db5e9e, + 0x4ebec2d96ef4c92c, + 0x32a058938ac67083, + ]), + )), + Qstr::MP_QSTR_IVK_COMMITMENT_BASE => Ok(( + Fp::from_raw([ + 0x9823486e5ff8a118, + 0x2957fe2d31aedc7, + 0x1634290a40808948, + 0x25a22ccd5070134e, + ]), + Fp::from_raw([ + 0x3fe793b3e37fdda9, + 0x6b4442fb1b58a6c7, + 0xc2c890c4284b5794, + 0x29cfd29966a2faeb, + ]), + )), + Qstr::MP_QSTR_IVK_COMMITMENT_Q => Ok(( + Fp::from_raw([ + 0x6bcb2f92790f82f2, + 0x421bcc245128a232, + 0x7dcc81b85aa241fa, + 0x5bc0cf14aa9c811, + ]), + Fp::from_raw([ + 0xbe5ae5cecfaddebe, + 0x46c4351dc96da5f1, + 0xef59074620de054b, + 0x1b014cf6d41abee6, + ]), + )), + _ => Err(Error::AttributeError(name)), + } +} diff --git a/core/embed/rust/src/zcash_primitives/pallas/mod.rs b/core/embed/rust/src/zcash_primitives/pallas/mod.rs new file mode 100644 index 000000000..0f7def583 --- /dev/null +++ b/core/embed/rust/src/zcash_primitives/pallas/mod.rs @@ -0,0 +1,88 @@ +use crate::micropython::{map::Map, module::Module, qstr::Qstr}; + +pub mod common; +mod fp; +mod generators; +mod point; +mod scalar; + +#[no_mangle] +pub static mp_module_trezorpallas: Module = obj_module! { + Qstr::MP_QSTR___name__ => Qstr::MP_QSTR_trezorpallas.to_obj(), + /// # https://zips.z.cash/protocol/protocol.pdf#orchardkeycomponents + /// def to_base(x: bytes) -> Fp: + /// ... + Qstr::MP_QSTR_to_base => obj_fn_1!(fp::to_base).as_obj(), + /// # https://zips.z.cash/protocol/protocol.pdf#orchardkeycomponents + /// def to_scalar(x: bytes) -> Scalar: + /// ... + Qstr::MP_QSTR_to_scalar => obj_fn_1!(scalar::to_scalar).as_obj(), + /// # https://zips.z.cash/protocol/protocol.pdf#concretegrouphashpallasandvesta + /// def group_hash(domain: str, message: bytes) -> Point: + /// ... + Qstr::MP_QSTR_group_hash => obj_fn_2!(point::group_hash).as_obj(), + /// def scalar_from_i64(x: int) -> Scalar: + /// """Converts integer to Scalar.""" + Qstr::MP_QSTR_scalar_from_i64 => obj_fn_1!(scalar::scalar_from_i64).as_obj(), + /// class Fp: + /// """Pallas base field.""" + /// + /// def __init__(self, repr: bytes) -> None: + /// ... + /// + /// def to_bytes(self) -> bytes: + /// ... + Qstr::MP_QSTR_Fp => (&fp::FP_TYPE).as_obj(), + /// class Scalar: + /// """Pallas scalar field.""" + /// + /// def __init__(self, repr: bytes) -> None: + /// ... + /// + /// def to_bytes(self) -> bytes: + /// ... + /// + /// def __mul__(self, other: Point) -> Point: + /// ... + /// + /// def __add__(self, other: Scalar) -> Scalar: + /// ... + /// + /// def __neg__(self) -> Point: + /// ... + /// + /// def __bool__(self) -> bool: + /// ... + Qstr::MP_QSTR_Scalar => (&scalar::SCALAR_TYPE).as_obj(), + /// class Point: + /// """Pallas point.""" + /// + /// def __init__(self, repr: bytes) -> None: + /// ... + /// + /// def to_bytes(self) -> bytes: + /// ... + /// + /// def extract(self) -> Fp: + /// ... + /// + /// def is_identity(self) -> bool: + /// ... + /// + /// def __add__(self, other: Point) -> Point: + /// ... + /// + /// def __neg__(self) -> Point: + /// ... + Qstr::MP_QSTR_Point => (&point::POINT_TYPE).as_obj(), + /// class generators: + /// SPENDING_KEY_BASE: Point + /// NULLIFIER_K_BASE: Point + /// VALUE_COMMITMENT_VALUE_BASE: Point + /// VALUE_COMMITMENT_RANDOMNESS_BASE: Point + /// NOTE_COMMITMENT_BASE: Point + /// NOTE_COMMITMENT_Q: Point + /// IVK_COMMITMENT_BASE: Point + /// IVK_COMMITMENT_Q: Point + Qstr::MP_QSTR_generators => (&generators::GENERATORS_TYPE).as_obj(), +}; diff --git a/core/embed/rust/src/zcash_primitives/pallas/point.rs b/core/embed/rust/src/zcash_primitives/pallas/point.rs new file mode 100644 index 000000000..516a0909f --- /dev/null +++ b/core/embed/rust/src/zcash_primitives/pallas/point.rs @@ -0,0 +1,143 @@ +use core::ops::Deref; +use cstr_core::cstr; +use pasta_curves::{ + arithmetic::{CurveAffine, CurveExt}, + group::{Curve, Group, GroupEncoding}, + pallas::Point, + Fp, +}; + +use crate::{ + error::Error, + micropython::{ + buffer::{Buffer, StrBuffer}, + ffi, + gc::Gc, + map::Map, + obj::Obj, + qstr::Qstr, + typ::Type, + util, + wrap::{Wrappable, Wrapped}, + }, +}; + +pub static POINT_TYPE: Type = obj_type! { + name: Qstr::MP_QSTR_Point, + locals: &obj_dict!(obj_map! { + Qstr::MP_QSTR_extract => obj_fn_1!(point_extract).as_obj(), + Qstr::MP_QSTR_to_bytes => obj_fn_1!(point_to_bytes).as_obj(), + Qstr::MP_QSTR_is_identity => obj_fn_1!(point_is_identity).as_obj(), + }), + make_new_fn: point_from_bytes, + unary_op_fn: point_unary_op, + binary_op_fn: point_binary_op, +}; + +impl Wrappable for Point { + fn obj_type() -> &'static Type { + &POINT_TYPE + } +} + +unsafe extern "C" fn point_from_bytes( + _type_: *const Type, + n_args: usize, + n_kw: usize, + args: *const Obj, +) -> Obj { + let block = |args: &[Obj], _kwargs: &Map| { + if args.len() != 1 { + return Err(Error::TypeError); + } + let bytes: [u8; 32] = args[0].try_into()?; + let point = Point::from_bytes(&bytes); + match Option::::from(point) { + Some(p) => p.wrap(), + None => Err(Error::ValueError(cstr!("Conversion failed"))), + } + }; + unsafe { util::try_with_args_and_kwargs_inline(n_args, n_kw, args, block) } +} + +unsafe extern "C" fn point_unary_op(op: ffi::mp_unary_op_t, this: Obj) -> Obj { + let block = || { + let this = Gc::>::try_from(this)?; + let this = this.deref().inner(); + match op { + ffi::mp_unary_op_t_MP_UNARY_OP_NEGATIVE => (-this).wrap(), + _ => Ok(Obj::const_null()), + } + }; + unsafe { util::try_or_raise(block) } +} + +unsafe extern "C" fn point_binary_op(op: ffi::mp_binary_op_t, this: Obj, other: Obj) -> Obj { + let block = || { + let this = Gc::>::try_from(this)?; + let this = this.deref().inner(); + match op { + ffi::mp_binary_op_t_MP_BINARY_OP_ADD => { + let other = Gc::>::try_from(other)?; + let other = other.deref().inner(); + (this + other).wrap() + } + ffi::mp_binary_op_t_MP_BINARY_OP_EQUAL => { + let other = Gc::>::try_from(other)?; + let other = other.deref().inner(); + Ok((this == other).into()) + } + ffi::mp_binary_op_t_MP_BINARY_OP_NOT_EQUAL => { + let other = Gc::>::try_from(other)?; + let other = other.deref().inner(); + Ok((this != other).into()) + } + _ => Ok(Obj::const_null()), + } + }; + unsafe { util::try_or_raise(block) } +} + +unsafe extern "C" fn point_extract(self_in: Obj) -> Obj { + let block = || { + let this = Gc::>::try_from(self_in)?; + let elem = this + .deref() + .inner() + .to_affine() + .coordinates() + .map(|c| *c.x()) + .unwrap_or_else(Fp::zero); + elem.wrap() + }; + unsafe { util::try_or_raise(block) } +} + +unsafe extern "C" fn point_is_identity(self_in: Obj) -> Obj { + let block = || { + let this = Gc::>::try_from(self_in)?; + let this = this.deref().inner(); + Ok((this == &Point::identity()).into()) + }; + unsafe { util::try_or_raise(block) } +} + +unsafe extern "C" fn point_to_bytes(self_in: Obj) -> Obj { + let block = || { + let this = Gc::>::try_from(self_in)?; + let bytes: [u8; 32] = this.deref().inner().to_bytes(); + bytes.try_into() + }; + unsafe { util::try_or_raise(block) } +} + +#[no_mangle] +pub unsafe extern "C" fn group_hash(domain: Obj, message: Obj) -> Obj { + let block = || { + let domain: StrBuffer = domain.try_into()?; + let message: Buffer = message.try_into()?; + let point = Point::hash_to_curve(domain.deref(), message.deref()); + point.wrap() + }; + unsafe { util::try_or_raise(block) } +} diff --git a/core/embed/rust/src/zcash_primitives/pallas/scalar.rs b/core/embed/rust/src/zcash_primitives/pallas/scalar.rs new file mode 100644 index 000000000..350af68b6 --- /dev/null +++ b/core/embed/rust/src/zcash_primitives/pallas/scalar.rs @@ -0,0 +1,100 @@ +use core::ops::Deref; + +use pasta_curves::{ + arithmetic::FieldExt, + group::ff::{Field, PrimeField}, + pallas::{Point, Scalar}, +}; + +use crate::micropython::{ + ffi, + gc::Gc, + map::Map, + obj::Obj, + qstr::Qstr, + typ::Type, + util, + wrap::{Wrappable, Wrapped}, +}; + +pub static SCALAR_TYPE: Type = obj_type! { + name: Qstr::MP_QSTR_Scalar, + locals: &obj_dict!(obj_map! { + Qstr::MP_QSTR_to_bytes => obj_fn_1!(scalar_to_bytes).as_obj(), + }), + make_new_fn: super::common::ff_from_bytes::, + unary_op_fn: scalar_unary_op, + binary_op_fn: scalar_binary_op, +}; + +impl Wrappable for Scalar { + fn obj_type() -> &'static Type { + &SCALAR_TYPE + } +} + +unsafe extern "C" fn scalar_binary_op(op: ffi::mp_binary_op_t, this: Obj, other: Obj) -> Obj { + let block = || { + let this = Gc::>::try_from(this)?; + let this = this.deref().inner(); + match op { + ffi::mp_binary_op_t_MP_BINARY_OP_MULTIPLY => { + let other = Gc::>::try_from(other)?; + let other = other.deref().inner(); + (other * this).wrap() + } + ffi::mp_binary_op_t_MP_BINARY_OP_ADD => { + let other = Gc::>::try_from(other)?; + let other = other.deref().inner(); + (other + this).wrap() + } + _ => Ok(Obj::const_null()), + } + }; + unsafe { util::try_or_raise(block) } +} + +unsafe extern "C" fn scalar_unary_op(op: ffi::mp_unary_op_t, this: Obj) -> Obj { + let block = || { + let this = Gc::>::try_from(this)?; + let this = this.deref().inner(); + match op { + ffi::mp_unary_op_t_MP_UNARY_OP_NEGATIVE => (-this).wrap(), + ffi::mp_unary_op_t_MP_UNARY_OP_BOOL => Ok(bool::from(!this.is_zero()).into()), + _ => Ok(Obj::const_null()), + } + }; + unsafe { util::try_or_raise(block) } +} + +unsafe extern "C" fn scalar_to_bytes(self_in: Obj) -> Obj { + let block = || { + let this = Gc::>::try_from(self_in)?; + let bytes: [u8; 32] = this.deref().inner().to_repr(); + bytes.try_into() + }; + unsafe { util::try_or_raise(block) } +} + +#[no_mangle] +pub unsafe extern "C" fn to_scalar(bytes: Obj) -> Obj { + let block = || { + let bytes: [u8; 64] = bytes.try_into()?; + let elem = Scalar::from_bytes_wide(&bytes); + elem.wrap() + }; + unsafe { util::try_or_raise(block) } +} + +#[no_mangle] +pub unsafe extern "C" fn scalar_from_i64(n: Obj) -> Obj { + let block = || { + let n: i64 = n.try_into()?; + if n >= 0 { + Scalar::from(n as u64).wrap() + } else { + (-Scalar::from(-n as u64)).wrap() + } + }; + unsafe { util::try_or_raise(block) } +} diff --git a/core/embed/rust/src/zcash_primitives/poseidon/constants.rs b/core/embed/rust/src/zcash_primitives/poseidon/constants.rs new file mode 100644 index 000000000..d27aee46c --- /dev/null +++ b/core/embed/rust/src/zcash_primitives/poseidon/constants.rs @@ -0,0 +1,1350 @@ +//! https://github.com/daira/pasta-hadeshash/blob/master/code/generate_parameters_grain.sage + +use pasta_curves::pallas; + +#[cfg(test)] +pub(crate) const ROUND_CONSTANTS: [[pallas::Base; 3]; 64] = [ + [ + pallas::Base::from_raw([ + 0x5753_8c25_9642_6303, + 0x4e71_162f_3100_3b70, + 0x353f_628f_76d1_10f3, + 0x360d_7470_611e_473d, + ]), + pallas::Base::from_raw([ + 0xbdb7_4213_bf63_188b, + 0x4908_ac2f_12eb_e06f, + 0x5dc3_c6c5_febf_aa31, + 0x2bab_94d7_ae22_2d13, + ]), + pallas::Base::from_raw([ + 0x0939_d927_53cc_5dc8, + 0xef77_e7d7_3676_6c5d, + 0x2bf0_3e1a_29aa_871f, + 0x150c_93fe_f652_fb1c, + ]), + ], + [ + pallas::Base::from_raw([ + 0x1425_9dce_5377_82b2, + 0x03cc_0a60_141e_894e, + 0x955d_55db_56dc_57c1, + 0x3270_661e_6892_8b3a, + ]), + pallas::Base::from_raw([ + 0xce9f_b9ff_c345_afb3, + 0xb407_c370_f2b5_a1cc, + 0xa0b7_afe4_e205_7299, + 0x073f_116f_0412_2e25, + ]), + pallas::Base::from_raw([ + 0x8eba_d76f_c715_54d8, + 0x55c9_cd20_61ae_93ca, + 0x7aff_d09c_1f53_f5fd, + 0x2a32_ec5c_4ee5_b183, + ]), + ], + [ + pallas::Base::from_raw([ + 0x2d8c_cbe2_92ef_eead, + 0x634d_24fc_6e25_59f2, + 0x651e_2cfc_7406_28ca, + 0x2703_26ee_039d_f19e, + ]), + pallas::Base::from_raw([ + 0xa068_fc37_c182_e274, + 0x8af8_95bc_e012_f182, + 0xdc10_0fe7_fcfa_5491, + 0x27c6_642a_c633_bc66, + ]), + pallas::Base::from_raw([ + 0x9ca1_8682_e26d_7ff9, + 0x710e_1fb6_ab97_6a45, + 0xd27f_5739_6989_129d, + 0x1bdf_d8b0_1401_c70a, + ]), + ], + [ + pallas::Base::from_raw([ + 0xc832_d824_261a_35ea, + 0xf4f6_fb3f_9054_d373, + 0x14b9_d6a9_c84d_d678, + 0x162a_14c6_2f9a_89b8, + ]), + pallas::Base::from_raw([ + 0xf798_2466_7b5b_6bec, + 0xac0a_1fc7_1e2c_f0c0, + 0x2af6_f79e_3127_feea, + 0x2d19_3e0f_76de_586b, + ]), + pallas::Base::from_raw([ + 0x5d0b_f58d_c8a4_aa94, + 0x4fef_f829_8499_0ff8, + 0x8169_6ef1_104e_674f, + 0x044c_a3cc_4a85_d73b, + ]), + ], + [ + pallas::Base::from_raw([ + 0x6198_785f_0cd6_b9af, + 0xb8d9_e2d4_f314_f46f, + 0x1d04_5341_6d3e_235c, + 0x1cba_f2b3_71da_c6a8, + ]), + pallas::Base::from_raw([ + 0x343e_0761_0f3f_ede5, + 0x293c_4ab0_38fd_bbdc, + 0x0e6c_49d0_61b6_b5f4, + 0x1d5b_2777_692c_205b, + ]), + pallas::Base::from_raw([ + 0xf60e_971b_8d73_b04f, + 0x06a9_adb0_c1e6_f962, + 0xaa30_535b_dd74_9a7e, + 0x2e9b_dbba_3dd3_4bff, + ]), + ], + [ + pallas::Base::from_raw([ + 0x035a_1366_1f22_418b, + 0xde40_fbe2_6d04_7b05, + 0x8bd5_bae3_6969_299f, + 0x2de1_1886_b180_11ca, + ]), + pallas::Base::from_raw([ + 0xbc99_8884_ba96_a721, + 0x2ab9_395c_449b_e947, + 0x0d5b_4a3f_1841_dcd8, + 0x2e07_de17_80b8_a70d, + ]), + pallas::Base::from_raw([ + 0x825e_4c2b_b749_25ca, + 0x2504_40a9_9d6b_8af3, + 0xbbdb_63db_d52d_ad16, + 0x0f69_f185_4d20_ca0c, + ]), + ], + [ + pallas::Base::from_raw([ + 0x816c_0594_22dc_705e, + 0x6ce5_1135_07f9_6de9, + 0x0d13_5dc6_39fb_09a4, + 0x2eb1_b254_17fe_1767, + ]), + pallas::Base::from_raw([ + 0xb8b1_bdf4_953b_d82c, + 0xff36_c661_d26c_c42d, + 0x8c24_cb44_c3fa_b48a, + 0x115c_d0a0_643c_fb98, + ]), + pallas::Base::from_raw([ + 0xde80_1612_311d_04cd, + 0xbb57_ddf1_4e0f_958a, + 0x066d_7378_b999_868b, + 0x26ca_293f_7b2c_462d, + ]), + ], + [ + pallas::Base::from_raw([ + 0xf520_9d14_b248_20ca, + 0x0f16_0bf9_f71e_967f, + 0x2a83_0aa1_6241_2cd9, + 0x17bf_1b93_c4c7_e01a, + ]), + pallas::Base::from_raw([ + 0x05c8_6f2e_7dc2_93c5, + 0xe03c_0354_bd8c_fd38, + 0xa24f_8456_369c_85df, + 0x35b4_1a7a_c4f3_c571, + ]), + pallas::Base::from_raw([ + 0x72ac_156a_f435_d09e, + 0x64e1_4d3b_eb2d_ddde, + 0x4359_2799_4849_bea9, + 0x3b14_8008_0523_c439, + ]), + ], + [ + pallas::Base::from_raw([ + 0x2716_18d8_74b1_4c6d, + 0x08e2_8644_2a2d_3eb2, + 0x4950_856d_c907_d575, + 0x2cc6_8100_31dc_1b0d, + ]), + pallas::Base::from_raw([ + 0x91f3_18c0_9f0c_b566, + 0x9e51_7aa9_3b78_341d, + 0x0596_18e2_afd2_ef99, + 0x25bd_bbed_a1bd_e8c1, + ]), + pallas::Base::from_raw([ + 0xc631_3487_073f_7f7b, + 0x2a5e_d0a2_7b61_926c, + 0xb95f_33c2_5dde_8ac0, + 0x392a_4a87_58e0_6ee8, + ]), + ], + [ + pallas::Base::from_raw([ + 0xe7bb_cef0_2eb5_866c, + 0x5e6a_6fd1_5db8_9365, + 0x9aa6_111f_4de0_0948, + 0x272a_5587_8a08_442b, + ]), + pallas::Base::from_raw([ + 0x9b92_5b3c_5b21_e0e2, + 0xa6eb_ba01_1694_dd12, + 0xefa1_3c4e_60e2_6239, + 0x2d5b_308b_0cf0_2cdf, + ]), + pallas::Base::from_raw([ + 0xef38_c57c_3116_73ac, + 0x44df_f42f_18b4_6c56, + 0xdd5d_293d_72e2_e5f2, + 0x1654_9fc6_af2f_3b72, + ]), + ], + [ + pallas::Base::from_raw([ + 0x9b71_26d9_b468_60df, + 0x7639_8265_3442_0311, + 0xfa69_c3a2_ad52_f76d, + 0x1b10_bb7a_82af_ce39, + ]), + pallas::Base::from_raw([ + 0x90d2_7f6a_00b7_dfc8, + 0xd1b3_6968_ba04_05c0, + 0xc79c_2df7_dc98_a3be, + 0x0f1e_7505_ebd9_1d2f, + ]), + pallas::Base::from_raw([ + 0xff45_7756_b819_bb20, + 0x797f_d6e3_f18e_b1ca, + 0x537a_7497_a3b4_3f46, + 0x2f31_3faf_0d3f_6187, + ]), + ], + [ + pallas::Base::from_raw([ + 0xf0bc_3e73_2ecb_26f6, + 0x5cad_11eb_f0f7_ceb8, + 0xfa3c_a61c_0ed1_5bc5, + 0x3a5c_bb6d_e450_b481, + ]), + pallas::Base::from_raw([ + 0x8655_27cb_ca91_5982, + 0x51ba_a6e2_0f89_2b62, + 0xd920_86e2_53b4_39d6, + 0x3dab_54bc_9bef_688d, + ]), + pallas::Base::from_raw([ + 0x3680_45ac_f2b7_1ae3, + 0x4c24_b33b_410f_efd4, + 0xe280_d316_7012_3f74, + 0x06db_fb42_b979_884d, + ]), + ], + [ + pallas::Base::from_raw([ + 0xa7fc_32d2_2f18_b9d3, + 0xb8d2_de72_e3d2_c9ec, + 0xc6f0_39ea_1973_a63e, + 0x068d_6b46_08aa_e810, + ]), + pallas::Base::from_raw([ + 0x2b5d_fcc5_5725_55df, + 0xb868_a7d7_e1f1_f69a, + 0x0ee2_58c9_b8fd_fccd, + 0x366e_bfaf_a3ad_381c, + ]), + pallas::Base::from_raw([ + 0xe6bc_229e_95bc_76b1, + 0x7ef6_6d89_d044_d022, + 0x04db_3024_f41d_3f56, + 0x3967_8f65_512f_1ee4, + ]), + ], + [ + pallas::Base::from_raw([ + 0xe534_c88f_e53d_85fe, + 0xcf82_c25f_99dc_01a4, + 0xd58b_7750_a3bc_2fe1, + 0x2166_8f01_6a80_63c0, + ]), + pallas::Base::from_raw([ + 0x4bef_429b_c533_1608, + 0xe34d_ea56_439f_e195, + 0x1bc7_4936_3e98_a768, + 0x39d0_0994_a8a5_046a, + ]), + pallas::Base::from_raw([ + 0x770c_956f_60d8_81b3, + 0xb163_d416_05d3_9f99, + 0x6b20_3bbe_12fb_3425, + 0x1f9d_bdc3_f843_1263, + ]), + ], + [ + pallas::Base::from_raw([ + 0x9794_a9f7_c336_eab2, + 0xbe0b_c829_fe5e_66c6, + 0xe5f1_7b9e_0ee0_cab6, + 0x0277_45a9_cddf_ad95, + ]), + pallas::Base::from_raw([ + 0x5202_5657_abd8_aee0, + 0x2fa4_3fe2_0a45_c78d, + 0x788d_695c_61e9_3212, + 0x1cec_0803_c504_b635, + ]), + pallas::Base::from_raw([ + 0xd387_2a95_59a0_3a73, + 0xed50_82c8_dbf3_1365, + 0x7207_7448_ef87_cc6e, + 0x1235_23d7_5e9f_abc1, + ]), + ], + [ + pallas::Base::from_raw([ + 0x0017_79e3_a1d3_57f4, + 0x27fe_ba35_975e_e7e5, + 0xf419_b848_e5d6_94bf, + 0x1723_d145_2c9c_f02d, + ]), + pallas::Base::from_raw([ + 0x9dab_1ee4_dcf9_6622, + 0x21c3_f776_f572_836d, + 0xfcc0_573d_7e61_3694, + 0x1739_d180_a160_10bd, + ]), + pallas::Base::from_raw([ + 0x7029_0452_042d_048d, + 0xfafa_96fb_eb0a_b893, + 0xacce_3239_1794_b627, + 0x2d4e_6354_da9c_c554, + ]), + ], + [ + pallas::Base::from_raw([ + 0x670b_cf6f_8b48_5dcd, + 0x8f3b_d43f_9926_0621, + 0x4a86_9553_c9d0_07f8, + 0x153e_e614_2e53_5e33, + ]), + pallas::Base::from_raw([ + 0xd258_d2e2_b778_2172, + 0x968a_d442_4af8_3700, + 0x635e_f7e7_a430_b486, + 0x0c45_bfd3_a69a_aa65, + ]), + pallas::Base::from_raw([ + 0x0e56_33d2_51f7_3307, + 0x6897_ac0a_8ffa_5ff1, + 0xf2d5_6aec_8314_4600, + 0x0adf_d53b_256a_6957, + ]), + ], + [ + pallas::Base::from_raw([ + 0xac9d_36a8_b751_6d63, + 0x3f87_b28f_1c1b_e4bd, + 0x8cd1_726b_7cba_b8ee, + 0x315d_2ac8_ebdb_ac3c, + ]), + pallas::Base::from_raw([ + 0x299c_e44e_a423_d8e1, + 0xc9bb_60d1_f695_9879, + 0xcfae_c23d_2b16_883f, + 0x1b84_7271_2d02_eef4, + ]), + pallas::Base::from_raw([ + 0xc4a5_4041_98ad_f70c, + 0x367d_2c54_e369_28c9, + 0xbd0b_70fa_2255_eb6f, + 0x3c1c_d07e_fda6_ff24, + ]), + ], + [ + pallas::Base::from_raw([ + 0xbbe5_23ae_f9ab_107a, + 0x4a16_073f_738f_7e0c, + 0x687f_4e51_b2e1_dcd3, + 0x1360_52d2_6bb3_d373, + ]), + pallas::Base::from_raw([ + 0x676c_36c2_4ef9_67dd, + 0x7b3c_fbb8_7303_2681, + 0xc1bd_d859_a123_2a1d, + 0x16c9_6bee_f6a0_a848, + ]), + pallas::Base::from_raw([ + 0x067e_ec7f_2d63_40c4, + 0x0123_87ba_b4f1_662d, + 0x2ab7_fed8_f499_a9fb, + 0x284b_38c5_7ff6_5c26, + ]), + ], + [ + pallas::Base::from_raw([ + 0xaf1d_ff20_4c92_2f86, + 0xfc06_772c_1c04_11a6, + 0x39e2_4219_8897_d17c, + 0x0c59_93d1_75e8_1f66, + ]), + pallas::Base::from_raw([ + 0xbbf5_3f67_b1f8_7b15, + 0xf248_87ad_48e1_7759, + 0xfcda_655d_1ba9_c8f9, + 0x03bf_7a3f_7bd0_43da, + ]), + pallas::Base::from_raw([ + 0x9b5c_d09e_36d8_be62, + 0x4c8f_9cbe_69f0_e827, + 0xb0cf_9995_67f0_0e73, + 0x3188_fe4e_e9f9_fafb, + ]), + ], + [ + pallas::Base::from_raw([ + 0xafea_99a2_ec6c_595a, + 0x3af5_bf77_c1c4_2652, + 0x5a39_768c_480d_61e1, + 0x171f_528c_cf65_8437, + ]), + pallas::Base::from_raw([ + 0x5a05_63b9_b8e9_f1d5, + 0x812c_3286_ee70_0067, + 0x196e_4185_9b35_ef88, + 0x12f4_175c_4ab4_5afc, + ]), + pallas::Base::from_raw([ + 0x0e74_d4d3_6911_8b79, + 0x7e23_e1aa_be96_cfab, + 0x8f8f_dcf8_00a9_ac69, + 0x3a50_9e15_5cb7_ebfd, + ]), + ], + [ + pallas::Base::from_raw([ + 0x9871_2c65_678c_fd30, + 0x984b_c8f2_e4c1_b69e, + 0x1a89_920e_2504_c3b3, + 0x10f2_a685_df4a_27c8, + ]), + pallas::Base::from_raw([ + 0xe8a1_6728_cc9d_4918, + 0x5457_3c93_33c5_6321, + 0x1d8d_93d5_4ab9_1a0e, + 0x09e5_f497_90c8_a0e2, + ]), + pallas::Base::from_raw([ + 0x609a_7403_47cf_5fea, + 0x42d1_7ed6_ee0f_ab7e, + 0x2bf3_5705_d9f8_4a34, + 0x352d_69be_d80e_e3e5, + ]), + ], + [ + pallas::Base::from_raw([ + 0x3a75_8af6_fa84_e0e8, + 0xc634_debd_281b_76a6, + 0x4915_62fa_f2b1_90d3, + 0x058e_e73b_a9f3_f293, + ]), + pallas::Base::from_raw([ + 0x621a_1325_10a4_3904, + 0x092c_b921_19bc_76be, + 0xcd0f_1fc5_5b1a_3250, + 0x232f_99cc_911e_ddd9, + ]), + pallas::Base::from_raw([ + 0xc3b9_7c1e_301b_c213, + 0xf9ef_d52c_a6bc_2961, + 0x86c2_2c6c_5d48_69f0, + 0x201b_eed7_b8f3_ab81, + ]), + ], + [ + pallas::Base::from_raw([ + 0xbf6b_3431_ba94_e9bc, + 0x2938_8842_744a_1210, + 0xa1c9_291d_5860_2f51, + 0x1376_dce6_5800_30c6, + ]), + pallas::Base::from_raw([ + 0x6454_843c_5486_d7b3, + 0x072b_a8b0_2d92_e722, + 0x2b33_56c3_8238_f761, + 0x1793_199e_6fd6_ba34, + ]), + pallas::Base::from_raw([ + 0x06a3_f1d3_b433_311b, + 0x3c66_160d_c62a_acac, + 0x9fee_9c20_c87a_67df, + 0x22de_7a74_88dc_c735, + ]), + ], + [ + pallas::Base::from_raw([ + 0x30d6_e3fd_516b_47a8, + 0xdbe0_b77f_ae77_e1d0, + 0xdf8f_f37f_e2d8_edf8, + 0x3514_d5e9_066b_b160, + ]), + pallas::Base::from_raw([ + 0x1937_7427_137a_81c7, + 0xff45_3d6f_900f_144a, + 0xf919_a00d_abbf_5fa5, + 0x30cd_3006_931a_d636, + ]), + pallas::Base::from_raw([ + 0x5b6a_7422_0692_b506, + 0x8f9e_4b2c_ae2e_bb51, + 0x41f8_1a5c_f613_c8df, + 0x253d_1a5c_5293_4127, + ]), + ], + [ + pallas::Base::from_raw([ + 0x73f6_66cb_86a4_8e8e, + 0x851b_3a59_c990_fafc, + 0xa35e_9613_e7f5_fe92, + 0x035b_461c_02d7_9d19, + ]), + pallas::Base::from_raw([ + 0x7cfb_f86a_3aa0_4780, + 0x92b1_283c_2d5f_ccde, + 0x5bc0_0eed_d56b_93e0, + 0x23a9_9280_79d1_75bd, + ]), + pallas::Base::from_raw([ + 0xf1e4_ccd7_3fa0_0a82, + 0xb5e2_ea34_36ee_f957, + 0xf159_4a07_63c6_11ab, + 0x13a7_785a_e134_ea92, + ]), + ], + [ + pallas::Base::from_raw([ + 0xbbf0_4f52_52de_4279, + 0x3889_c578_6344_6d88, + 0x4962_ae3c_0da1_7e31, + 0x39fc_e308_b7d4_3c57, + ]), + pallas::Base::from_raw([ + 0x3b57_e344_89b5_3fad, + 0xbef0_0a08_c6ed_38d2, + 0xc0fd_f016_62f6_0d22, + 0x1aae_1883_3f8e_1d3a, + ]), + pallas::Base::from_raw([ + 0x5551_3e03_3398_513f, + 0x27c1_b3fd_8f85_d8a8, + 0x8b2e_80c0_64fd_83ed, + 0x1a76_1ce8_2400_af01, + ]), + ], + [ + pallas::Base::from_raw([ + 0x5244_ca74_9b73_e481, + 0xdcf6_af28_30a5_0287, + 0x16dd_1a87_ca22_e1cc, + 0x275a_03e4_5add_a7c3, + ]), + pallas::Base::from_raw([ + 0x58a2_53cf_b6a9_5786, + 0x07e5_6145_3fc5_648b, + 0xeb08_e47e_5fea_bcf8, + 0x2e5a_10f0_8b5a_b8bb, + ]), + pallas::Base::from_raw([ + 0xe033_d82c_efe7_8ce3, + 0xc141_a5b6_d594_bec4, + 0xb84e_9c33_3b29_32f1, + 0x1459_cb85_8720_8473, + ]), + ], + [ + pallas::Base::from_raw([ + 0x5cec_7e7b_338f_be1b, + 0x52f9_332f_bffc_fbbd, + 0x7b92_ce81_0e14_a400, + 0x193a_e592_1d78_b5de, + ]), + pallas::Base::from_raw([ + 0x6022_4be6_7248_e82c, + 0x3743_84f4_a072_8205, + 0x8911_1fb2_c466_0281, + 0x3097_898a_5d00_11a4, + ]), + pallas::Base::from_raw([ + 0x5499_80de_8629_30f5, + 0x1979_b2d1_c465_b4d9, + 0x5717_82fd_96ce_54b4, + 0x378d_97bf_8c86_4ae7, + ]), + ], + [ + pallas::Base::from_raw([ + 0x37ea_32a9_71d1_7884, + 0xdbc7_f5cb_4609_3421, + 0x8813_6287_ce37_6b08, + 0x2eb0_4ea7_c01d_97ec, + ]), + pallas::Base::from_raw([ + 0xead3_726f_1af2_e7b0, + 0x861c_bda4_7680_4e6c, + 0x2302_a1c2_2e49_baec, + 0x3642_5347_ea03_f641, + ]), + pallas::Base::from_raw([ + 0xecd6_27e5_9590_d09e, + 0x3f5b_5ca5_a19a_9701, + 0xcc99_6cd8_5c98_a1d8, + 0x26b7_2df4_7408_ad42, + ]), + ], + [ + pallas::Base::from_raw([ + 0x59be_ce31_f0a3_1e95, + 0xde01_212e_e458_8f89, + 0x1f05_636c_610b_89aa, + 0x1301_80e4_4e29_24db, + ]), + pallas::Base::from_raw([ + 0x9ea8_e7bc_7926_3550, + 0xdf77_93cc_89e5_b52f, + 0x7327_5aca_ed5f_579c, + 0x219e_9773_7d39_79ba, + ]), + pallas::Base::from_raw([ + 0x9c12_635d_f251_d153, + 0x3b06_72dd_7d42_cbb4, + 0x3461_363f_81c4_89a2, + 0x3cdb_9359_8a5c_a528, + ]), + ], + [ + pallas::Base::from_raw([ + 0x2861_ce16_f219_d5a9, + 0x4ad0_4470_45a7_c5aa, + 0x2072_4b92_7a0c_a81c, + 0x0e59_e6f3_32d7_ed37, + ]), + pallas::Base::from_raw([ + 0x43b0_a3fc_ff20_36bd, + 0x172c_c07b_9d33_fbf9, + 0x3d73_6946_7222_697a, + 0x1b06_4342_d51a_4275, + ]), + pallas::Base::from_raw([ + 0x3eb3_1022_8a0e_5f6c, + 0x78fa_9fb9_1712_21b7, + 0x2f36_3c55_b288_2e0b, + 0x30b8_2a99_8cbd_8e8a, + ]), + ], + [ + pallas::Base::from_raw([ + 0xe46f_6d42_9874_0107, + 0x8ad7_1ea7_15be_0573, + 0x63df_7a76_e858_a4aa, + 0x23e4_ab37_183a_cba4, + ]), + pallas::Base::from_raw([ + 0xfca9_95e2_b599_14a1, + 0xacfe_1464_0de0_44f2, + 0x5d33_094e_0bed_a75b, + 0x2795_d5c5_fa42_8022, + ]), + pallas::Base::from_raw([ + 0xc26d_909d_ee8b_53c0, + 0xa668_7c3d_f16c_8fe4, + 0xd765_f26d_d03f_4c45, + 0x3001_ca40_1e89_601c, + ]), + ], + [ + pallas::Base::from_raw([ + 0xe7fe_a6bd_f347_1380, + 0xe84b_5beb_ae4e_501d, + 0xf7bf_86e8_9280_827f, + 0x0072_e45c_c676_b08e, + ]), + pallas::Base::from_raw([ + 0xd0c5_4dde_b26b_86c0, + 0xb648_29e2_d40e_41bd, + 0xe2ab_e4c5_18ce_599e, + 0x13de_7054_8487_4bb5, + ]), + pallas::Base::from_raw([ + 0x3891_5b43_2a99_59a5, + 0x82bb_18e5_af1b_05bb, + 0x3159_50f1_211d_efe8, + 0x0408_a9fc_f9d6_1abf, + ]), + ], + [ + pallas::Base::from_raw([ + 0x3407_0cbe_e268_86a0, + 0xae4d_23b0_b41b_e9a8, + 0xbb4e_4a14_00cc_d2c4, + 0x2780_b9e7_5b55_676e, + ]), + pallas::Base::from_raw([ + 0x9405_5920_98b4_056f, + 0xdc4d_8fbe_fe24_405a, + 0xf803_33ec_8563_4ac9, + 0x3a57_0d4d_7c4e_7ac3, + ]), + pallas::Base::from_raw([ + 0x78d2_b247_8995_20b4, + 0xe2cc_1507_bebd_cc62, + 0xf347_c247_fcf0_9294, + 0x0c13_cca7_cb1f_9d2c, + ]), + ], + [ + pallas::Base::from_raw([ + 0x2e8c_88f7_7074_70e0, + 0x0b50_bb2e_b82d_f74d, + 0xd261_4a19_7c6b_794b, + 0x14f5_9baa_03cd_0ca4, + ]), + pallas::Base::from_raw([ + 0xbe52_476e_0a16_f3be, + 0xa51d_54ed_e661_67f5, + 0x6f54_6e17_04c3_9c60, + 0x307d_efee_925d_fb43, + ]), + pallas::Base::from_raw([ + 0x380b_67d8_0473_dce3, + 0x6611_0683_6adf_e5e7, + 0x7a07_e767_4b5a_2621, + 0x1960_cd51_1a91_e060, + ]), + ], + [ + pallas::Base::from_raw([ + 0x15aa_f1f7_7125_89dd, + 0xb8ee_335d_8828_4cbe, + 0xca2a_d0fb_5667_2500, + 0x2301_ef9c_63ea_84c5, + ]), + pallas::Base::from_raw([ + 0x5e68_478c_4d60_27a9, + 0xc861_82d1_b424_6b58, + 0xd10f_4cd5_2be9_7f6b, + 0x029a_5a47_da79_a488, + ]), + pallas::Base::from_raw([ + 0x2cc4_f962_eaae_2260, + 0xf97f_e46b_6a92_5428, + 0x2360_d17d_890e_55cb, + 0x32d7_b16a_7f11_cc96, + ]), + ], + [ + pallas::Base::from_raw([ + 0xc0ca_b915_d536_3d9f, + 0xa5f2_404c_d7b3_5eb0, + 0x18e8_57a9_8d49_8cf7, + 0x2670_3e48_c03b_81ca, + ]), + pallas::Base::from_raw([ + 0xf691_123a_e112_b928, + 0xf443_88bd_6b89_221e, + 0x88ac_8d25_a246_03f1, + 0x0486_82a3_5b32_65bc, + ]), + pallas::Base::from_raw([ + 0x3ab7_defc_b8d8_03e2, + 0x91d6_e171_5164_775e, + 0xd72c_ddc6_cf06_b507, + 0x06b1_3904_41fa_7030, + ]), + ], + [ + pallas::Base::from_raw([ + 0xbcd7_9541_4a6e_2e86, + 0x43b3_60f6_386a_86d7, + 0x1689_426d_ce05_fcd8, + 0x31aa_0eeb_868c_626d, + ]), + pallas::Base::from_raw([ + 0xed77_f5d5_76b9_9cc3, + 0x90ef_d8f4_1b20_78b2, + 0x057a_bad3_764c_104b, + 0x2394_64f7_5bf7_b6af, + ]), + pallas::Base::from_raw([ + 0xb2cb_4873_07c1_cecf, + 0xa5cc_47c5_9654_b2a7, + 0xa45e_19ed_813a_54ab, + 0x0a64_d4c0_4fd4_26bd, + ]), + ], + [ + pallas::Base::from_raw([ + 0x1f73_1532_2f65_8735, + 0x777c_7a92_1a06_2e9d, + 0x576a_4ad2_5986_0fb1, + 0x21fb_bdbb_7367_0734, + ]), + pallas::Base::from_raw([ + 0x6743_2400_3fc5_2146, + 0x5b86_d294_63d3_1564, + 0xd937_1ca2_eb95_acf3, + 0x31b8_6f3c_f017_05d4, + ]), + pallas::Base::from_raw([ + 0x7045_f48a_a4eb_4f6f, + 0x1354_1d65_157e_e1ce, + 0x05ef_1736_d090_56f6, + 0x2bfd_e533_5437_7c91, + ]), + ], + [ + pallas::Base::from_raw([ + 0x5a13_a58d_2001_1e2f, + 0xf4d5_239c_11d0_eafa, + 0xd558_f36e_65f8_eca7, + 0x1233_ca93_6ec2_4671, + ]), + pallas::Base::from_raw([ + 0x6e70_af0a_7a92_4b3a, + 0x8780_58d0_234a_576f, + 0xc437_846d_8e0b_2b30, + 0x27d4_52a4_3ac7_dea2, + ]), + pallas::Base::from_raw([ + 0xa025_76b9_4392_f980, + 0x6a30_641a_1c3d_87b2, + 0xe816_ea8d_a493_e0fa, + 0x2699_dba8_2184_e413, + ]), + ], + [ + pallas::Base::from_raw([ + 0x608c_6f7a_61b5_6e55, + 0xf185_8466_4f8c_ab49, + 0xc398_8bae_e42e_4b10, + 0x36c7_22f0_efcc_8803, + ]), + pallas::Base::from_raw([ + 0x6e49_ac17_0dbb_7fcd, + 0x85c3_8899_a7b5_a833, + 0x08b0_f2ec_89cc_aa37, + 0x02b3_ff48_861e_339b, + ]), + pallas::Base::from_raw([ + 0xa8c5_ae03_ad98_e405, + 0x6fc3_ff4c_49eb_59ad, + 0x6016_2f44_27bc_657b, + 0x0b70_d061_d58d_8a7f, + ]), + ], + [ + pallas::Base::from_raw([ + 0x2e06_cc4a_f33b_0a06, + 0xad3d_e8be_46ed_9693, + 0xf875_3ade_b9d7_cee2, + 0x3fc2_a13f_127f_96a4, + ]), + pallas::Base::from_raw([ + 0xc120_80ac_117e_e15f, + 0x00cb_3d62_1e17_1d80, + 0x1bd6_3434_ac8c_419f, + 0x0c41_a6e4_8dd2_3a51, + ]), + pallas::Base::from_raw([ + 0x9685_213e_9692_f5e1, + 0x72aa_ad7e_4e75_339d, + 0xed44_7653_7169_084e, + 0x2de8_072a_6bd8_6884, + ]), + ], + [ + pallas::Base::from_raw([ + 0x0ad0_1184_567b_027c, + 0xb81c_f735_cc9c_39c0, + 0x9d34_96a3_d9fe_05ec, + 0x0355_7a8f_7b38_a17f, + ]), + pallas::Base::from_raw([ + 0x45bc_b5ac_0082_6abc, + 0x060f_4336_3d81_8e54, + 0xee97_6d34_282f_1a37, + 0x0b5f_5955_2f49_8735, + ]), + pallas::Base::from_raw([ + 0x2f29_09e1_7e22_b0df, + 0xf5d6_46e5_7507_e548, + 0xfedb_b185_70dc_7300, + 0x0e29_23a5_fee7_b878, + ]), + ], + [ + pallas::Base::from_raw([ + 0xf71e_ed73_f15b_3326, + 0xcf1c_b37c_3b03_2af6, + 0xc787_be97_020a_7fdd, + 0x1d78_5005_a7a0_0592, + ]), + pallas::Base::from_raw([ + 0x0acf_bfb2_23f8_f00d, + 0xa590_b88a_3b06_0294, + 0x0ba5_fedc_b8f2_5bd2, + 0x1ad7_72c2_73d9_c6df, + ]), + pallas::Base::from_raw([ + 0xc1ce_13d6_0f2f_5031, + 0x8105_10eb_61f0_672d, + 0xa78f_3275_c278_234b, + 0x027b_d647_85fc_bd2a, + ]), + ], + [ + pallas::Base::from_raw([ + 0x8337_f5e0_7923_a853, + 0xe224_3134_6945_7b8e, + 0xce6f_8ffe_a103_1b6d, + 0x2080_0f44_1b4a_0526, + ]), + pallas::Base::from_raw([ + 0xa33d_7bed_89a4_408a, + 0x36cd_c8ee_d662_ad37, + 0x6eea_2cd4_9f43_12b4, + 0x3d5a_d61d_7b65_f938, + ]), + pallas::Base::from_raw([ + 0x3bbb_ae94_cc19_5284, + 0x1df9_6cc0_3ea4_b26d, + 0x02c5_f91b_e4dd_8e3d, + 0x1333_8bc3_51fc_46dd, + ]), + ], + [ + pallas::Base::from_raw([ + 0xc527_1c29_7852_819e, + 0x646c_49f9_b46c_bf19, + 0xb87d_b1e2_af3e_a923, + 0x25e5_2be5_07c9_2760, + ]), + pallas::Base::from_raw([ + 0x5c38_0ab7_01b5_2ea9, + 0xa34c_83a3_485c_6b2d, + 0x7109_6d8b_1b98_3c98, + 0x1c49_2d64_c157_aaa4, + ]), + pallas::Base::from_raw([ + 0xa20c_0b3d_a0da_4ca3, + 0xd434_87bc_288d_f682, + 0xf4e6_c5e7_a573_f592, + 0x0c5b_8015_7999_2718, + ]), + ], + [ + pallas::Base::from_raw([ + 0x7ea3_3c93_e408_33cf, + 0x584e_9e62_a7f9_554e, + 0x6869_5c0c_d7cb_f43d, + 0x1090_b1b4_d2be_be7a, + ]), + pallas::Base::from_raw([ + 0xe383_e1ec_3baa_8d69, + 0x1b21_8e35_ecf2_328e, + 0x68f5_ce5c_bed1_9cad, + 0x33e3_8018_a801_387a, + ]), + pallas::Base::from_raw([ + 0xb76b_0b3d_787e_e953, + 0x5f4a_02d2_8729_e3ae, + 0xeef8_d83d_0e87_6bac, + 0x1654_af18_772b_2da5, + ]), + ], + [ + pallas::Base::from_raw([ + 0xef7c_e6a0_1326_5477, + 0xbb08_9387_0367_ec6c, + 0x4474_2de8_8c5a_b0d5, + 0x1678_be3c_c9c6_7993, + ]), + pallas::Base::from_raw([ + 0xaf5d_4789_3348_f766, + 0xdaf1_8183_55b1_3b4f, + 0x7ff9_c6be_546e_928a, + 0x3780_bd1e_01f3_4c22, + ]), + pallas::Base::from_raw([ + 0xa123_8032_0d7c_c1de, + 0x5d11_e69a_a6c0_b98c, + 0x0786_018e_7cb7_7267, + 0x1e83_d631_5c9f_125b, + ]), + ], + [ + pallas::Base::from_raw([ + 0x1799_603e_855c_e731, + 0xc486_894d_76e0_c33b, + 0x160b_4155_2f29_31c8, + 0x354a_fd0a_2f9d_0b26, + ]), + pallas::Base::from_raw([ + 0x8b99_7ee0_6be1_bff3, + 0x60b0_0dbe_1fac_ed07, + 0x2d8a_ffa6_2905_c5a5, + 0x00cd_6d29_f166_eadc, + ]), + pallas::Base::from_raw([ + 0x08d0_6419_1708_2f2c, + 0xc60d_0197_3f18_3057, + 0xdbe0_e3d7_cdbc_66ef, + 0x1d62_1935_2768_e3ae, + ]), + ], + [ + pallas::Base::from_raw([ + 0xfa08_dd98_0638_7577, + 0xafe3_ca1d_b8d4_f529, + 0xe48d_2370_d7d1_a142, + 0x1463_36e2_5db5_181d, + ]), + pallas::Base::from_raw([ + 0xa901_d3ce_84de_0ad4, + 0x022e_54b4_9c13_d907, + 0x997a_2116_3e2e_43df, + 0x0005_d8e0_85fd_72ee, + ]), + pallas::Base::from_raw([ + 0x1c36_f313_4196_4484, + 0x6f8e_bc1d_2296_021a, + 0x0dd5_e61c_8a4e_8642, + 0x364e_97c7_a389_3227, + ]), + ], + [ + pallas::Base::from_raw([ + 0xd7a0_0c03_d2e0_baaa, + 0xfa97_ec80_ad30_7a52, + 0x561c_6fff_1534_6878, + 0x0118_9910_671b_c16b, + ]), + pallas::Base::from_raw([ + 0x63fd_8ac5_7a95_ca8c, + 0x4c0f_7e00_1df4_90aa, + 0x5229_dfaa_0123_1a45, + 0x162a_7c80_f4d2_d12e, + ]), + pallas::Base::from_raw([ + 0x32e6_9efb_22f4_0b96, + 0xcaff_31b4_fda3_2124, + 0x2604_e4af_b09f_8603, + 0x2a0d_6c09_5766_66bb, + ]), + ], + [ + pallas::Base::from_raw([ + 0xc0a0_180f_8cbf_c0d2, + 0xf444_d10d_63a7_4e2c, + 0xe16a_4d60_3d5a_808e, + 0x0978_e5c5_1e1e_5649, + ]), + pallas::Base::from_raw([ + 0x03f4_460e_bc35_1b6e, + 0x0508_7d90_3bda_cfd1, + 0xebe1_9bbd_ce25_1011, + 0x1bdc_ee3a_aca9_cd25, + ]), + pallas::Base::from_raw([ + 0xf619_64bf_3ade_7670, + 0x0c94_7321_e007_5e3f, + 0xe494_7914_0b19_44fd, + 0x1862_cccb_70b5_b885, + ]), + ], + [ + pallas::Base::from_raw([ + 0xc326_7da6_e94a_dc50, + 0x39ee_99c1_cc6e_5dda, + 0xbc26_cc88_3a19_87e1, + 0x1f3e_91d8_63c1_6922, + ]), + pallas::Base::from_raw([ + 0x0f85_b4ac_2c36_7406, + 0xfa66_1465_c656_ad99, + 0xef5c_08f8_478f_663a, + 0x1af4_7a48_a601_6a49, + ]), + pallas::Base::from_raw([ + 0x0eab_cd87_e7d0_1b15, + 0x1c36_98b0_a2e3_da10, + 0x009d_5733_8c69_3505, + 0x3c8e_e901_956e_3d3f, + ]), + ], + [ + pallas::Base::from_raw([ + 0x8b94_7721_8967_3476, + 0xe10c_e2b7_069f_4dbd, + 0x68d0_b024_f591_b520, + 0x1660_a8cd_e7fe_c553, + ]), + pallas::Base::from_raw([ + 0x9d8d_0f67_fdaa_79d5, + 0x3963_c2c1_f558_6e2f, + 0x1303_9363_34dd_1132, + 0x0f6d_9919_29d5_e4e7, + ]), + pallas::Base::from_raw([ + 0x7a43_3091_e1ce_2d3a, + 0x4e7f_da77_0712_f343, + 0xcc62_5eaa_ab52_b4dc, + 0x02b9_cea1_921c_d9f6, + ]), + ], + [ + pallas::Base::from_raw([ + 0x3797_b2d8_3760_43b3, + 0xd8ca_f468_976f_0472, + 0x214f_7c67_84ac_b565, + 0x14a3_23b9_9b90_0331, + ]), + pallas::Base::from_raw([ + 0x347f_ef2c_00f0_953a, + 0x718b_7fbc_7788_af78, + 0xec01_ea79_642d_5760, + 0x1904_76b5_80cb_9277, + ]), + pallas::Base::from_raw([ + 0xff4e_7e6f_b268_dfd7, + 0x9660_902b_6008_7651, + 0xa424_63d3_0b44_2b6f, + 0x090a_3a9d_869d_2eef, + ]), + ], + [ + pallas::Base::from_raw([ + 0xf983_387e_a045_6203, + 0xe365_0013_04f9_a11e, + 0x0dbe_8fd2_270a_6795, + 0x3877_a955_8636_7567, + ]), + pallas::Base::from_raw([ + 0x39c0_af0f_e01f_4a06, + 0x6011_8c53_a218_1352, + 0x5df3_9a2c_c63d_dc0a, + 0x2d89_4691_240f_e953, + ]), + pallas::Base::from_raw([ + 0x1aca_9eaf_9bba_9850, + 0x5914_e855_eeb4_4aa1, + 0x7ef7_1780_2016_6189, + 0x21b9_c182_92bd_bc59, + ]), + ], + [ + pallas::Base::from_raw([ + 0x33f5_09a7_4ad9_d39b, + 0x272e_1cc6_c36a_2968, + 0x505a_05f2_a6ae_834c, + 0x2fe7_6be7_cff7_23e2, + ]), + pallas::Base::from_raw([ + 0x0df9_fa97_277f_a8b4, + 0xd15b_ff84_0dda_e8a5, + 0x9299_81d7_cfce_253b, + 0x187a_a448_f391_e3ca, + ]), + pallas::Base::from_raw([ + 0xf0c6_6af5_ffc7_3736, + 0x663c_cf7b_2ffe_4b5e, + 0x007a_b3aa_3617_f422, + 0x0b70_83ad_7517_07bf, + ]), + ], + [ + pallas::Base::from_raw([ + 0x2f9b_20f1_fbd4_9791, + 0x1975_b962_f6cb_8e0b, + 0x3bc4_ca99_02c5_2acb, + 0x030d_dbb4_7049_3f16, + ]), + pallas::Base::from_raw([ + 0x3a1c_62ca_8fbf_2525, + 0x8fb8_ab9d_60ea_17b2, + 0x950b_0ab1_8d35_46df, + 0x3130_fbaf_fb5a_a82a, + ]), + pallas::Base::from_raw([ + 0x43a8_7618_0dc3_82e0, + 0x15ce_2ead_2fcd_051e, + 0x4f74_d74b_ac2e_e457, + 0x337f_5447_07c4_30f0, + ]), + ], + [ + pallas::Base::from_raw([ + 0x26de_98a8_736d_1d11, + 0x7d8e_471a_9fb9_5fef, + 0xac9d_91b0_930d_ac75, + 0x3499_7991_9015_394f, + ]), + pallas::Base::from_raw([ + 0xccfc_b618_31d5_c775, + 0x3bf9_3da6_fff3_1d95, + 0x2305_cd7a_921e_c5f1, + 0x027c_c4ef_e3fb_35dd, + ]), + pallas::Base::from_raw([ + 0xc3fa_2629_635d_27de, + 0x67f1_c6b7_3147_64af, + 0x61b7_1a36_9868_2ad2, + 0x037f_9f23_6595_4c5b, + ]), + ], + [ + pallas::Base::from_raw([ + 0x77c5_b024_8483_71ae, + 0x6041_4abe_362d_01c9, + 0x10f1_cc6d_f8b4_bcd7, + 0x1f69_7cac_4d07_feb7, + ]), + pallas::Base::from_raw([ + 0x786a_dd24_4aa0_ef29, + 0x3145_c478_0631_09d6, + 0x26e6_c851_fbd5_72a6, + 0x267a_750f_e5d7_cfbc, + ]), + pallas::Base::from_raw([ + 0x180e_2b4d_3e75_6f65, + 0xaf28_5fa8_2ce4_fae5, + 0x678c_9996_d9a4_72c8, + 0x0c91_feab_4a43_193a, + ]), + ], + [ + pallas::Base::from_raw([ + 0x79c4_7c57_3ac4_10f7, + 0x7e3b_83af_4a4b_a3ba, + 0x2186_c303_8ea0_5e69, + 0x1745_569a_0a3e_3014, + ]), + pallas::Base::from_raw([ + 0x1e03_8852_2696_191f, + 0xfdff_66c6_f3b5_ffe1, + 0xeca5_1207_78a5_6711, + 0x2986_3d54_6e7e_7c0d, + ]), + pallas::Base::from_raw([ + 0x2f22_5e63_66bf_e390, + 0xa79a_03df_8339_94c6, + 0xbf06_bae4_9ef8_53f6, + 0x1148_d6ab_2bd0_0192, + ]), + ], + [ + pallas::Base::from_raw([ + 0xf4f6_331a_8b26_5d15, + 0xf745_f45d_350d_41d4, + 0xe18b_1499_060d_a366, + 0x02e0_e121_b0f3_dfef, + ]), + pallas::Base::from_raw([ + 0x078a_e6aa_1510_54b7, + 0x6904_0173_6d44_a653, + 0xb89e_f73a_40a2_b274, + 0x0d0a_a46e_76a6_a278, + ]), + pallas::Base::from_raw([ + 0x9a4d_532c_7b6e_0958, + 0x392d_de71_0f1f_06db, + 0xeee5_45f3_fa6d_3d08, + 0x1394_3675_b04a_a986, + ]), + ], + [ + pallas::Base::from_raw([ + 0x961f_c818_dcbb_66b5, + 0xc9f2_b325_7530_dafe, + 0xd97a_11d6_3088_f5d9, + 0x2901_ec61_942d_34aa, + ]), + pallas::Base::from_raw([ + 0xfdf5_44b9_63d1_fdc7, + 0x22ff_a2a2_af9f_a3e3, + 0xf431_d544_34a3_e0cf, + 0x2020_4a21_05d2_2e7e, + ]), + pallas::Base::from_raw([ + 0x1211_b9e2_190d_6852, + 0xa004_abe8_e015_28c4, + 0x5c1e_3e9e_27a5_71c3, + 0x3a8a_6282_9512_1d5c, + ]), + ], +]; + +pub(crate) const MDS: [[pallas::Base; 3]; 3] = [ + [ + pallas::Base::from_raw([ + 0x323f_2486_d7e1_1b63, + 0x97d7_a0ab_2385_0b56, + 0xb3d5_9fbd_c8c9_ead4, + 0x0ab5_e5b8_74a6_8de7, + ]), + pallas::Base::from_raw([ + 0x8eca_5596_e996_ab5e, + 0x240d_4a7c_bf73_5736, + 0x293f_0f0d_886c_7954, + 0x3191_6628_e58a_5abb, + ]), + pallas::Base::from_raw([ + 0x19d1_cf25_d8e8_345d, + 0xa0a3_b71a_5fb1_5735, + 0xd803_952b_bb36_4fdf, + 0x07c0_45d5_f5e9_e5a6, + ]), + ], + [ + pallas::Base::from_raw([ + 0xd049_cdc8_d085_167c, + 0x3a0a_4640_48bd_770a, + 0xf8e2_4f66_822c_2d9f, + 0x2331_6263_0ebf_9ed7, + ]), + pallas::Base::from_raw([ + 0x4022_7011_3e04_7a2e, + 0x78f8_365c_85bb_ab07, + 0xb366_6454_8d60_957d, + 0x25ca_e259_9892_a8b0, + ]), + pallas::Base::from_raw([ + 0xf84d_806f_685f_747a, + 0x9aad_3d82_62ef_d83f, + 0x7493_8717_989a_1957, + 0x22f5_b5e1_e608_1c97, + ]), + ], + [ + pallas::Base::from_raw([ + 0xfee7_a994_4f84_dbe4, + 0x2168_0eab_c56b_c15d, + 0xf333_aa91_c383_3464, + 0x2e29_dd59_c64b_1037, + ]), + pallas::Base::from_raw([ + 0xc771_effa_4326_3664, + 0xcbea_f48b_3a06_24c3, + 0x92d1_5e7d_ceef_1665, + 0x1d1a_ab4e_c1cd_6788, + ]), + pallas::Base::from_raw([ + 0x1563_9415_f6e8_5ef1, + 0x7587_2c39_b59a_31f6, + 0x51e0_cbea_d655_16b9, + 0x3bf7_6308_6a18_9364, + ]), + ], +]; diff --git a/core/embed/rust/src/zcash_primitives/poseidon/constants_gen.rs b/core/embed/rust/src/zcash_primitives/poseidon/constants_gen.rs new file mode 100644 index 000000000..b763e96bc --- /dev/null +++ b/core/embed/rust/src/zcash_primitives/poseidon/constants_gen.rs @@ -0,0 +1,113 @@ +//! Generates Poseidon round constants according to apendix F of article +//! +//! POSEIDON: A New Hash Function for Zero-Knowledge Proof Systems +//! +//! +//! for paraments defined in +//! + +use core::cmp::Ordering; +use pasta_curves::Fp; + +/// Size of Pallas base field in little-endian +const MODULUS: [u64; 4] = [ + 0x992d30ed00000001, + 0x224698fc094cf91b, + 0x0000000000000000, + 0x4000000000000000, +]; + +fn is_less_then_modulus(number: &[u64; 4]) -> bool { + for i in (0..4).rev() { + match number[i].cmp(&MODULUS[i]) { + Ordering::Less => return true, + Ordering::Greater => return false, + Ordering::Equal => continue, + }; + } + false // numbers are equal +} + +// the newest bit is the lowest bit of `reg0` +// the oldest bit is the highest bit of `reg2` +pub struct ConstantsGenerator { + reg0: u32, + reg1: u32, + reg2: u16, +} + +impl ConstantsGenerator { + pub fn new() -> Self { + // LSFR state is precomputed as follows: + // 1. initialize LSFR according to the step 1 + // with parameters from the Zcash protocol paper ยง5.4.1.10 + // 2. drop first 160 bits according to the step 3 + // 3. convert the LSFR state from Fibonacci representation to Galois repr. + ConstantsGenerator { + reg0: 0x7858ff5e, + reg1: 0xb8da546e, + reg2: 0xfbfe, + } + } + + /// Returns next LSFR bit + fn next_bit(&mut self) -> u8 { + // pop the oldest bit + let new_bit = (self.reg2 >> 15) as u8; + // shift register + self.reg2 = (self.reg2 << 1) | (self.reg1 >> 31) as u16; + self.reg1 = (self.reg1 << 1) | (self.reg0 >> 31); + self.reg0 <<= 1; + // add feedback + if new_bit == 1 { + // s_80 = s_62 + s_51 + s_38 + s_23 + s_13 + s_0 + self.reg0 ^= 0b00000000100000000010000000000001; + self.reg1 ^= 0b01000000000010000000000001000000; + } + new_bit + } + + /// Filter bits according to the step 4 + fn next_filtered_bit(&mut self) -> u8 { + loop { + let b1 = self.next_bit(); + let b2 = self.next_bit(); + if b1 == 1 { + break b2; + } + } + } + + fn next_digit(&mut self, n: usize) -> u64 { + (0..n).fold(0u64, |acc, _| (acc << 1) + self.next_filtered_bit() as u64) + } + + fn next_field_element(&mut self) -> Fp { + loop { + // sample the number in the big-endian + let mut digits = [ + self.next_digit(63), + self.next_digit(64), + self.next_digit(64), + self.next_digit(64), + ]; + // then convert it to the little-endian + digits.reverse(); + if is_less_then_modulus(&digits) { + break Fp::from_raw(digits); + } + } + } +} + +impl Iterator for ConstantsGenerator { + type Item = [Fp; 3]; + + fn next(&mut self) -> Option<[Fp; 3]> { + Some([ + self.next_field_element(), + self.next_field_element(), + self.next_field_element(), + ]) + } +} diff --git a/core/embed/rust/src/zcash_primitives/poseidon/mod.rs b/core/embed/rust/src/zcash_primitives/poseidon/mod.rs new file mode 100644 index 000000000..b76111141 --- /dev/null +++ b/core/embed/rust/src/zcash_primitives/poseidon/mod.rs @@ -0,0 +1,109 @@ +//! The Poseidon permutation lightweight implementation +//! Poseidon specification: https://eprint.iacr.org/2019/458 +//! Orchard instantiation: https://zips.z.cash/protocol/protocol.pdf#poseidonhash + +use core::{iter, ops::Deref}; + +use crate::micropython::{ + gc::Gc, + map::Map, + module::Module, + obj::Obj, + qstr::Qstr, + util, + wrap::{Wrappable, Wrapped}, +}; +use constants::MDS; +use pasta_curves::{arithmetic::FieldExt, group::ff::Field, Fp}; + +mod constants; +mod constants_gen; +#[cfg(test)] +mod tests; +use constants_gen::ConstantsGenerator; + +/// The type used to hold permutation state. +type State = [Fp; 3]; + +#[inline] +fn sbox(x: Fp) -> Fp { + x.pow_vartime(&[5]) +} + +#[inline] +fn full_round_sbox_layer(state: &mut State) { + state[0] = sbox(state[0]); + state[1] = sbox(state[1]); + state[2] = sbox(state[2]); +} + +#[inline] +fn half_round_sbox_layer(state: &mut State) { + state[0] = sbox(state[0]); +} + +#[inline] +fn add_round_constants(state: &mut State, rcs: &State) { + state[0] += rcs[0]; + state[1] += rcs[1]; + state[2] += rcs[2]; +} + +#[inline] +fn apply_mds(state: &mut State) { + let mut new_state = [Fp::zero(); 3]; + for i in 0..3 { + for j in 0..3 { + new_state[i] += MDS[i][j] * state[j]; + } + } + *state = new_state; +} + +/// Runs the Poseidon permutation on the given state. +pub(crate) fn permute(state: &mut State) { + let rounds = iter::empty() + .chain(iter::repeat(true).take(4)) + .chain(iter::repeat(false).take(56)) + .chain(iter::repeat(true).take(4)); + + let round_constants = ConstantsGenerator::new(); + + for (is_full_round, rcs) in rounds.zip(round_constants) { + add_round_constants(state, &rcs); + if is_full_round { + full_round_sbox_layer(state); + } else { + half_round_sbox_layer(state); + } + apply_mds(state); + } +} + +/// Poseidon hash +pub fn hash(x: Fp, y: Fp) -> Fp { + let mut state = [x, y, Fp::from_u128((2 as u128) << 64)]; + permute(&mut state); + state[0] +} + +#[no_mangle] +unsafe extern "C" fn poseidon_wrapper(nk: Obj, rho: Obj) -> Obj { + let block = || { + let nk: Gc> = nk.try_into()?; + let nk: Fp = nk.deref().inner().clone(); + let rho: Gc> = rho.try_into()?; + let rho: Fp = rho.deref().inner().clone(); + + hash(nk, rho).wrap() + }; + unsafe { util::try_or_raise(block) } +} + +#[no_mangle] +pub static mp_module_trezorposeidon: Module = obj_module! { + Qstr::MP_QSTR___name__ => Qstr::MP_QSTR_trezorposeidon.to_obj(), + /// def poseidon(x: Fp, y: Fp) -> Fp: + /// """Poseidon hash function.""" + Qstr::MP_QSTR_poseidon => obj_fn_2!(poseidon_wrapper).as_obj(), +}; diff --git a/core/embed/rust/src/zcash_primitives/poseidon/tests.rs b/core/embed/rust/src/zcash_primitives/poseidon/tests.rs new file mode 100644 index 000000000..418067bfc --- /dev/null +++ b/core/embed/rust/src/zcash_primitives/poseidon/tests.rs @@ -0,0 +1,662 @@ +//! Test vectors for `OrchardNullifier`. + +use super::*; +use pasta_curves::group::ff::PrimeField; + +#[test] +fn permute_test_vectors() { + for tv in PERMUTE_TEST_VECTORS.iter() { + let mut state = [ + Fp::from_repr(tv.initial_state[0]).unwrap(), + Fp::from_repr(tv.initial_state[1]).unwrap(), + Fp::from_repr(tv.initial_state[2]).unwrap(), + ]; + + permute(&mut state); + + for (expected, actual) in tv.final_state.iter().zip(state.iter()) { + assert_eq!(&actual.to_repr(), expected); + } + } +} + +#[test] +fn hash_test_vectors() { + for tv in HASH_TEST_VECTORS.iter() { + let x = Fp::from_repr(tv.input[0]).unwrap(); + let y = Fp::from_repr(tv.input[1]).unwrap(); + + let result = hash(x, y); + + assert_eq!(result.to_repr(), tv.output); + } +} + +#[test] +fn constants_generator() { + let gen = constants_gen::ConstantsGenerator::new(); + for (a, b) in constants::ROUND_CONSTANTS.into_iter().zip(gen) { + assert_eq!(a, b); + } +} + +pub(crate) struct PermuteTestVector { + pub(crate) initial_state: [[u8; 32]; 3], + pub(crate) final_state: [[u8; 32]; 3], +} + +pub(crate) struct HashTestVector { + pub(crate) input: [[u8; 32]; 2], + pub(crate) output: [u8; 32], +} + +pub(crate) static PERMUTE_TEST_VECTORS: [PermuteTestVector; 11] = [ + PermuteTestVector { + initial_state: [ + [ + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + ], + [ + 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + ], + [ + 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + ], + ], + final_state: [ + [ + 0x56, 0xa4, 0xec, 0x4a, 0x02, 0xbc, 0xb1, 0xae, 0xa0, 0x42, 0xb6, 0xd0, 0x71, 0x9a, + 0xe6, 0xf7, 0x0f, 0x24, 0x66, 0xf9, 0x64, 0xb3, 0xef, 0x94, 0x53, 0xb4, 0x64, 0x0b, + 0xcd, 0x6a, 0x52, 0x2a, + ], + [ + 0x2a, 0xb8, 0xe5, 0x28, 0x96, 0x3e, 0x2a, 0x01, 0xfe, 0xda, 0xd9, 0xbe, 0x7f, 0x2e, + 0xd4, 0xdc, 0x12, 0x55, 0x3d, 0x34, 0xae, 0x7d, 0xff, 0x76, 0x30, 0xa4, 0x4a, 0x8b, + 0x56, 0xd1, 0xc5, 0x13, + ], + [ + 0xdd, 0x9d, 0x4e, 0xd3, 0xa1, 0x29, 0x90, 0x35, 0x7b, 0x2c, 0xa4, 0xbd, 0xe1, 0xdf, + 0xcf, 0xf7, 0x1a, 0x56, 0x84, 0x79, 0x59, 0xcd, 0x6f, 0x25, 0x44, 0x65, 0x97, 0xc6, + 0x68, 0xc8, 0x49, 0x0a, + ], + ], + }, + PermuteTestVector { + initial_state: [ + [ + 0x5c, 0x7a, 0x8f, 0x73, 0xad, 0xfc, 0x70, 0xfb, 0x3f, 0x13, 0x94, 0x49, 0xac, 0x6b, + 0x57, 0x07, 0x4c, 0x4d, 0x6e, 0x66, 0xb1, 0x64, 0x93, 0x9d, 0xaf, 0xfa, 0x2e, 0xf6, + 0xee, 0x69, 0x21, 0x08, + ], + [ + 0x1a, 0xdd, 0x86, 0xb3, 0xf2, 0xe1, 0xbd, 0xa6, 0x2a, 0x5d, 0x2e, 0x0e, 0x98, 0x2b, + 0x77, 0xe6, 0xb0, 0xef, 0x9c, 0xa3, 0xf2, 0x49, 0x88, 0xc7, 0xb3, 0x53, 0x42, 0x01, + 0xcf, 0xb1, 0xcd, 0x0d, + ], + [ + 0xbd, 0x69, 0xb8, 0x25, 0x32, 0xb6, 0x94, 0x0f, 0xf2, 0x59, 0x0f, 0x67, 0x9b, 0xa9, + 0xc7, 0x27, 0x1f, 0xe0, 0x1f, 0x7e, 0x9c, 0x8e, 0x36, 0xd6, 0xa5, 0xe2, 0x9d, 0x4e, + 0x30, 0xa7, 0x35, 0x14, + ], + ], + final_state: [ + [ + 0xd0, 0x6e, 0x2f, 0x83, 0x38, 0x92, 0x8a, 0x7e, 0xe7, 0x38, 0x0c, 0x77, 0x92, 0x80, + 0x87, 0xcd, 0xa2, 0xfd, 0x29, 0x61, 0xa1, 0x52, 0x69, 0x03, 0x7a, 0x22, 0xd6, 0xd1, + 0x20, 0xae, 0xdd, 0x21, + ], + [ + 0x29, 0x55, 0xa4, 0x5f, 0x41, 0x6f, 0x10, 0xd6, 0xbc, 0x79, 0xac, 0x94, 0xd0, 0xc0, + 0x69, 0xc9, 0x49, 0xe5, 0xf4, 0xbd, 0x09, 0x48, 0x1e, 0x1f, 0x36, 0x8c, 0xb9, 0xb8, + 0xee, 0x51, 0x14, 0x0d, + ], + [ + 0x0d, 0x83, 0x76, 0xbb, 0xe9, 0xd6, 0x5d, 0x2b, 0x1e, 0x13, 0x6f, 0xb7, 0xd9, 0x82, + 0xab, 0x87, 0xc5, 0x1c, 0x40, 0x30, 0x44, 0xbe, 0x5c, 0x79, 0x9d, 0x56, 0xbb, 0x68, + 0xac, 0xf9, 0x5b, 0x10, + ], + ], + }, + PermuteTestVector { + initial_state: [ + [ + 0xbc, 0x50, 0x98, 0x42, 0x55, 0xd6, 0xaf, 0xbe, 0x9e, 0xf9, 0x28, 0x48, 0xed, 0x5a, + 0xc0, 0x08, 0x62, 0xc2, 0xfa, 0x7b, 0x2f, 0xec, 0xbc, 0xb6, 0x4b, 0x69, 0x68, 0x91, + 0x2a, 0x63, 0x81, 0x0e, + ], + [ + 0x3d, 0xc1, 0x66, 0xd5, 0x6a, 0x1d, 0x62, 0xf5, 0xa8, 0xd7, 0x55, 0x1d, 0xb5, 0xfd, + 0x93, 0x13, 0xe8, 0xc7, 0x20, 0x3d, 0x99, 0x6a, 0xf7, 0xd4, 0x77, 0x08, 0x37, 0x56, + 0xd5, 0x9a, 0xf8, 0x0d, + ], + [ + 0x05, 0xa7, 0x45, 0xf4, 0x5d, 0x7f, 0xf6, 0xdb, 0x10, 0xbc, 0x67, 0xfd, 0xf0, 0xf0, + 0x3e, 0xbf, 0x81, 0x30, 0xab, 0x33, 0x36, 0x26, 0x97, 0xb0, 0xe4, 0xe4, 0xc7, 0x63, + 0xcc, 0xb8, 0xf6, 0x36, + ], + ], + final_state: [ + [ + 0x0b, 0x77, 0xec, 0x53, 0x07, 0x14, 0x5a, 0x0c, 0x05, 0x2d, 0xc7, 0xa9, 0xd6, 0xf9, + 0x6a, 0xc3, 0x41, 0xae, 0x72, 0x64, 0x08, 0x32, 0xd5, 0x8e, 0x51, 0xeb, 0x92, 0xa4, + 0x17, 0x80, 0x17, 0x12, + ], + [ + 0x3b, 0x52, 0x3f, 0x44, 0xf0, 0x0e, 0x46, 0x3f, 0x8b, 0x0f, 0xd7, 0xd4, 0xfc, 0x0e, + 0x28, 0x0c, 0xdb, 0xde, 0xb9, 0x27, 0xf1, 0x81, 0x68, 0x07, 0x7b, 0xb3, 0x62, 0xf2, + 0x67, 0x5a, 0x2e, 0x18, + ], + [ + 0x95, 0x7a, 0x97, 0x06, 0xff, 0xcc, 0x35, 0x15, 0x64, 0xae, 0x80, 0x2a, 0x99, 0x11, + 0x31, 0x4c, 0x05, 0xe2, 0x3e, 0x22, 0xaf, 0xcf, 0x83, 0x40, 0x59, 0xdf, 0x80, 0xfa, + 0xc1, 0x05, 0x76, 0x26, + ], + ], + }, + PermuteTestVector { + initial_state: [ + [ + 0x49, 0x5c, 0x22, 0x2f, 0x7f, 0xba, 0x1e, 0x31, 0xde, 0xfa, 0x3d, 0x5a, 0x57, 0xef, + 0xc2, 0xe1, 0xe9, 0xb0, 0x1a, 0x03, 0x55, 0x87, 0xd5, 0xfb, 0x1a, 0x38, 0xe0, 0x1d, + 0x94, 0x90, 0x3d, 0x3c, + ], + [ + 0x3d, 0x0a, 0xd3, 0x36, 0x1f, 0xec, 0x09, 0x77, 0x90, 0xd9, 0xbe, 0x0e, 0x42, 0x98, + 0x8d, 0x7d, 0x25, 0xc9, 0xa1, 0x38, 0xf4, 0x9b, 0x1a, 0x53, 0x7e, 0xdc, 0xf0, 0x4b, + 0xe3, 0x4a, 0x98, 0x11, + ], + [ + 0xa4, 0xaf, 0x9d, 0xb6, 0xd2, 0x7b, 0x50, 0x72, 0x83, 0x5f, 0x0c, 0x3e, 0x88, 0x39, + 0x5e, 0xd7, 0xa4, 0x1b, 0x00, 0x52, 0xad, 0x80, 0x84, 0xa8, 0xb9, 0xda, 0x94, 0x8d, + 0x32, 0x0d, 0xad, 0x16, + ], + ], + final_state: [ + [ + 0x67, 0x80, 0x08, 0x3f, 0x7f, 0x82, 0xcb, 0x42, 0x54, 0xe7, 0xb6, 0x6f, 0x4b, 0x83, + 0x84, 0x6a, 0xc9, 0x77, 0x3f, 0xb9, 0xc3, 0x9c, 0x6e, 0xc9, 0x81, 0x8b, 0x06, 0x22, + 0x23, 0x09, 0x55, 0x2a, + ], + [ + 0xa5, 0xf9, 0xa5, 0x7e, 0x2c, 0x40, 0xb1, 0x58, 0xd8, 0x16, 0x53, 0x43, 0xe6, 0x02, + 0x65, 0x2c, 0x3e, 0xfc, 0x0b, 0x64, 0xdd, 0xca, 0xee, 0xe5, 0xce, 0x3d, 0x95, 0x1f, + 0xd5, 0x9f, 0x50, 0x08, + ], + [ + 0xdc, 0xa4, 0x64, 0x36, 0x12, 0x7c, 0x47, 0x7e, 0x83, 0x95, 0x0f, 0xa0, 0x7c, 0xc6, + 0x8a, 0x56, 0x6e, 0x54, 0x18, 0x55, 0xad, 0xc2, 0x68, 0x52, 0x97, 0x87, 0x35, 0x24, + 0x88, 0x92, 0x1e, 0x3b, + ], + ], + }, + PermuteTestVector { + initial_state: [ + [ + 0x4d, 0x54, 0x31, 0xe6, 0x43, 0x7d, 0x0b, 0x5b, 0xed, 0xbb, 0xcd, 0xaf, 0x34, 0x5b, + 0x86, 0xc4, 0x12, 0x1f, 0xc0, 0x0f, 0xe7, 0xf2, 0x35, 0x73, 0x42, 0x76, 0xd3, 0x8d, + 0x47, 0xf1, 0xe1, 0x11, + ], + [ + 0xdd, 0x0c, 0x7a, 0x1d, 0x81, 0x1c, 0x7d, 0x9c, 0xd4, 0x6d, 0x37, 0x7b, 0x3f, 0xde, + 0xab, 0x3f, 0xb6, 0x79, 0xf3, 0xdc, 0x60, 0x1d, 0x00, 0x82, 0x85, 0xed, 0xcb, 0xda, + 0xe6, 0x9c, 0xe8, 0x3c, + ], + [ + 0x19, 0xe4, 0xaa, 0xc0, 0x35, 0x90, 0x17, 0xec, 0x85, 0xa1, 0x83, 0xd2, 0x20, 0x53, + 0xdb, 0x33, 0xf7, 0x34, 0x76, 0xf2, 0x1a, 0x48, 0x2e, 0xc9, 0x37, 0x83, 0x65, 0xc8, + 0xf7, 0x39, 0x3c, 0x14, + ], + ], + final_state: [ + [ + 0x89, 0x99, 0x8e, 0x5e, 0x0f, 0xa1, 0x95, 0x2a, 0x40, 0xb8, 0xb5, 0x2b, 0x62, 0xd9, + 0x45, 0x70, 0xa4, 0x9a, 0x7d, 0x91, 0xdd, 0x22, 0x6d, 0x69, 0x2b, 0xc9, 0xb1, 0xa6, + 0x13, 0xc9, 0x08, 0x30, + ], + [ + 0xd0, 0xee, 0x44, 0xd9, 0xa9, 0x0d, 0x90, 0x79, 0xef, 0xfb, 0x24, 0x86, 0xd3, 0xd8, + 0x4d, 0x1a, 0x18, 0x4e, 0xdf, 0x14, 0x97, 0x0b, 0xac, 0x36, 0xc7, 0x48, 0x04, 0xc7, + 0xff, 0xbe, 0xe5, 0x0b, + ], + [ + 0x04, 0x81, 0x45, 0xa6, 0x61, 0xce, 0x78, 0x7c, 0x7e, 0x12, 0x2a, 0xc6, 0x44, 0x7e, + 0x9b, 0xa3, 0x93, 0xd3, 0x67, 0xac, 0x05, 0x4f, 0xaa, 0xc5, 0xb7, 0xb5, 0xf7, 0x19, + 0x2b, 0x2f, 0xde, 0x21, + ], + ], + }, + PermuteTestVector { + initial_state: [ + [ + 0xe2, 0x88, 0x53, 0x15, 0xeb, 0x46, 0x71, 0x09, 0x8b, 0x79, 0x53, 0x5e, 0x79, 0x0f, + 0xe5, 0x3e, 0x29, 0xfe, 0xf2, 0xb3, 0x76, 0x66, 0x97, 0xac, 0x32, 0xb4, 0xf4, 0x73, + 0xf4, 0x68, 0xa0, 0x08, + ], + [ + 0xe6, 0x23, 0x89, 0xfc, 0x16, 0x57, 0xe0, 0xde, 0xf0, 0xb6, 0x32, 0xc6, 0xae, 0x25, + 0xf9, 0xf7, 0x83, 0xb2, 0x7d, 0xb5, 0x9a, 0x4a, 0x15, 0x3d, 0x88, 0x2d, 0x2b, 0x21, + 0x03, 0x59, 0x65, 0x15, + ], + [ + 0xeb, 0x94, 0x94, 0xc6, 0xd2, 0x27, 0xe2, 0x16, 0x3b, 0x46, 0x99, 0xd9, 0x91, 0xf4, + 0x33, 0xbf, 0x94, 0x86, 0xa7, 0xaf, 0xcf, 0x4a, 0x0d, 0x9c, 0x73, 0x1e, 0x98, 0x5d, + 0x99, 0x58, 0x9c, 0x0b, + ], + ], + final_state: [ + [ + 0xce, 0x2d, 0x1f, 0x8d, 0x67, 0x7f, 0xfb, 0xfd, 0x73, 0xb2, 0x35, 0xe8, 0xc6, 0x87, + 0xfb, 0x42, 0x18, 0x7f, 0x78, 0x81, 0xc3, 0xce, 0x9c, 0x79, 0x4f, 0x2b, 0xd4, 0x61, + 0x40, 0xf7, 0xcc, 0x2a, + ], + [ + 0xaf, 0x82, 0x92, 0x39, 0xb6, 0xd5, 0x5d, 0x5f, 0x43, 0xec, 0x6f, 0x32, 0xb8, 0x4a, + 0x2a, 0x01, 0x1e, 0x64, 0xc5, 0x74, 0x73, 0x9f, 0x87, 0xcb, 0x47, 0xdc, 0x70, 0x23, + 0x83, 0xfa, 0x5a, 0x34, + ], + [ + 0x03, 0xd1, 0x08, 0x5b, 0x21, 0x4c, 0x69, 0xb8, 0xbf, 0xe8, 0x91, 0x02, 0xbd, 0x61, + 0x7e, 0xce, 0x0c, 0x54, 0x00, 0x17, 0x96, 0x40, 0x41, 0x05, 0xc5, 0x33, 0x30, 0xd2, + 0x49, 0x58, 0x1d, 0x0f, + ], + ], + }, + PermuteTestVector { + initial_state: [ + [ + 0xb7, 0x38, 0xe8, 0xaa, 0x0a, 0x15, 0x26, 0xa5, 0xbd, 0xef, 0x61, 0x31, 0x20, 0x37, + 0x2e, 0x83, 0x1a, 0x20, 0xda, 0x8a, 0xba, 0x18, 0xd1, 0xdb, 0xeb, 0xbc, 0x86, 0x2d, + 0xed, 0x42, 0x43, 0x1e, + ], + [ + 0x91, 0x47, 0x69, 0x30, 0xe3, 0x38, 0x5c, 0xd3, 0xe3, 0x37, 0x9e, 0x38, 0x53, 0xd9, + 0x34, 0x67, 0xe0, 0x01, 0xaf, 0xa2, 0xfb, 0x8d, 0xc3, 0x43, 0x6d, 0x75, 0xa4, 0xa6, + 0xf2, 0x65, 0x72, 0x10, + ], + [ + 0x4b, 0x19, 0x22, 0x32, 0xec, 0xb9, 0xf0, 0xc0, 0x24, 0x11, 0xe5, 0x25, 0x96, 0xbc, + 0x5e, 0x90, 0x45, 0x7e, 0x74, 0x59, 0x39, 0xff, 0xed, 0xbd, 0x12, 0x86, 0x3c, 0xe7, + 0x1a, 0x02, 0xaf, 0x11, + ], + ], + final_state: [ + [ + 0x5f, 0xcc, 0xd8, 0x7d, 0x2f, 0x66, 0x7b, 0x9e, 0xe3, 0x88, 0xf3, 0x4c, 0x1c, 0x71, + 0x06, 0x87, 0x12, 0x7b, 0xff, 0x5b, 0x02, 0x21, 0xfd, 0x8a, 0x52, 0x94, 0x88, 0x66, + 0x91, 0x57, 0x94, 0x2b, + ], + [ + 0x89, 0x62, 0xb5, 0x80, 0x30, 0xaa, 0x63, 0x52, 0xd9, 0x90, 0xf3, 0xb9, 0x00, 0x1c, + 0xcb, 0xe8, 0x8a, 0x56, 0x27, 0x58, 0x1b, 0xbf, 0xb9, 0x01, 0xac, 0x4a, 0x6a, 0xed, + 0xfa, 0xe5, 0xc6, 0x34, + ], + [ + 0x7c, 0x0b, 0x76, 0x59, 0xf2, 0x4c, 0x98, 0xaf, 0x31, 0x0e, 0x3e, 0x8d, 0x82, 0xb5, + 0xf3, 0x99, 0x43, 0x3c, 0xdd, 0xa5, 0x8f, 0x48, 0xd9, 0xef, 0x8d, 0xd0, 0xca, 0x86, + 0x42, 0x72, 0xda, 0x3f, + ], + ], + }, + PermuteTestVector { + initial_state: [ + [ + 0x7b, 0x41, 0x7a, 0xdb, 0x63, 0xb3, 0x71, 0x22, 0xa5, 0xbf, 0x62, 0xd2, 0x6f, 0x1e, + 0x7f, 0x26, 0x8f, 0xb8, 0x6b, 0x12, 0xb5, 0x6d, 0xa9, 0xc3, 0x82, 0x85, 0x7d, 0xee, + 0xcc, 0x40, 0xa9, 0x0d, + ], + [ + 0x5e, 0x29, 0x35, 0x39, 0x71, 0xb3, 0x49, 0x94, 0xb6, 0x21, 0xb0, 0xb2, 0x61, 0xae, + 0xb3, 0x78, 0x6d, 0xd9, 0x84, 0xd5, 0x67, 0xdb, 0x28, 0x57, 0xb9, 0x27, 0xb7, 0xfa, + 0xe2, 0xdb, 0x58, 0x31, + ], + [ + 0x05, 0x41, 0x5d, 0x46, 0x42, 0x78, 0x9d, 0x38, 0xf5, 0x0b, 0x8d, 0xbc, 0xc1, 0x29, + 0xca, 0xb3, 0xd1, 0x7d, 0x19, 0xf3, 0x35, 0x5b, 0xcf, 0x73, 0xce, 0xcb, 0x8c, 0xb8, + 0xa5, 0xda, 0x01, 0x30, + ], + ], + final_state: [ + [ + 0x9e, 0xe1, 0xad, 0xdc, 0x6f, 0x64, 0xda, 0xb6, 0xac, 0xdc, 0xea, 0xec, 0xc1, 0xfb, + 0xbc, 0x8a, 0x32, 0x45, 0x8e, 0x49, 0xc1, 0x9e, 0x79, 0x85, 0x56, 0xc6, 0x4b, 0x59, + 0x8b, 0xa6, 0xff, 0x14, + ], + [ + 0x42, 0xcc, 0x10, 0x36, 0x4f, 0xd6, 0x59, 0xc3, 0xcc, 0x77, 0x25, 0x84, 0xdb, 0x91, + 0xc4, 0x9a, 0x38, 0x67, 0x2b, 0x69, 0x24, 0x93, 0xb9, 0x07, 0x5f, 0x16, 0x53, 0xca, + 0x1f, 0xae, 0x1c, 0x33, + ], + [ + 0xff, 0x41, 0xf3, 0x51, 0x80, 0x14, 0x56, 0xc4, 0x96, 0x0b, 0x39, 0x3a, 0xff, 0xa8, + 0x62, 0x13, 0xa7, 0xea, 0xc0, 0x6c, 0x66, 0x21, 0x3b, 0x45, 0xc3, 0xb5, 0x0e, 0xc6, + 0x48, 0xd6, 0x7d, 0x0d, + ], + ], + }, + PermuteTestVector { + initial_state: [ + [ + 0x71, 0x52, 0xf1, 0x39, 0x36, 0xa2, 0x70, 0x57, 0x26, 0x70, 0xdc, 0x82, 0xd3, 0x90, + 0x26, 0xc6, 0xcb, 0x4c, 0xd4, 0xb0, 0xf7, 0xf5, 0xaa, 0x2a, 0x4f, 0x5a, 0x53, 0x41, + 0xec, 0x5d, 0xd7, 0x15, + ], + [ + 0x40, 0x6f, 0x2f, 0xdd, 0x2a, 0xfa, 0x73, 0x3f, 0x5f, 0x64, 0x1c, 0x8c, 0x21, 0x86, + 0x2a, 0x1b, 0xaf, 0xce, 0x26, 0x09, 0xd9, 0xee, 0xcf, 0xa1, 0x58, 0xcf, 0xb5, 0xcd, + 0x79, 0xf8, 0x80, 0x08, + ], + [ + 0xe2, 0x15, 0xdc, 0x7d, 0x96, 0x57, 0xba, 0xd3, 0xfb, 0x88, 0xb0, 0x1e, 0x99, 0x38, + 0x44, 0x54, 0x36, 0x24, 0xc2, 0x5f, 0xa9, 0x59, 0xcc, 0x97, 0x48, 0x9c, 0xe7, 0x57, + 0x45, 0x82, 0x4b, 0x37, + ], + ], + final_state: [ + [ + 0x63, 0x09, 0x15, 0xd7, 0xd8, 0x25, 0xeb, 0x74, 0x37, 0xb0, 0xe4, 0x6e, 0x37, 0x28, + 0x6a, 0x88, 0xb3, 0x89, 0xdc, 0x69, 0x85, 0x93, 0x07, 0x11, 0x6d, 0x34, 0x7b, 0x98, + 0xca, 0x14, 0x5c, 0x31, + ], + [ + 0xaa, 0x58, 0x1b, 0xae, 0xe9, 0x4f, 0xb5, 0x46, 0xa7, 0x61, 0xf1, 0x7a, 0x5d, 0x6e, + 0xaa, 0x70, 0x29, 0x52, 0x78, 0x42, 0xf3, 0x1c, 0x39, 0x87, 0xb8, 0x68, 0xed, 0x7d, + 0xaf, 0xfd, 0xb5, 0x34, + ], + [ + 0x7d, 0xc1, 0x17, 0xb3, 0x39, 0x1a, 0xab, 0x85, 0xde, 0x9f, 0x42, 0x4d, 0xb6, 0x65, + 0x1e, 0x00, 0x45, 0xab, 0x79, 0x98, 0xf2, 0x8e, 0x54, 0x10, 0x15, 0x35, 0x90, 0x61, + 0x99, 0xce, 0x1f, 0x1a, + ], + ], + }, + PermuteTestVector { + initial_state: [ + [ + 0x86, 0x8c, 0x53, 0x23, 0x9c, 0xfb, 0xdf, 0x73, 0xca, 0xec, 0x65, 0x60, 0x40, 0x37, + 0x31, 0x4f, 0xaa, 0xce, 0xb5, 0x62, 0x18, 0xc6, 0xbd, 0x30, 0xf8, 0x37, 0x4a, 0xc1, + 0x33, 0x86, 0x79, 0x3f, + ], + [ + 0x21, 0xa9, 0xfb, 0x80, 0xad, 0x03, 0xbc, 0x0c, 0xda, 0x4a, 0x44, 0x94, 0x6c, 0x00, + 0xe1, 0xb1, 0xa1, 0xdf, 0x0e, 0x5b, 0x87, 0xb5, 0xbe, 0xce, 0x47, 0x7a, 0x70, 0x96, + 0x49, 0xe9, 0x50, 0x06, + ], + [ + 0x04, 0x91, 0x39, 0x48, 0x25, 0x64, 0xf1, 0x85, 0xc7, 0x90, 0x0e, 0x83, 0xc7, 0x38, + 0x07, 0x0a, 0xf6, 0x55, 0x6d, 0xf6, 0xed, 0x4b, 0x4d, 0xdd, 0x3d, 0x9a, 0x69, 0xf5, + 0x33, 0x57, 0xd7, 0x36, + ], + ], + final_state: [ + [ + 0x6a, 0x5a, 0x19, 0x19, 0xa4, 0x49, 0xa5, 0xe0, 0x29, 0x71, 0x1f, 0x48, 0x8a, 0xdb, + 0xd6, 0xb0, 0x3e, 0x5c, 0x92, 0x7b, 0x6f, 0x9d, 0x9d, 0x35, 0xc5, 0xb3, 0xcc, 0xeb, + 0x76, 0x60, 0x52, 0x03, + ], + [ + 0x80, 0x47, 0x5b, 0x46, 0x89, 0x59, 0x61, 0x47, 0xab, 0x2a, 0xdf, 0x01, 0x73, 0xdb, + 0x28, 0x9b, 0x3a, 0x26, 0xa1, 0x04, 0x84, 0x21, 0x73, 0xe8, 0x8b, 0xdb, 0xfe, 0xc0, + 0x4a, 0x28, 0x67, 0x1b, + ], + [ + 0x1e, 0xf3, 0xc8, 0xd0, 0xf5, 0x44, 0x44, 0xf5, 0x55, 0xb1, 0x5f, 0x7b, 0xc9, 0xfa, + 0x4f, 0xfa, 0x0f, 0x56, 0x7c, 0x0f, 0x19, 0xac, 0x7d, 0x0f, 0xf9, 0x44, 0xfd, 0x36, + 0x42, 0x6e, 0x32, 0x3a, + ], + ], + }, + PermuteTestVector { + initial_state: [ + [ + 0x7d, 0x4f, 0x5c, 0xcb, 0x01, 0x64, 0x3c, 0x31, 0xdb, 0x84, 0x5e, 0xec, 0xd5, 0xd6, + 0x3d, 0xc1, 0x6a, 0x95, 0xe3, 0x02, 0x5b, 0x97, 0x92, 0xff, 0xf7, 0xf2, 0x44, 0xfc, + 0x71, 0x62, 0x69, 0x39, + ], + [ + 0x26, 0xd6, 0x2e, 0x95, 0x96, 0xfa, 0x82, 0x5c, 0x6b, 0xf2, 0x1a, 0xff, 0x9e, 0x68, + 0x62, 0x5a, 0x19, 0x24, 0x40, 0xea, 0x06, 0x82, 0x81, 0x23, 0xd9, 0x78, 0x84, 0x80, + 0x6f, 0x15, 0xfa, 0x08, + ], + [ + 0xd9, 0x52, 0x75, 0x4a, 0x23, 0x64, 0xb6, 0x66, 0xff, 0xc3, 0x0f, 0xdb, 0x01, 0x47, + 0x86, 0xda, 0x3a, 0x61, 0x28, 0xae, 0xf7, 0x84, 0xa6, 0x46, 0x10, 0xa8, 0x9d, 0x1a, + 0x70, 0x99, 0x21, 0x2d, + ], + ], + final_state: [ + [ + 0x1b, 0x4a, 0xc9, 0xbe, 0xf5, 0x6b, 0xdb, 0x6f, 0xb4, 0x2d, 0x3e, 0x3c, 0xd3, 0xa2, + 0xac, 0x70, 0xa4, 0xc4, 0x0c, 0x42, 0x5b, 0x0b, 0xd6, 0x67, 0x9c, 0xa5, 0x7b, 0x30, + 0x7e, 0xf1, 0xd4, 0x2f, + ], + [ + 0x1a, 0x2e, 0xf4, 0x11, 0x94, 0xaa, 0xa2, 0x34, 0x32, 0xe0, 0x86, 0xed, 0x8a, 0xdb, + 0xd1, 0xde, 0xec, 0x3c, 0x7c, 0xb3, 0x96, 0xde, 0x35, 0xba, 0xe9, 0x5a, 0xaf, 0x5a, + 0x08, 0xa0, 0xec, 0x36, + ], + [ + 0x68, 0xeb, 0x80, 0xc7, 0x3e, 0x2c, 0xcb, 0xde, 0xe1, 0xba, 0x71, 0x24, 0x77, 0x61, + 0xd5, 0xb5, 0xec, 0xc6, 0x20, 0xe6, 0xe4, 0x8e, 0x00, 0x3b, 0x02, 0x3d, 0x9f, 0x55, + 0x61, 0x66, 0x2f, 0x20, + ], + ], + }, +]; + +pub(crate) static HASH_TEST_VECTORS: [HashTestVector; 11] = [ + HashTestVector { + input: [ + [ + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + ], + [ + 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + ], + ], + output: [ + 0x83, 0x58, 0xd7, 0x11, 0xa0, 0x32, 0x9d, 0x38, 0xbe, 0xcd, 0x54, 0xfb, 0xa7, 0xc2, + 0x83, 0xed, 0x3e, 0x08, 0x9a, 0x39, 0xc9, 0x1b, 0x6a, 0x9d, 0x10, 0xef, 0xb0, 0x2b, + 0xc3, 0xf1, 0x2f, 0x06, + ], + }, + HashTestVector { + input: [ + [ + 0x5c, 0x7a, 0x8f, 0x73, 0xad, 0xfc, 0x70, 0xfb, 0x3f, 0x13, 0x94, 0x49, 0xac, 0x6b, + 0x57, 0x07, 0x4c, 0x4d, 0x6e, 0x66, 0xb1, 0x64, 0x93, 0x9d, 0xaf, 0xfa, 0x2e, 0xf6, + 0xee, 0x69, 0x21, 0x08, + ], + [ + 0x1a, 0xdd, 0x86, 0xb3, 0xf2, 0xe1, 0xbd, 0xa6, 0x2a, 0x5d, 0x2e, 0x0e, 0x98, 0x2b, + 0x77, 0xe6, 0xb0, 0xef, 0x9c, 0xa3, 0xf2, 0x49, 0x88, 0xc7, 0xb3, 0x53, 0x42, 0x01, + 0xcf, 0xb1, 0xcd, 0x0d, + ], + ], + output: [ + 0xdb, 0x26, 0x75, 0xff, 0x3e, 0xf8, 0xfe, 0x30, 0xc4, 0xd5, 0xde, 0x61, 0xca, 0xc0, + 0x2a, 0x8e, 0xf1, 0xa0, 0x85, 0x23, 0xbe, 0x92, 0x39, 0x4b, 0x79, 0xd2, 0x67, 0x26, + 0x30, 0x3b, 0xe6, 0x03, + ], + }, + HashTestVector { + input: [ + [ + 0xbd, 0x69, 0xb8, 0x25, 0x32, 0xb6, 0x94, 0x0f, 0xf2, 0x59, 0x0f, 0x67, 0x9b, 0xa9, + 0xc7, 0x27, 0x1f, 0xe0, 0x1f, 0x7e, 0x9c, 0x8e, 0x36, 0xd6, 0xa5, 0xe2, 0x9d, 0x4e, + 0x30, 0xa7, 0x35, 0x14, + ], + [ + 0xbc, 0x50, 0x98, 0x42, 0x55, 0xd6, 0xaf, 0xbe, 0x9e, 0xf9, 0x28, 0x48, 0xed, 0x5a, + 0xc0, 0x08, 0x62, 0xc2, 0xfa, 0x7b, 0x2f, 0xec, 0xbc, 0xb6, 0x4b, 0x69, 0x68, 0x91, + 0x2a, 0x63, 0x81, 0x0e, + ], + ], + output: [ + 0xf5, 0x12, 0x1d, 0x1e, 0x1d, 0x5c, 0xfe, 0x8d, 0xa8, 0x96, 0xac, 0x0f, 0x9c, 0x18, + 0x3d, 0x76, 0x00, 0x31, 0xf6, 0xef, 0x8c, 0x7a, 0x41, 0xe6, 0x5e, 0xb0, 0x07, 0xcd, + 0xdc, 0x1d, 0x14, 0x3d, + ], + }, + HashTestVector { + input: [ + [ + 0x3d, 0xc1, 0x66, 0xd5, 0x6a, 0x1d, 0x62, 0xf5, 0xa8, 0xd7, 0x55, 0x1d, 0xb5, 0xfd, + 0x93, 0x13, 0xe8, 0xc7, 0x20, 0x3d, 0x99, 0x6a, 0xf7, 0xd4, 0x77, 0x08, 0x37, 0x56, + 0xd5, 0x9a, 0xf8, 0x0d, + ], + [ + 0x05, 0xa7, 0x45, 0xf4, 0x5d, 0x7f, 0xf6, 0xdb, 0x10, 0xbc, 0x67, 0xfd, 0xf0, 0xf0, + 0x3e, 0xbf, 0x81, 0x30, 0xab, 0x33, 0x36, 0x26, 0x97, 0xb0, 0xe4, 0xe4, 0xc7, 0x63, + 0xcc, 0xb8, 0xf6, 0x36, + ], + ], + output: [ + 0xa4, 0x16, 0xa5, 0xe7, 0x13, 0x51, 0x36, 0xa0, 0x50, 0x56, 0x90, 0x00, 0x58, 0xfa, + 0x50, 0xbf, 0x18, 0x6a, 0xd7, 0x33, 0x90, 0xac, 0xe6, 0x32, 0x3d, 0x8d, 0x81, 0xaa, + 0x8a, 0xdb, 0xd4, 0x11, + ], + }, + HashTestVector { + input: [ + [ + 0x49, 0x5c, 0x22, 0x2f, 0x7f, 0xba, 0x1e, 0x31, 0xde, 0xfa, 0x3d, 0x5a, 0x57, 0xef, + 0xc2, 0xe1, 0xe9, 0xb0, 0x1a, 0x03, 0x55, 0x87, 0xd5, 0xfb, 0x1a, 0x38, 0xe0, 0x1d, + 0x94, 0x90, 0x3d, 0x3c, + ], + [ + 0x3d, 0x0a, 0xd3, 0x36, 0x1f, 0xec, 0x09, 0x77, 0x90, 0xd9, 0xbe, 0x0e, 0x42, 0x98, + 0x8d, 0x7d, 0x25, 0xc9, 0xa1, 0x38, 0xf4, 0x9b, 0x1a, 0x53, 0x7e, 0xdc, 0xf0, 0x4b, + 0xe3, 0x4a, 0x98, 0x11, + ], + ], + output: [ + 0x1a, 0xba, 0xf3, 0x06, 0xfe, 0xd0, 0x5f, 0xa8, 0x92, 0x84, 0x8c, 0x49, 0xf6, 0xba, + 0x10, 0x41, 0x63, 0x43, 0x3f, 0x3f, 0x63, 0x31, 0x08, 0xa1, 0x3b, 0xc1, 0x5b, 0x2a, + 0x1d, 0x55, 0xd4, 0x0c, + ], + }, + HashTestVector { + input: [ + [ + 0xa4, 0xaf, 0x9d, 0xb6, 0xd2, 0x7b, 0x50, 0x72, 0x83, 0x5f, 0x0c, 0x3e, 0x88, 0x39, + 0x5e, 0xd7, 0xa4, 0x1b, 0x00, 0x52, 0xad, 0x80, 0x84, 0xa8, 0xb9, 0xda, 0x94, 0x8d, + 0x32, 0x0d, 0xad, 0x16, + ], + [ + 0x4d, 0x54, 0x31, 0xe6, 0x43, 0x7d, 0x0b, 0x5b, 0xed, 0xbb, 0xcd, 0xaf, 0x34, 0x5b, + 0x86, 0xc4, 0x12, 0x1f, 0xc0, 0x0f, 0xe7, 0xf2, 0x35, 0x73, 0x42, 0x76, 0xd3, 0x8d, + 0x47, 0xf1, 0xe1, 0x11, + ], + ], + output: [ + 0x04, 0xa1, 0x8a, 0xeb, 0x59, 0x3f, 0x79, 0x0b, 0x76, 0xa3, 0x99, 0xb7, 0xc1, 0x52, + 0x8a, 0xcd, 0xed, 0xe9, 0x3b, 0x3b, 0x2c, 0x49, 0x6b, 0xd7, 0x1b, 0xd5, 0x87, 0xcb, + 0xd7, 0xcf, 0xdf, 0x35, + ], + }, + HashTestVector { + input: [ + [ + 0xdd, 0x0c, 0x7a, 0x1d, 0x81, 0x1c, 0x7d, 0x9c, 0xd4, 0x6d, 0x37, 0x7b, 0x3f, 0xde, + 0xab, 0x3f, 0xb6, 0x79, 0xf3, 0xdc, 0x60, 0x1d, 0x00, 0x82, 0x85, 0xed, 0xcb, 0xda, + 0xe6, 0x9c, 0xe8, 0x3c, + ], + [ + 0x19, 0xe4, 0xaa, 0xc0, 0x35, 0x90, 0x17, 0xec, 0x85, 0xa1, 0x83, 0xd2, 0x20, 0x53, + 0xdb, 0x33, 0xf7, 0x34, 0x76, 0xf2, 0x1a, 0x48, 0x2e, 0xc9, 0x37, 0x83, 0x65, 0xc8, + 0xf7, 0x39, 0x3c, 0x14, + ], + ], + output: [ + 0x11, 0x03, 0xcc, 0xdc, 0x00, 0xd0, 0xf3, 0x5f, 0x65, 0x83, 0x14, 0x11, 0x6b, 0xc2, + 0xbc, 0xd9, 0x43, 0x74, 0xa9, 0x1f, 0xf9, 0x87, 0x7e, 0x70, 0x66, 0x33, 0x29, 0x04, + 0x2b, 0xd2, 0xf6, 0x1f, + ], + }, + HashTestVector { + input: [ + [ + 0xe2, 0x88, 0x53, 0x15, 0xeb, 0x46, 0x71, 0x09, 0x8b, 0x79, 0x53, 0x5e, 0x79, 0x0f, + 0xe5, 0x3e, 0x29, 0xfe, 0xf2, 0xb3, 0x76, 0x66, 0x97, 0xac, 0x32, 0xb4, 0xf4, 0x73, + 0xf4, 0x68, 0xa0, 0x08, + ], + [ + 0xe6, 0x23, 0x89, 0xfc, 0x16, 0x57, 0xe0, 0xde, 0xf0, 0xb6, 0x32, 0xc6, 0xae, 0x25, + 0xf9, 0xf7, 0x83, 0xb2, 0x7d, 0xb5, 0x9a, 0x4a, 0x15, 0x3d, 0x88, 0x2d, 0x2b, 0x21, + 0x03, 0x59, 0x65, 0x15, + ], + ], + output: [ + 0xf8, 0xf8, 0xc6, 0x5f, 0x43, 0x7c, 0x45, 0xbe, 0xac, 0x11, 0xeb, 0x7d, 0x9e, 0x47, + 0x58, 0x6d, 0x87, 0x9a, 0xfd, 0x6f, 0x93, 0x04, 0x35, 0xbe, 0x0c, 0x01, 0xd1, 0x9c, + 0x89, 0x5b, 0x8d, 0x10, + ], + }, + HashTestVector { + input: [ + [ + 0xeb, 0x94, 0x94, 0xc6, 0xd2, 0x27, 0xe2, 0x16, 0x3b, 0x46, 0x99, 0xd9, 0x91, 0xf4, + 0x33, 0xbf, 0x94, 0x86, 0xa7, 0xaf, 0xcf, 0x4a, 0x0d, 0x9c, 0x73, 0x1e, 0x98, 0x5d, + 0x99, 0x58, 0x9c, 0x0b, + ], + [ + 0xb7, 0x38, 0xe8, 0xaa, 0x0a, 0x15, 0x26, 0xa5, 0xbd, 0xef, 0x61, 0x31, 0x20, 0x37, + 0x2e, 0x83, 0x1a, 0x20, 0xda, 0x8a, 0xba, 0x18, 0xd1, 0xdb, 0xeb, 0xbc, 0x86, 0x2d, + 0xed, 0x42, 0x43, 0x1e, + ], + ], + output: [ + 0x5a, 0xeb, 0x48, 0x96, 0x21, 0xb0, 0x2e, 0x8e, 0x69, 0x27, 0xb9, 0x4f, 0xd2, 0x9a, + 0x61, 0x01, 0x83, 0xdf, 0x7f, 0x42, 0x87, 0xe9, 0xcb, 0xf1, 0xcc, 0xc8, 0x81, 0xd7, + 0xd0, 0xb7, 0x38, 0x27, + ], + }, + HashTestVector { + input: [ + [ + 0x91, 0x47, 0x69, 0x30, 0xe3, 0x38, 0x5c, 0xd3, 0xe3, 0x37, 0x9e, 0x38, 0x53, 0xd9, + 0x34, 0x67, 0xe0, 0x01, 0xaf, 0xa2, 0xfb, 0x8d, 0xc3, 0x43, 0x6d, 0x75, 0xa4, 0xa6, + 0xf2, 0x65, 0x72, 0x10, + ], + [ + 0x4b, 0x19, 0x22, 0x32, 0xec, 0xb9, 0xf0, 0xc0, 0x24, 0x11, 0xe5, 0x25, 0x96, 0xbc, + 0x5e, 0x90, 0x45, 0x7e, 0x74, 0x59, 0x39, 0xff, 0xed, 0xbd, 0x12, 0x86, 0x3c, 0xe7, + 0x1a, 0x02, 0xaf, 0x11, + ], + ], + output: [ + 0xb0, 0x14, 0x47, 0x20, 0xf5, 0xf2, 0xa2, 0x5d, 0x49, 0x2a, 0x50, 0x4e, 0xc0, 0x73, + 0x7f, 0x09, 0x7e, 0xd8, 0x52, 0x17, 0x4f, 0x55, 0xf5, 0x86, 0x30, 0x91, 0x30, 0x6c, + 0x1a, 0xf2, 0x00, 0x35, + ], + }, + HashTestVector { + input: [ + [ + 0x7b, 0x41, 0x7a, 0xdb, 0x63, 0xb3, 0x71, 0x22, 0xa5, 0xbf, 0x62, 0xd2, 0x6f, 0x1e, + 0x7f, 0x26, 0x8f, 0xb8, 0x6b, 0x12, 0xb5, 0x6d, 0xa9, 0xc3, 0x82, 0x85, 0x7d, 0xee, + 0xcc, 0x40, 0xa9, 0x0d, + ], + [ + 0x5e, 0x29, 0x35, 0x39, 0x71, 0xb3, 0x49, 0x94, 0xb6, 0x21, 0xb0, 0xb2, 0x61, 0xae, + 0xb3, 0x78, 0x6d, 0xd9, 0x84, 0xd5, 0x67, 0xdb, 0x28, 0x57, 0xb9, 0x27, 0xb7, 0xfa, + 0xe2, 0xdb, 0x58, 0x31, + ], + ], + output: [ + 0xbb, 0xbe, 0xb7, 0x42, 0xd6, 0xe7, 0xc0, 0x1a, 0xdb, 0xf4, 0xd3, 0x85, 0x5e, 0x35, + 0xfe, 0xc4, 0x62, 0x04, 0x30, 0x89, 0xc1, 0x8b, 0xa8, 0x02, 0x90, 0x64, 0x7b, 0xb0, + 0xe5, 0x81, 0xad, 0x11, + ], + }, +]; diff --git a/core/embed/unix/mpconfigport.h b/core/embed/unix/mpconfigport.h index 82e7baa44..313a05c94 100644 --- a/core/embed/unix/mpconfigport.h +++ b/core/embed/unix/mpconfigport.h @@ -203,6 +203,8 @@ extern const struct _mp_print_t mp_stderr_print; #define MICROPY_PY_TREZORUTILS (1) #define MICROPY_PY_TREZORPROTO (1) #define MICROPY_PY_TREZORUI2 (1) +#define MICROPY_PY_TREZORPALLAS (1) +#define MICROPY_PY_TREZORPOSEIDON (1) #define MP_STATE_PORT MP_STATE_VM diff --git a/core/mocks/generated/trezorpallas.pyi b/core/mocks/generated/trezorpallas.pyi new file mode 100644 index 000000000..6d559f820 --- /dev/null +++ b/core/mocks/generated/trezorpallas.pyi @@ -0,0 +1,79 @@ +from typing import * +# https://zips.z.cash/protocol/protocol.pdf#orchardkeycomponents + + +# rust/src/zcash_primitives/pallas/mod.rs +def to_base(x: bytes) -> Fp: + ... +# https://zips.z.cash/protocol/protocol.pdf#orchardkeycomponents + + +# rust/src/zcash_primitives/pallas/mod.rs +def to_scalar(x: bytes) -> Scalar: + ... +# https://zips.z.cash/protocol/protocol.pdf#concretegrouphashpallasandvesta + + +# rust/src/zcash_primitives/pallas/mod.rs +def group_hash(domain: str, message: bytes) -> Point: + ... + + +# rust/src/zcash_primitives/pallas/mod.rs +def scalar_from_i64(x: int) -> Scalar: + """Converts integer to Scalar.""" + + +# rust/src/zcash_primitives/pallas/mod.rs +class Fp: + """Pallas base field.""" + def __init__(self, repr: bytes) -> None: + ... + def to_bytes(self) -> bytes: + ... + + +# rust/src/zcash_primitives/pallas/mod.rs +class Scalar: + """Pallas scalar field.""" + def __init__(self, repr: bytes) -> None: + ... + def to_bytes(self) -> bytes: + ... + def is_not_zero(self) -> bool: + ... + def __mul__(self, other: Point) -> Point: + ... + def __add__(self, other: Scalar) -> Scalar: + ... + def __neg__(self) -> Point: + ... + + +# rust/src/zcash_primitives/pallas/mod.rs +class Point: + """Pallas point.""" + def __init__(self, repr: bytes) -> None: + ... + def to_bytes(self) -> bytes: + ... + def extract(self) -> Fp: + ... + def is_identity(self) -> bool: + ... + def __add__(self, other: Point) -> Point: + ... + def __neg__(self) -> Point: + ... + + +# rust/src/zcash_primitives/pallas/mod.rs +class generators: + SPENDING_KEY_BASE: Point + NULLIFIER_K_BASE: Point + VALUE_COMMITMENT_VALUE_BASE: Point + VALUE_COMMITMENT_RANDOMNESS_BASE: Point + NOTE_COMMITMENT_BASE: Point + NOTE_COMMITMENT_Q: Point + IVK_COMMITMENT_BASE: Point + IVK_COMMITMENT_Q: Point diff --git a/core/mocks/generated/trezorposeidon.pyi b/core/mocks/generated/trezorposeidon.pyi new file mode 100644 index 000000000..b40e2ae5a --- /dev/null +++ b/core/mocks/generated/trezorposeidon.pyi @@ -0,0 +1,6 @@ +from typing import * + + +# rust/src/zcash_primitives/poseidon/mod.rs +def poseidon(x: Fp, y: Fp) -> Fp: + """Poseidon hash function.""" diff --git a/core/mocks/generated/trezorutils.pyi b/core/mocks/generated/trezorutils.pyi index 177a42288..b2bcf4cbd 100644 --- a/core/mocks/generated/trezorutils.pyi +++ b/core/mocks/generated/trezorutils.pyi @@ -80,3 +80,4 @@ VERSION_PATCH: int MODEL: str EMULATOR: bool BITCOIN_ONLY: bool +ZCASH_SHIELDED: bool diff --git a/core/site_scons/site_tools/micropython/__init__.py b/core/site_scons/site_tools/micropython/__init__.py index 38c40880f..d49a5b7c7 100644 --- a/core/site_scons/site_tools/micropython/__init__.py +++ b/core/site_scons/site_tools/micropython/__init__.py @@ -26,9 +26,12 @@ def generate(env): # replace "utils.BITCOIN_ONLY" with literal constant (True/False) # so the compiler can optimize out the things we don't want btc_only = env['bitcoin_only'] == '1' + zcash_shielded = env['zcash_shielded'] == '1' interim = f"{target[:-4]}.i" # replace .mpy with .i sed_scripts = " ".join([ rf"-e 's/utils\.BITCOIN_ONLY/{btc_only}/g'", + rf"-e 's/utils\.ZCASH_SHIELDED/{zcash_shielded}/g'", + rf"-e 's/^\s+ZCASH_SHIELDED/# \0/'", r"-e 's/if TYPE_CHECKING/if False/'", r"-e 's/import typing/# \0/'", r"-e '/from typing import (/,/^\s*)/ {s/^/# /}'", diff --git a/core/src/all_modules.py b/core/src/all_modules.py index 6f99efaee..610c2f8a9 100644 --- a/core/src/all_modules.py +++ b/core/src/all_modules.py @@ -81,6 +81,8 @@ trezor.crypto.der import trezor.crypto.der trezor.crypto.hashlib import trezor.crypto.hashlib +trezor.crypto.pallas +import trezor.crypto.pallas trezor.crypto.rlp import trezor.crypto.rlp trezor.crypto.scripts diff --git a/core/src/trezor/crypto/hashlib.py b/core/src/trezor/crypto/hashlib.py index 388c004db..d1e02c0aa 100644 --- a/core/src/trezor/crypto/hashlib.py +++ b/core/src/trezor/crypto/hashlib.py @@ -10,3 +10,8 @@ from trezorcrypto import ( # noqa: F401 sha256, sha512, ) + +from trezor import utils + +if utils.ZCASH_SHIELDED: + from trezorposeidon import poseidon # noqa: F401 diff --git a/core/src/trezor/crypto/pallas.py b/core/src/trezor/crypto/pallas.py new file mode 100644 index 000000000..122708683 --- /dev/null +++ b/core/src/trezor/crypto/pallas.py @@ -0,0 +1,14 @@ +"""Curve for Zcash cryptography.""" + +from trezor import utils + +if utils.ZCASH_SHIELDED: + from trezorpallas import ( # noqa: F401 + Fp, + Point, + Scalar, + group_hash, + scalar_from_i64, + to_base, + to_scalar, + ) diff --git a/core/src/trezor/utils.py b/core/src/trezor/utils.py index 1a7331079..a417dad1e 100644 --- a/core/src/trezor/utils.py +++ b/core/src/trezor/utils.py @@ -8,6 +8,7 @@ from trezorutils import ( # noqa: F401 VERSION_MAJOR, VERSION_MINOR, VERSION_PATCH, + ZCASH_SHIELDED, consteq, firmware_hash, firmware_vendor,