#![no_std]
use micro_ecc_sys as uecc;
mod rng;
pub const SHA256_LENGTH: usize = 32;
pub const PUBLICKEY_LENGTH: usize = 64;
pub const PUBLICKEY_COMPRESSED_LENGTH: usize = 33;
pub const SEED_LENGTH: usize = 32;
pub const SECRETKEY_LENGTH: usize = 32;
pub const SIGNATURE_LENGTH: usize = 64;
#[derive(Copy,Clone,Debug)]
pub struct Error;
pub type Result<T> = core::result::Result<T, Error>;
#[derive(Copy,Clone,Debug,PartialEq)]
pub struct Seed([u8; SECRETKEY_LENGTH]);
#[derive(Copy,Clone,Debug,PartialEq)]
pub struct SecretKey(pub [u8; SECRETKEY_LENGTH]);
#[derive(Copy,Clone/*,Debug,PartialEq*/)]
pub struct PublicKey(pub [u8; PUBLICKEY_LENGTH]);
#[derive(Copy,Clone/*,Debug,PartialEq*/)]
pub struct Keypair {
pub secret: SecretKey,
pub public: PublicKey,
}
#[derive(Copy,Clone/*,Debug*/)]
pub struct Signature(pub [u8; SIGNATURE_LENGTH]);
impl core::cmp::PartialEq<Signature> for Signature {
fn eq(&self, other: &Self) -> bool {
for (l, r) in self.0.iter().zip(other.0.iter()) {
if l != r { return false; }
}
true
}
}
pub fn prehash(message: &[u8]) -> [u8; SHA256_LENGTH] {
use sha2::digest::Digest;
let mut hash = sha2::Sha256::new();
hash.input(message);
let data = hash.result();
let mut prehashed = [0u8; SHA256_LENGTH];
prehashed.copy_from_slice(data.as_slice());
prehashed
}
impl Keypair {
fn curve() -> uecc::uECC_Curve {
unsafe { uecc::uECC_secp256r1() }
}
pub fn sign(&self, message: &[u8]) -> Signature {
use sha2::digest::Digest;
let mut hash = sha2::Sha256::new();
hash.input(message);
let data = hash.result();
let mut prehashed_message = [0u8; 32];
prehashed_message.copy_from_slice(data.as_slice());
self.sign_prehashed(&prehashed_message)
}
pub fn sign_prehashed(&self, prehashed_message: &[u8; SHA256_LENGTH]) -> Signature {
debug_assert!(prehashed_message.len() == SHA256_LENGTH);
let mut signature = Signature([0u8; SIGNATURE_LENGTH]);
let mut tmp = [0u8; 128];
let hash_context = uecc::uECC_HashContext {
init_hash: Some(uecc_init_hash),
update_hash: Some(uecc_update_hash),
finish_hash: Some(uecc_finish_hash),
block_size: 64,
result_size: 32,
tmp: &mut tmp[0], };
use sha2::digest::Digest;
#[allow(unused_variables)]
let sha_context = ShaHashContext {
context: hash_context,
sha: sha2::Sha256::new(),
};
debug_assert!(unsafe { uecc::uECC_get_rng() }.is_none());
let return_code = unsafe {
uecc::uECC_sign_deterministic(
&self.secret.0[0], &prehashed_message[0],
prehashed_message.len() as u32,
&hash_context,
&mut signature.0[0], Self::curve(),
)
};
assert_eq!(return_code, 1);
signature
}
pub fn verify(&self, message: &[u8], signature: &Signature) -> bool {
self.public.verify(message, signature)
}
pub fn verify_prehashed(&self, prehashed_message: &[u8; 32], signature: &Signature) -> bool {
self.public.verify_prehashed(prehashed_message, signature)
}
}
impl From<&[u8; SEED_LENGTH]> for Keypair {
fn from(seed: &[u8; SECRETKEY_LENGTH]) -> Self {
let mut keypair = Self { secret: SecretKey([0u8; 32]), public: PublicKey([0u8; 64]) };
let rng = rng::ChaCha20Rng::new(seed);
unsafe {
rng::RNG.replace(rng);
uecc::uECC_set_rng(Some(rng::chacha_rng));
}
let return_code = unsafe {
uecc::uECC_make_key(
&mut keypair.public.0[0] as *mut u8,
&mut keypair.secret.0[0] as *mut u8,
Self::curve(),
)
};
unsafe {
uecc::uECC_set_rng(None);
rng::RNG.take();
};
debug_assert!(return_code == 1);
keypair
}
}
impl PublicKey {
pub fn verify_prehashed(&self, prehashed_message: &[u8; 32], signature: &Signature) -> bool {
let return_code = unsafe {
uecc::uECC_verify(
&self.0[0], &prehashed_message[0],
prehashed_message.len() as u32,
&signature.0[0], Keypair::curve(),
)
};
return_code == 1
}
pub fn verify(&self, message: &[u8], signature: &Signature) -> bool {
let prehashed_message = prehash(message);
self.verify_prehashed(&prehashed_message, signature)
}
pub fn compress(&self) -> [u8; PUBLICKEY_COMPRESSED_LENGTH] {
let mut compressed = [0u8; PUBLICKEY_COMPRESSED_LENGTH];
unsafe {
uecc::uECC_compress(
&self.0[0], &mut compressed[0],
Keypair::curve(),
)
};
compressed
}
}
#[repr(C)]
struct ShaHashContext {
context: uecc::uECC_HashContext,
sha: sha2::Sha256,
}
extern "C" fn uecc_init_hash(context: *const uecc::uECC_HashContext) {
let sha2 = unsafe { &mut(*(context as *mut ShaHashContext)).sha };
use sha2::digest::Reset;
sha2.reset();
}
extern "C" fn uecc_update_hash(context: *const uecc::uECC_HashContext, message: *const u8, message_size: u32) {
let sha2 = unsafe { &mut(*(context as *mut ShaHashContext)).sha };
let buf = unsafe { core::slice::from_raw_parts(message, message_size as usize) } ;
use sha2::digest::Input;
sha2.input(&buf);
}
static mut HASHES: u32 = 0;
pub fn hash_calls() -> u32 {
unsafe { HASHES }
}
extern "C" fn uecc_finish_hash(context: *const uecc::uECC_HashContext, hash_result: *mut u8) {
let sha2 = unsafe { &mut(*(context as *mut ShaHashContext)).sha };
use sha2::digest::Digest;
let data = sha2.result_reset();
let result = unsafe { core::slice::from_raw_parts_mut(hash_result, SHA256_LENGTH) } ;
result.copy_from_slice(&data);
unsafe { HASHES += 1 };
}