use ledger_secure_sdk_sys::*;
use zeroize::Zeroize;
pub mod edwards;
pub use edwards::*;
pub mod math;
pub use math::*;
pub mod montgomery;
pub use montgomery::*;
pub mod weierstrass;
pub use weierstrass::*;
#[repr(u8)]
#[derive(Copy, Clone)]
pub enum CurvesId {
Secp256k1 = ledger_secure_sdk_sys::CX_CURVE_SECP256K1,
Secp256r1 = ledger_secure_sdk_sys::CX_CURVE_SECP256R1,
Secp384r1 = ledger_secure_sdk_sys::CX_CURVE_SECP384R1,
BrainpoolP256T1 = ledger_secure_sdk_sys::CX_CURVE_BrainPoolP256T1,
BrainpoolP256R1 = ledger_secure_sdk_sys::CX_CURVE_BrainPoolP256R1,
BrainpoolP320R1 = ledger_secure_sdk_sys::CX_CURVE_BrainPoolP320R1,
BrainpoolP320T1 = ledger_secure_sdk_sys::CX_CURVE_BrainPoolP320T1,
BrainpoolP384T1 = ledger_secure_sdk_sys::CX_CURVE_BrainPoolP384T1,
BrainpoolP384R1 = ledger_secure_sdk_sys::CX_CURVE_BrainPoolP384R1,
BrainpoolP512T1 = ledger_secure_sdk_sys::CX_CURVE_BrainPoolP512T1,
BrainpoolP512R1 = ledger_secure_sdk_sys::CX_CURVE_BrainPoolP512R1,
Bls12381G1 = ledger_secure_sdk_sys::CX_CURVE_BLS12_381_G1,
FRP256v1 = ledger_secure_sdk_sys::CX_CURVE_FRP256V1, Stark256 = ledger_secure_sdk_sys::CX_CURVE_Stark256,
Bls12377G1 = ledger_secure_sdk_sys::CX_CURVE_BLS12_377_G1,
Pallas = ledger_secure_sdk_sys::CX_CURVE_PALLAS,
Vesta = ledger_secure_sdk_sys::CX_CURVE_VESTA,
Ed25519 = ledger_secure_sdk_sys::CX_CURVE_Ed25519,
Ed448 = ledger_secure_sdk_sys::CX_CURVE_Ed448,
EdBLS12 = ledger_secure_sdk_sys::CX_CURVE_EdBLS12,
JubJub = ledger_secure_sdk_sys::CX_CURVE_JUBJUB,
Curve25519 = ledger_secure_sdk_sys::CX_CURVE_Curve25519,
Curve448 = ledger_secure_sdk_sys::CX_CURVE_Curve448,
Secp521r1 = ledger_secure_sdk_sys::CX_CURVE_SECP521R1,
Invalid,
}
impl From<u8> for CurvesId {
fn from(x: u8) -> CurvesId {
match x {
ledger_secure_sdk_sys::CX_CURVE_SECP256K1 => CurvesId::Secp256k1,
ledger_secure_sdk_sys::CX_CURVE_SECP256R1 => CurvesId::Secp256r1,
ledger_secure_sdk_sys::CX_CURVE_SECP384R1 => CurvesId::Secp384r1,
ledger_secure_sdk_sys::CX_CURVE_BrainPoolP256T1 => CurvesId::BrainpoolP256T1,
ledger_secure_sdk_sys::CX_CURVE_BrainPoolP256R1 => CurvesId::BrainpoolP256R1,
ledger_secure_sdk_sys::CX_CURVE_BrainPoolP320R1 => CurvesId::BrainpoolP320R1,
ledger_secure_sdk_sys::CX_CURVE_BrainPoolP320T1 => CurvesId::BrainpoolP320T1,
ledger_secure_sdk_sys::CX_CURVE_BrainPoolP384T1 => CurvesId::BrainpoolP384T1,
ledger_secure_sdk_sys::CX_CURVE_BrainPoolP384R1 => CurvesId::BrainpoolP384R1,
ledger_secure_sdk_sys::CX_CURVE_BrainPoolP512T1 => CurvesId::BrainpoolP512T1,
ledger_secure_sdk_sys::CX_CURVE_BrainPoolP512R1 => CurvesId::BrainpoolP512R1,
ledger_secure_sdk_sys::CX_CURVE_BLS12_381_G1 => CurvesId::Bls12381G1,
ledger_secure_sdk_sys::CX_CURVE_FRP256V1 => CurvesId::FRP256v1,
ledger_secure_sdk_sys::CX_CURVE_Stark256 => CurvesId::Stark256,
ledger_secure_sdk_sys::CX_CURVE_BLS12_377_G1 => CurvesId::Bls12377G1,
ledger_secure_sdk_sys::CX_CURVE_PALLAS => CurvesId::Pallas,
ledger_secure_sdk_sys::CX_CURVE_VESTA => CurvesId::Vesta,
ledger_secure_sdk_sys::CX_CURVE_Ed25519 => CurvesId::Ed25519,
ledger_secure_sdk_sys::CX_CURVE_Ed448 => CurvesId::Ed448,
ledger_secure_sdk_sys::CX_CURVE_EdBLS12 => CurvesId::EdBLS12,
ledger_secure_sdk_sys::CX_CURVE_JUBJUB => CurvesId::JubJub,
ledger_secure_sdk_sys::CX_CURVE_Curve25519 => CurvesId::Curve25519,
ledger_secure_sdk_sys::CX_CURVE_Curve448 => CurvesId::Curve448,
ledger_secure_sdk_sys::CX_CURVE_SECP521R1 => CurvesId::Secp521r1,
_ => CurvesId::Invalid,
}
}
}
impl From<CurvesId> for u8 {
fn from(curve: CurvesId) -> u8 {
curve as u8
}
}
#[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)
}
}
}
#[macro_export]
macro_rules! check_cx_ok {
($fn_call:expr) => {{
let err = unsafe { $fn_call };
if err != CX_OK {
return Err(err.into());
}
}};
}
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
}
}
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(())
}
#[cfg_attr(test, derive(PartialEq))]
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
}
}
#[repr(u8)]
#[derive(Copy, Clone)]
pub enum HDKeyDeriveMode {
Bip32 = HDKEY_DERIVE_MODE_NORMAL,
Slip10Ed25519 = HDKEY_DERIVE_MODE_ED25519_SLIP10,
Slip21 = HDKEY_DERIVE_MODE_SLIP21,
Zip32Sapling = HDKEY_DERIVE_MODE_ZIP32_SAPLING,
Zip32Orchard = HDKEY_DERIVE_MODE_ZIP32_ORCHARD,
Zip32Registered = HDKEY_DERIVE_MODE_ZIP32_REGISTERED,
}
#[macro_export]
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
}
}
};
}
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;
#[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]);
}
}
}