feat(core): add Zcash Rust primitives

zcash-rust-primitives
Tomas Krnak 2 years ago
parent 031bac4a9b
commit 6311675119

@ -0,0 +1 @@
Add Zcash Rust primitives

@ -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}',

@ -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)}"'

@ -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,

@ -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 <http://www.gnu.org/licenses/>.
*/
#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

@ -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)

@ -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]]

@ -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"

@ -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"

@ -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"

@ -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;
}

@ -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()
}

@ -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

@ -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;
}

@ -1,4 +1,5 @@
use core::{
array::TryFromSliceError,
convert::{Infallible, TryInto},
num::TryFromIntError,
};
@ -83,3 +84,9 @@ impl From<TryFromIntError> for Error {
Self::OutOfRange
}
}
impl From<TryFromSliceError> for Error {
fn from(_e: TryFromSliceError) -> Error {
Error::OutOfRange
}
}

@ -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;

@ -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,

@ -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;

@ -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<Obj> for usize {
}
}
impl<const N: usize> TryFrom<[u8; N]> for Obj {
type Error = Error;
fn try_from(array: [u8; N]) -> Result<Obj, Error> {
Ok((&array[..]).try_into()?)
}
}
impl<const N: usize> TryFrom<Obj> 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<T> From<Option<T>> for Obj
where
T: Into<Obj>,

@ -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.

@ -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<Wrapped<Foo>> = 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<Gc<Wrapped<Self>>, Error> {
Gc::new(Wrapped {
base: Self::obj_type().as_base(),
inner,
})
}
fn wrap(self) -> Result<Obj, Error> {
let value: Gc<Wrapped<Self>> = 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<T: Wrappable> {
base: ObjBase,
inner: T,
}
impl<T: Wrappable> Wrapped<T> {
pub fn inner(&self) -> &T {
&self.inner
}
pub fn inner_mut(&mut self) -> &mut T {
&mut self.inner
}
}
impl<T: Wrappable> TryFrom<Obj> for Gc<Wrapped<T>> {
type Error = Error;
fn try_from(obj: Obj) -> Result<Gc<Wrapped<T>>, 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)
}
}
}

@ -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");
}
}

@ -1,4 +1,6 @@
pub mod bip39;
#[cfg(all(test, feature = "zcash_shielded"))]
mod blake2b_tests;
#[macro_use]
#[allow(unused_macros)]
pub mod common;

@ -0,0 +1,2 @@
pub mod pallas;
pub mod poseidon;

@ -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<F: Wrappable + ff::PrimeField<Repr = [u8; 32]>>(
_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::<F>::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) }
}

@ -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::<Fp>,
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<Wrapped<Fp>> = 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::<Wrapped<Fp>>::try_from(this)?;
let this = this.deref().inner();
match op {
ffi::mp_binary_op_t_MP_BINARY_OP_ADD => {
let other = Gc::<Wrapped<Fp>>::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) }
}

@ -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)),
}
}

@ -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(),
};

@ -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::<Point>::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::<Wrapped<Point>>::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::<Wrapped<Point>>::try_from(this)?;
let this = this.deref().inner();
match op {
ffi::mp_binary_op_t_MP_BINARY_OP_ADD => {
let other = Gc::<Wrapped<Point>>::try_from(other)?;
let other = other.deref().inner();
(this + other).wrap()
}
ffi::mp_binary_op_t_MP_BINARY_OP_EQUAL => {
let other = Gc::<Wrapped<Point>>::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::<Wrapped<Point>>::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::<Wrapped<Point>>::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::<Wrapped<Point>>::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::<Wrapped<Point>>::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) }
}

@ -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::<Scalar>,
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::<Wrapped<Scalar>>::try_from(this)?;
let this = this.deref().inner();
match op {
ffi::mp_binary_op_t_MP_BINARY_OP_MULTIPLY => {
let other = Gc::<Wrapped<Point>>::try_from(other)?;
let other = other.deref().inner();
(other * this).wrap()
}
ffi::mp_binary_op_t_MP_BINARY_OP_ADD => {
let other = Gc::<Wrapped<Scalar>>::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::<Wrapped<Scalar>>::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::<Wrapped<Scalar>>::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) }
}

File diff suppressed because it is too large Load Diff

@ -0,0 +1,113 @@
//! Generates Poseidon round constants according to apendix F of article
//!
//! POSEIDON: A New Hash Function for Zero-Knowledge Proof Systems
//! <https://eprint.iacr.org/2019/458>
//!
//! for paraments defined in
//! <https://zips.z.cash/protocol/protocol.pdf#poseidonhash>
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(),
])
}
}

@ -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<Wrapped<Fp>> = nk.try_into()?;
let nk: Fp = nk.deref().inner().clone();
let rho: Gc<Wrapped<Fp>> = 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(),
};

@ -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,
],
},
];

@ -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

@ -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

@ -0,0 +1,6 @@
from typing import *
# rust/src/zcash_primitives/poseidon/mod.rs
def poseidon(x: Fp, y: Fp) -> Fp:
"""Poseidon hash function."""

@ -80,3 +80,4 @@ VERSION_PATCH: int
MODEL: str
EMULATOR: bool
BITCOIN_ONLY: bool
ZCASH_SHIELDED: bool

@ -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/^/# /}'",

@ -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

@ -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

@ -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,
)

@ -8,6 +8,7 @@ from trezorutils import ( # noqa: F401
VERSION_MAJOR,
VERSION_MINOR,
VERSION_PATCH,
ZCASH_SHIELDED,
consteq,
firmware_hash,
firmware_vendor,

Loading…
Cancel
Save