use ledger_secure_sdk_sys::*;
use zeroize::Zeroize;
use crate::hash::{sha2::Sha2_512, HashInit};
mod stark;
#[repr(u8)]
#[derive(Copy, Clone)]
pub enum CurvesId {
Secp256k1 = CX_CURVE_SECP256K1,
Secp256r1 = CX_CURVE_SECP256R1,
Secp384r1 = CX_CURVE_SECP384R1,
BrainpoolP256T1 = CX_CURVE_BrainPoolP256T1,
BrainpoolP256R1 = CX_CURVE_BrainPoolP256R1,
BrainpoolP320R1 = CX_CURVE_BrainPoolP320R1,
BrainpoolP320T1 = CX_CURVE_BrainPoolP320T1,
BrainpoolP384T1 = CX_CURVE_BrainPoolP384T1,
BrainpoolP384R1 = CX_CURVE_BrainPoolP384R1,
BrainpoolP512T1 = CX_CURVE_BrainPoolP512T1,
BrainpoolP512R1 = CX_CURVE_BrainPoolP512R1,
Bls12381G1 = CX_CURVE_BLS12_381_G1, FRP256v1 = CX_CURVE_FRP256V1, Stark256 = CX_CURVE_Stark256,
Ed25519 = CX_CURVE_Ed25519,
Ed448 = CX_CURVE_Ed448, Curve25519 = CX_CURVE_Curve25519, Curve448 = CX_CURVE_Curve448, Secp521r1 = CX_CURVE_SECP521R1, Invalid,
}
#[derive(Copy, Clone, PartialEq, Eq, Debug)]
pub enum CxError {
Carry,
Locked,
Unlocked,
NotLocked,
NotUnlocked,
InternalError,
InvalidParameterSize,
InvalidParameterValue,
InvalidParameter,
NotInvertible,
Overflow,
MemoryFull,
NoResidue,
PointAtInfinity,
InvalidPoint,
InvalidCurve,
GenericError,
}
impl From<u32> for CxError {
fn from(x: u32) -> CxError {
match x {
CX_CARRY => CxError::Carry,
CX_LOCKED => CxError::Locked,
CX_UNLOCKED => CxError::Unlocked,
CX_NOT_LOCKED => CxError::NotLocked,
CX_NOT_UNLOCKED => CxError::NotUnlocked,
CX_INTERNAL_ERROR => CxError::InternalError,
CX_INVALID_PARAMETER_SIZE => CxError::InvalidParameterSize,
CX_INVALID_PARAMETER_VALUE => CxError::InvalidParameterValue,
CX_INVALID_PARAMETER => CxError::InvalidParameter,
CX_NOT_INVERTIBLE => CxError::NotInvertible,
CX_OVERFLOW => CxError::Overflow,
CX_MEMORY_FULL => CxError::MemoryFull,
CX_NO_RESIDUE => CxError::NoResidue,
CX_EC_INFINITE_POINT => CxError::PointAtInfinity,
CX_EC_INVALID_POINT => CxError::InvalidPoint,
CX_EC_INVALID_CURVE => CxError::InvalidCurve,
_ => CxError::GenericError,
}
}
}
impl From<CxError> for u32 {
fn from(e: CxError) -> u32 {
e as u32
}
}
#[repr(C)]
pub struct ECCKeyRaw {
curve: CurvesId,
keylength: usize,
key: [u8; 160],
}
#[repr(C)]
pub struct ECPrivateKey<const N: usize, const TY: char> {
curve: CurvesId,
keylength: usize,
key: [u8; N],
}
#[repr(C)]
#[derive(Clone)]
pub struct ECPublicKey<const S: usize, const TY: char> {
pub curve: CurvesId,
pub keylength: usize,
pub pubkey: [u8; S],
}
impl<const N: usize, const TY: char> Default for ECPrivateKey<N, TY> {
fn default() -> ECPrivateKey<N, TY> {
ECPrivateKey {
curve: CurvesId::Invalid,
keylength: N,
key: [0u8; N],
}
}
}
impl<const N: usize, const TY: char> Drop for ECPrivateKey<N, TY> {
#[inline(never)]
fn drop(&mut self) {
self.key.zeroize();
}
}
impl<const N: usize, const TY: char> ECPrivateKey<N, TY> {
pub const P: usize = 2 * N + 1;
pub const S: usize = 6 + 2 * (N + 1);
pub fn new(curve: CurvesId) -> ECPrivateKey<N, TY> {
ECPrivateKey {
curve,
keylength: N,
key: [0u8; N],
}
}
pub fn public_key(&self) -> Result<ECPublicKey<{ Self::P }, TY>, CxError>
where
[(); Self::P]: Sized,
{
let mut pubkey = ECPublicKey::<{ Self::P }, TY>::new(self.curve);
let err = unsafe {
cx_ecfp_generate_pair_no_throw(
self.curve as u8,
(&mut pubkey as *mut ECPublicKey<{ Self::P }, TY>).cast(), self as *const ECPrivateKey<N, TY> as *mut cx_ecfp_256_private_key_s,
true,
)
};
if err != 0 {
Err(err.into())
} else {
Ok(pubkey)
}
}
}
impl<const N: usize> ECPrivateKey<N, 'W'> {
fn ecdsa_sign(
&self,
hash: &[u8],
hash_id: u8,
mode: u32,
) -> Result<([u8; Self::S], u32, u32), CxError> {
let mut sig = [0u8; Self::S];
let mut sig_len = Self::S;
let mut info = 0;
let len = unsafe {
cx_ecdsa_sign_no_throw(
self as *const ECPrivateKey<N, 'W'> as *const cx_ecfp_256_private_key_s,
mode,
hash_id,
hash.as_ptr(),
hash.len(),
sig.as_mut_ptr(),
&mut sig_len,
&mut info,
)
};
if len != CX_OK {
Err(len.into())
} else {
Ok((sig, sig_len as u32, info & CX_ECCINFO_PARITY_ODD))
}
}
pub fn deterministic_sign(&self, hash: &[u8]) -> Result<([u8; Self::S], u32, u32), CxError> {
let hash_id = match self.keylength {
x if x <= 32 => CX_SHA256,
x if x <= 48 => CX_SHA384,
x if x <= 64 => CX_SHA512,
_ => CX_BLAKE2B,
};
self.ecdsa_sign(hash, hash_id, CX_RND_RFC6979 | CX_LAST)
}
pub fn sign(&self, hash: &[u8]) -> Result<([u8; Self::S], u32, u32), CxError> {
self.ecdsa_sign(hash, 0, CX_RND_TRNG | CX_LAST)
}
pub fn ecdh(&self, p: &[u8]) -> Result<[u8; N], CxError> {
let mut secret = [0u8; N];
let len = unsafe {
cx_ecdh_no_throw(
self as *const ECPrivateKey<N, 'W'> as *const cx_ecfp_256_private_key_s,
CX_ECDH_X,
p.as_ptr(),
p.len(),
secret.as_mut_ptr(),
N,
)
};
if len != CX_OK {
Err(len.into())
} else {
Ok(secret)
}
}
}
pub struct Ed25519Stream {
hash: Sha2_512,
pub big_r: [u8; 32],
pub signature: [u8; 64],
}
impl Default for Ed25519Stream {
fn default() -> Self {
Ed25519Stream {
hash: Sha2_512::default(),
big_r: [0u8; 32],
signature: [0u8; 64],
}
}
}
macro_rules! check_cx_ok {
($fn_call:expr) => {{
let err = unsafe { $fn_call };
if err != CX_OK {
return Err(err.into());
}
}};
}
impl Ed25519Stream {
pub fn init(&mut self, key: &ECPrivateKey<32, 'E'>) -> Result<(), CxError> {
let mut temp = Secret::<64>::new();
self.hash.reset();
self.hash
.hash(&key.key[..], temp.as_mut())
.map_err(|_| CxError::GenericError)?;
self.hash.reset();
self.hash
.update(&temp.0[32..64])
.map_err(|_| CxError::GenericError)?;
Ok(())
}
fn compute_r(&mut self, key: &ECPrivateKey<32, 'E'>) -> Result<(), CxError> {
self.hash
.finalize(&mut self.signature)
.map_err(|_| CxError::GenericError)?;
self.signature.reverse();
check_cx_ok!(cx_bn_lock(32, 0));
let mut r = CX_BN_FLAG_UNSET;
check_cx_ok!(cx_bn_alloc_init(
&mut r as *mut cx_bn_t,
64,
self.signature.as_ptr(),
self.signature.len(),
));
let mut ed_p = cx_ecpoint_t::default();
check_cx_ok!(cx_ecpoint_alloc(
&mut ed_p as *mut cx_ecpoint_t,
CX_CURVE_Ed25519
));
check_cx_ok!(cx_ecdomain_generator_bn(CX_CURVE_Ed25519, &mut ed_p));
check_cx_ok!(cx_ecpoint_scalarmul_bn(&mut ed_p, r));
let mut sign = 0;
check_cx_ok!(cx_ecpoint_compress(
&ed_p,
self.big_r.as_mut_ptr(),
self.big_r.len(),
&mut sign
));
check_cx_ok!(cx_bn_unlock());
self.big_r.reverse();
self.big_r[31] |= if sign != 0 { 0x80 } else { 0x00 };
self.hash.reset();
self.hash
.update(&self.big_r)
.map_err(|_| CxError::GenericError)?;
let mut pk = key.public_key()?;
check_cx_ok!(cx_edwards_compress_point_no_throw(
CX_CURVE_Ed25519,
pk.pubkey.as_mut_ptr(),
pk.keylength,
));
self.hash
.update(&pk.pubkey[1..33])
.map_err(|_| CxError::GenericError)?;
Ok(())
}
fn compute_s(&mut self, key: &ECPrivateKey<32, 'E'>) -> Result<(), CxError> {
check_cx_ok!(cx_bn_lock(32, 0));
let (h_a, ed25519_order) = {
let mut h_scalar = Secret::<64>::new();
self.hash
.finalize(h_scalar.as_mut())
.map_err(|_| CxError::GenericError)?;
h_scalar.0.reverse();
let mut h_scalar_bn = CX_BN_FLAG_UNSET;
check_cx_ok!(cx_bn_alloc_init(
&mut h_scalar_bn as *mut cx_bn_t,
64,
h_scalar.0.as_ptr(),
h_scalar.0.len(),
));
let mut ed25519_order = CX_BN_FLAG_UNSET;
check_cx_ok!(cx_bn_alloc(&mut ed25519_order, 64));
check_cx_ok!(cx_ecdomain_parameter_bn(
CX_CURVE_Ed25519,
CX_CURVE_PARAM_Order,
ed25519_order,
));
let mut rv = CX_BN_FLAG_UNSET;
let mut temp = Secret::<64>::new();
self.hash.reset();
self.hash
.hash(&key.key[0..key.keylength], temp.as_mut())
.map_err(|_| CxError::GenericError)?;
temp.0[0] &= 248;
temp.0[31] &= 63;
temp.0[31] |= 64;
let key_slice = &mut temp.0[0..32];
key_slice.reverse();
let mut key_bn = CX_BN_FLAG_UNSET;
check_cx_ok!(cx_bn_alloc_init(
&mut key_bn as *mut cx_bn_t,
64,
key_slice.as_ptr(),
key_slice.len(),
));
check_cx_ok!(cx_bn_alloc(&mut rv, 64));
check_cx_ok!(cx_bn_mod_mul(rv, key_bn, h_scalar_bn, ed25519_order));
check_cx_ok!(cx_bn_destroy(&mut key_bn));
(rv, ed25519_order)
};
let mut r = CX_BN_FLAG_UNSET;
check_cx_ok!(cx_bn_alloc_init(
&mut r as *mut cx_bn_t,
64,
self.signature.as_ptr(),
self.signature.len(),
));
let mut s = CX_BN_FLAG_UNSET;
check_cx_ok!(cx_bn_alloc(&mut s, 64));
check_cx_ok!(cx_bn_mod_add(s, h_a, r, ed25519_order));
check_cx_ok!(cx_bn_set_u32(r, 0));
check_cx_ok!(cx_bn_mod_sub(s, s, r, ed25519_order));
check_cx_ok!(cx_bn_export(s, self.signature.as_mut_ptr(), 32));
check_cx_ok!(cx_bn_unlock());
self.signature[..32].reverse();
self.signature.copy_within(0..32, 32);
self.signature[0..32].copy_from_slice(&self.big_r);
Ok(())
}
pub fn sign_finalize(&mut self, key: &ECPrivateKey<32, 'E'>) -> Result<(), CxError> {
match self.big_r.iter().all(|b| b == &0) {
true => self.compute_r(key),
false => self.compute_s(key),
}
}
pub fn sign_update(&mut self, msg: &[u8]) -> Result<(), CxError> {
self.hash.update(msg).map_err(|_| CxError::GenericError)?;
Ok(())
}
}
impl<const N: usize> ECPrivateKey<N, 'E'> {
pub const EP: usize = 2 * N;
pub fn sign(&self, hash: &[u8]) -> Result<([u8; Self::EP], u32), CxError> {
let mut sig = [0u8; Self::EP];
let sig_len = Self::EP;
let hash_id = match self.keylength {
x if x <= 32 => CX_SHA512,
_ => CX_BLAKE2B,
};
let len = unsafe {
cx_eddsa_sign_no_throw(
self as *const ECPrivateKey<N, 'E'> as *const cx_ecfp_256_private_key_s,
hash_id,
hash.as_ptr(),
hash.len(),
sig.as_mut_ptr(),
sig_len,
)
};
if len != CX_OK {
Err(len.into())
} else {
Ok((sig, sig_len as u32))
}
}
}
impl<const P: usize, const TY: char> ECPublicKey<P, TY> {
pub const S: usize = 7 + P;
pub fn new(curve_id: CurvesId) -> ECPublicKey<P, TY> {
ECPublicKey::<P, TY> {
curve: curve_id,
keylength: P,
pubkey: [0u8; P],
}
}
}
impl<const P: usize, const TY: char> AsRef<[u8]> for ECPublicKey<P, TY> {
fn as_ref(&self) -> &[u8] {
&self.pubkey
}
}
impl<const P: usize, const TY: char> From<ECPublicKey<P, TY>> for [u8; P] {
fn from(p: ECPublicKey<P, TY>) -> Self {
p.pubkey
}
}
impl<const P: usize> ECPublicKey<P, 'W'> {
pub fn verify(&self, signature: (&[u8], u32), hash: &[u8]) -> bool {
unsafe {
cx_ecdsa_verify_no_throw(
self as *const ECPublicKey<P, 'W'> as *const cx_ecfp_256_public_key_s,
hash.as_ptr(),
hash.len(),
signature.0.as_ptr(),
signature.1 as usize,
)
}
}
}
impl<const P: usize> ECPublicKey<P, 'E'> {
pub fn verify(&self, signature: (&[u8], u32), hash: &[u8], hash_id: u8) -> bool {
unsafe {
cx_eddsa_verify_no_throw(
self as *const ECPublicKey<P, 'E'> as *const cx_ecfp_256_public_key_s,
hash_id,
hash.as_ptr(),
hash.len(),
signature.0.as_ptr(),
signature.1 as usize,
)
}
}
}
pub fn bip32_derive(
curve: CurvesId,
path: &[u32],
key: &mut [u8],
cc: Option<&mut [u8]>,
) -> Result<(), CxError> {
match curve {
CurvesId::Secp256k1 | CurvesId::Secp256r1 | CurvesId::Ed25519 => {
if key.len() < 64 {
return Err(CxError::InvalidParameter);
}
}
_ => return Err(CxError::InvalidParameter),
}
unsafe {
match cc {
Some(buf) => os_perso_derive_node_bip32(
curve as u8,
path.as_ptr(),
path.len() as u32,
key.as_mut_ptr(),
buf.as_mut_ptr(),
),
None => os_perso_derive_node_bip32(
curve as u8,
path.as_ptr(),
path.len() as u32,
key.as_mut_ptr(),
core::ptr::null_mut(),
),
}
};
Ok(())
}
pub struct Secret<const N: usize>([u8; N]);
impl<const N: usize> Default for Secret<N> {
fn default() -> Secret<N> {
Secret([0u8; N])
}
}
impl<const N: usize> Secret<N> {
pub fn new() -> Secret<N> {
Secret::default()
}
}
impl<const N: usize> AsRef<[u8]> for Secret<N> {
fn as_ref(&self) -> &[u8] {
&self.0
}
}
impl<const N: usize> AsMut<[u8]> for Secret<N> {
fn as_mut(&mut self) -> &mut [u8] {
&mut self.0
}
}
impl<const N: usize> Drop for Secret<N> {
#[inline(never)]
fn drop(&mut self) {
self.0.zeroize();
}
}
#[repr(C)]
#[derive(Default)]
pub struct ChainCode {
pub value: [u8; 32],
}
pub trait SeedDerive {
type Target;
fn derive_from(path: &[u32]) -> (Self::Target, Option<ChainCode>);
fn derive_from_path(path: &[u32]) -> Self::Target {
Self::derive_from(path).0
}
}
impl SeedDerive for Secp256k1 {
type Target = ECPrivateKey<32, 'W'>;
fn derive_from(path: &[u32]) -> (Self::Target, Option<ChainCode>) {
let mut tmp = Secret::<64>::new();
let mut cc: ChainCode = Default::default();
let _ = bip32_derive(
CurvesId::Secp256k1,
path,
tmp.as_mut(),
Some(cc.value.as_mut()),
);
let mut sk = Self::Target::new(CurvesId::Secp256k1);
let keylen = sk.key.len();
sk.key.copy_from_slice(&tmp.0[..keylen]);
(sk, Some(cc))
}
}
impl SeedDerive for Secp256r1 {
type Target = ECPrivateKey<32, 'W'>;
fn derive_from(path: &[u32]) -> (Self::Target, Option<ChainCode>) {
let mut tmp = Secret::<64>::new();
let mut cc: ChainCode = Default::default();
let _ = bip32_derive(
CurvesId::Secp256r1,
path,
tmp.as_mut(),
Some(cc.value.as_mut()),
);
let mut sk = Self::Target::new(CurvesId::Secp256r1);
let keylen = sk.key.len();
sk.key.copy_from_slice(&tmp.0[..keylen]);
(sk, Some(cc))
}
}
impl SeedDerive for Ed25519 {
type Target = ECPrivateKey<32, 'E'>;
fn derive_from(path: &[u32]) -> (Self::Target, Option<ChainCode>) {
let mut tmp = Secret::<64>::new();
let mut cc: ChainCode = Default::default();
let _ = bip32_derive(
CurvesId::Ed25519,
path,
tmp.as_mut(),
Some(cc.value.as_mut()),
);
let mut sk = Self::Target::new(CurvesId::Ed25519);
let keylen = sk.key.len();
sk.key.copy_from_slice(&tmp.0[..keylen]);
(sk, Some(cc))
}
}
impl Ed25519 {
pub fn derive_from_path_slip10(path: &[u32]) -> ECPrivateKey<32, 'E'> {
let mut tmp = Secret::<64>::new();
unsafe {
os_perso_derive_node_with_seed_key(
HDW_ED25519_SLIP10,
CurvesId::Ed25519 as u8,
path.as_ptr(),
path.len() as u32,
tmp.as_mut().as_mut_ptr(),
core::ptr::null_mut(),
core::ptr::null_mut(),
0,
);
}
let mut sk = ECPrivateKey::new(CurvesId::Ed25519);
let keylen = sk.key.len();
sk.key.copy_from_slice(&tmp.0[..keylen]);
sk
}
}
impl SeedDerive for Stark256 {
type Target = ECPrivateKey<32, 'W'>;
fn derive_from(path: &[u32]) -> (Self::Target, Option<ChainCode>) {
let mut sk = Self::Target::new(CurvesId::Stark256);
stark::eip2645_derive(path, &mut sk.key);
(sk, None)
}
}
macro_rules! impl_curve {
($typename:ident, $size:expr, $curvetype:expr) => {
pub struct $typename {}
impl $typename {
#[allow(clippy::new_ret_no_self)]
pub fn new() -> ECPrivateKey<$size, $curvetype> {
ECPrivateKey::<$size, $curvetype>::new(CurvesId::$typename)
}
pub fn from(keybytes: &[u8]) -> ECPrivateKey<$size, $curvetype> {
let mut sk = $typename::new();
sk.key.copy_from_slice(keybytes);
sk
}
}
};
}
impl_curve!(Secp256k1, 32, 'W');
impl_curve!(Secp256r1, 32, 'W');
impl_curve!(Secp384r1, 48, 'W');
impl_curve!(BrainpoolP256R1, 32, 'W');
impl_curve!(BrainpoolP256T1, 32, 'W');
impl_curve!(BrainpoolP320R1, 40, 'W');
impl_curve!(BrainpoolP320T1, 40, 'W');
impl_curve!(BrainpoolP384R1, 48, 'W');
impl_curve!(BrainpoolP384T1, 48, 'W');
impl_curve!(BrainpoolP512R1, 64, 'W');
impl_curve!(BrainpoolP512T1, 64, 'W');
impl_curve!(Stark256, 32, 'W');
impl_curve!(Ed25519, 32, 'E');
pub const fn make_bip32_path<const N: usize>(bytes: &[u8]) -> [u32; N] {
#[derive(Copy, Clone)]
enum Bip32ParserState {
FirstDigit,
Digit,
Hardened,
}
let mut path = [0u32; N];
if (bytes[0] != b'm') || (bytes[1] != b'/') {
panic!("path must start with \"m/\"")
}
let mut i = 2; let mut j = 0; let mut acc = 0; let mut state = Bip32ParserState::FirstDigit;
while i < bytes.len() {
let c = bytes[i];
match state {
Bip32ParserState::FirstDigit => match c {
b'0'..=b'9' => {
acc = (c - b'0') as u32;
path[j] = acc;
state = Bip32ParserState::Digit
}
_ => panic!("expected digit after '/'"),
},
Bip32ParserState::Digit => {
match c {
b'0'..=b'9' => {
acc = acc * 10 + (c - b'0') as u32;
path[j] = acc;
}
b'\'' => {
path[j] = acc + 0x80000000;
state = Bip32ParserState::Hardened
}
b'/' => {
path[j] = acc;
j += 1;
state = Bip32ParserState::FirstDigit
}
_ => panic!("unexpected character in path"),
}
}
Bip32ParserState::Hardened => match c {
b'/' => {
j += 1;
state = Bip32ParserState::FirstDigit
}
_ => panic!("expected '/' character after hardening"),
},
}
i += 1;
}
if let Bip32ParserState::FirstDigit = state {
panic!("missing number in path")
}
if j != N - 1 {
panic!("wrong path length");
}
path
}
#[cfg(test)]
mod tests {
use super::*;
use crate::assert_eq_err as assert_eq;
use crate::testing::TestType;
use testmacro::test_item as test;
trait ConstantFill {
fn set_constant_key(&mut self);
}
impl<const N: usize, const TY: char> ConstantFill for ECPrivateKey<N, TY> {
fn set_constant_key(&mut self) {
let length = self.key.len();
self.key[..length - 1].fill_with(|| 0xab);
}
}
const PATH0: [u32; 5] = make_bip32_path(b"m/44'/535348'/0'/0/0");
const PATH1: [u32; 5] = make_bip32_path(b"m/44'/535348'/0'/0/1");
fn display_error_code(e: CxError) {
let ec = crate::testing::to_hex(e.into());
crate::log::info!(
"Error code: \x1b[1;33m{}\x1b[0m",
core::str::from_utf8(&ec).unwrap()
);
}
const TEST_HASH: &[u8; 13] = b"test_message1";
#[test]
fn pubkey_secp256k1() {
let sk_bytes = [0x77u8; 32];
let pk = Secp256k1::from(&sk_bytes)
.public_key()
.map_err(display_error_code)?;
let expected = [
0x04, 0x79, 0x62, 0xd4, 0x5b, 0x38, 0xe8, 0xbc, 0xf8, 0x2f, 0xa8, 0xef, 0xa8, 0x43,
0x2a, 0x1, 0xf2, 0xc, 0x9a, 0x53, 0xe2, 0x4c, 0x7d, 0x3f, 0x11, 0xdf, 0x19, 0x7c, 0xb8,
0xe7, 0x9, 0x26, 0xda, 0x7a, 0x3e, 0xf3, 0xeb, 0xaf, 0xc7, 0x56, 0xdc, 0x3b, 0x24,
0xb7, 0x52, 0x92, 0xd4, 0xcc, 0x5d, 0x71, 0xb1, 0x70, 0xe9, 0x70, 0x44, 0xa9, 0x85,
0x83, 0x53, 0x44, 0x3a, 0x96, 0xba, 0xed, 0x23,
];
assert_eq!(pk.as_ref(), &expected);
}
#[test]
fn ecdsa_secp256k1() {
let sk = Secp256k1::derive_from_path(&PATH0);
let s = sk
.deterministic_sign(TEST_HASH)
.map_err(display_error_code)?;
let pk = sk.public_key().map_err(display_error_code)?;
assert_eq!(pk.verify((&s.0, s.1), TEST_HASH), true);
let s = sk.sign(TEST_HASH).map_err(display_error_code)?;
assert_eq!(pk.verify((&s.0, s.1), TEST_HASH), true);
}
#[test]
fn ecdsa_secp256r1() {
let sk = Secp256r1::derive_from_path(&PATH0);
let s = sk
.deterministic_sign(TEST_HASH)
.map_err(display_error_code)?;
let pk = sk.public_key().map_err(display_error_code)?;
assert_eq!(pk.verify((&s.0, s.1), TEST_HASH), true);
let s = sk.sign(TEST_HASH).map_err(display_error_code)?;
assert_eq!(pk.verify((&s.0, s.1), TEST_HASH), true);
}
#[test]
fn ecdsa_secp384r1() {
let mut sk = Secp384r1::new();
sk.set_constant_key();
let s = sk
.deterministic_sign(TEST_HASH)
.map_err(display_error_code)?;
let pk = sk.public_key().map_err(display_error_code)?;
assert_eq!(pk.verify((&s.0, s.1), TEST_HASH), true);
let s = sk.sign(TEST_HASH).map_err(display_error_code)?;
assert_eq!(pk.verify((&s.0, s.1), TEST_HASH), true);
}
#[test]
fn ecdsa_brainpool256r1() {
let mut sk = BrainpoolP256R1::new();
sk.set_constant_key();
let pk = sk.public_key().map_err(display_error_code)?;
let s = sk
.deterministic_sign(TEST_HASH)
.map_err(display_error_code)?;
assert_eq!(pk.verify((&s.0, s.1), TEST_HASH), true);
let s = sk.sign(TEST_HASH).map_err(display_error_code)?;
assert_eq!(pk.verify((&s.0, s.1), TEST_HASH), true);
}
#[test]
fn ecdsa_brainpool320r1() {
let mut sk = BrainpoolP320R1::new();
sk.set_constant_key();
let s = sk
.deterministic_sign(TEST_HASH)
.map_err(display_error_code)?;
let pk = sk.public_key().map_err(display_error_code)?;
assert_eq!(pk.verify((&s.0, s.1), TEST_HASH), true);
let s = sk.sign(TEST_HASH).map_err(display_error_code)?;
assert_eq!(pk.verify((&s.0, s.1), TEST_HASH), true);
}
#[test]
fn ecdsa_brainpool384r1() {
let mut sk = BrainpoolP384R1::new();
sk.set_constant_key();
let s = sk
.deterministic_sign(TEST_HASH)
.map_err(display_error_code)?;
let pk = sk.public_key().map_err(display_error_code)?;
assert_eq!(pk.verify((&s.0, s.1), TEST_HASH), true);
let s = sk.sign(TEST_HASH).map_err(display_error_code)?;
assert_eq!(pk.verify((&s.0, s.1), TEST_HASH), true);
}
#[test]
fn ecdsa_brainpool512r1() {
let mut sk = BrainpoolP512R1::new();
sk.set_constant_key();
let s = sk
.deterministic_sign(TEST_HASH)
.map_err(display_error_code)?;
let pk = sk.public_key().map_err(display_error_code)?;
assert_eq!(pk.verify((&s.0, s.1), TEST_HASH), true);
let s = sk.sign(TEST_HASH).map_err(display_error_code)?;
assert_eq!(pk.verify((&s.0, s.1), TEST_HASH), true);
}
#[test]
fn ecdsa_stark256() {
let sk = Stark256::derive_from_path(&PATH0);
let s = sk
.deterministic_sign(TEST_HASH)
.map_err(display_error_code)?;
let pk = sk.public_key().map_err(display_error_code)?;
assert_eq!(pk.verify((&s.0, s.1), TEST_HASH), true);
let s = sk.sign(TEST_HASH).map_err(display_error_code)?;
assert_eq!(pk.verify((&s.0, s.1), TEST_HASH), true);
}
#[test]
fn eddsa_ed25519() {
let sk = Ed25519::derive_from_path(&PATH0);
let s = sk.sign(TEST_HASH).map_err(display_error_code)?;
let pk = sk.public_key().map_err(display_error_code)?;
assert_eq!(pk.verify((&s.0, s.1), TEST_HASH, CX_SHA512), true);
}
#[test]
fn eddsa_ed25519_slip10() {
let path: [u32; 5] = make_bip32_path(b"m/44'/535348'/0'/0'/1'");
let sk = Ed25519::derive_from_path_slip10(&path);
let s = sk.sign(TEST_HASH).map_err(display_error_code)?;
let pk = sk.public_key().map_err(display_error_code)?;
assert_eq!(pk.verify((&s.0, s.1), TEST_HASH, CX_SHA512), true);
}
#[test]
fn eddsa_ed25519_stream_sign() {
let sk = Ed25519::derive_from_path(&PATH0);
let pk = sk.public_key().map_err(display_error_code)?;
const MSG1: &[u8] = b"test_message1";
const MSG2: &[u8] = b"test_message2";
const MSG3: &[u8] = b"test_message3";
let mut streamer = Ed25519Stream::default();
streamer.init(&sk).map_err(display_error_code)?;
streamer.sign_update(MSG1).unwrap();
streamer.sign_update(MSG2).unwrap();
streamer.sign_update(MSG3).unwrap();
streamer.sign_finalize(&sk).unwrap();
streamer.sign_update(MSG1).unwrap();
streamer.sign_update(MSG2).unwrap();
streamer.sign_update(MSG3).unwrap();
streamer.sign_finalize(&sk).unwrap();
let mut concatenated: [u8; 39] = [0; 39];
concatenated[0..13].copy_from_slice(MSG1);
concatenated[13..26].copy_from_slice(MSG2);
concatenated[26..39].copy_from_slice(MSG3);
assert_eq!(
pk.verify(
(&streamer.signature, streamer.signature.len() as u32),
&concatenated,
CX_SHA512
),
true
);
}
#[test]
fn test_make_bip32_path() {
{
const P: [u32; 1] = make_bip32_path(b"m/1234");
assert_eq!(P, [1234u32]);
}
{
const P: [u32; 2] = make_bip32_path(b"m/1234/5678");
assert_eq!(P, [1234u32, 5678u32]);
}
{
const P: [u32; 3] = make_bip32_path(b"m/1234/5678/91011");
assert_eq!(P, [1234u32, 5678u32, 91011u32]);
}
{
const P: [u32; 4] = make_bip32_path(b"m/1234/5678'/91011/0");
assert_eq!(P, [1234u32, 5678u32 + 0x80000000u32, 91011u32, 0u32]);
}
{
const P: [u32; 2] = make_bip32_path(b"m/1234/5678'");
assert_eq!(P, [1234u32, 5678u32 + 0x80000000u32]);
}
}
#[test]
fn test_ecdh() {
let sk0 = Secp256k1::derive_from_path(&PATH0);
let pk0 = sk0.public_key().map_err(display_error_code)?;
let sk1 = Secp256k1::derive_from_path(&PATH1);
let pk1 = sk1.public_key().map_err(display_error_code)?;
let shared_secret0 = sk1.ecdh(&pk0.pubkey).map_err(display_error_code)?;
let shared_secret1 = sk0.ecdh(&pk1.pubkey).map_err(display_error_code)?;
assert_eq!(shared_secret0, shared_secret1);
}
}