#![no_std]
use micro_ecc_sys as uecc;
#[cfg(feature = "cose")]
pub use cosey::P256PublicKey as CosePublicKey;
#[cfg(feature = "asn1-der")]
pub use asn1derpy::{Bytes, consts::U72};
#[cfg(feature = "logging")]
use funnel::info;
fn nist_p256() -> uecc::uECC_Curve {
unsafe { uecc::uECC_secp256r1() }
}
pub const DIGEST_LENGTH: usize = 32;
pub const PUBLIC_KEY_LENGTH: usize = 64;
pub const PUBLIC_KEY_X_COORDINATES_LENGTH: usize = 32;
pub const PUBLIC_KEY_Y_COORDINATES_LENGTH: usize = 32;
pub const PUBLIC_KEY_COMPRESSED_LENGTH: usize = 33;
pub const SEED_LENGTH: usize = 32;
pub const SHARED_SECRET_LENGTH: usize = 32;
pub const SECRET_KEY_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>;
pub trait AsArrayRef<T> where
T: ?Sized,
{
fn as_array_ref(&self) -> &T;
}
impl AsArrayRef<[u8; 32]> for [u8; 32] {
fn as_array_ref(&self) -> &[u8; 32] {
&self
}
}
impl AsArrayRef<[u8; 32]> for &[u8; 32] {
fn as_array_ref(&self) -> &[u8; 32] {
self
}
}
impl AsArrayRef<[u8; 64]> for [u8; 64] {
fn as_array_ref(&self) -> &[u8; 64] {
&self
}
}
impl AsArrayRef<[u8; 64]> for &[u8; 64] {
fn as_array_ref(&self) -> &[u8; 64] {
self
}
}
type SeedBytes = [u8; SEED_LENGTH];
#[derive(Copy,Clone,Debug,PartialEq)]
pub struct Seed(SeedBytes);
impl From<SeedBytes> for Seed {
fn from(seed_bytes: SeedBytes) -> Seed {
Seed(seed_bytes)
}
}
impl Into<SeedBytes> for Seed {
fn into(self) -> SeedBytes {
self.0
}
}
impl AsArrayRef<SeedBytes> for Seed {
fn as_array_ref(&self) -> &SeedBytes {
&self.0
}
}
impl AsArrayRef<SeedBytes> for &Seed {
fn as_array_ref(&self) -> &SeedBytes {
&self.0
}
}
impl Seed {
pub fn from_bytes(seed_bytes: SeedBytes) -> Self {
Self::from(seed_bytes)
}
pub fn to_bytes(self) -> SeedBytes {
self.into()
}
pub fn as_bytes(&self) -> &SeedBytes {
self.as_array_ref()
}
}
type SharedSecretBytes = [u8; SHARED_SECRET_LENGTH];
#[derive(Copy,Clone,Debug,PartialEq)]
pub struct SharedSecret(SharedSecretBytes);
impl From<SharedSecretBytes> for SharedSecret {
fn from(shared_secret_bytes: SharedSecretBytes) -> SharedSecret {
SharedSecret(shared_secret_bytes)
}
}
impl Into<SharedSecretBytes> for SharedSecret {
fn into(self) -> SharedSecretBytes {
self.0
}
}
impl AsArrayRef<SharedSecretBytes> for SharedSecret {
fn as_array_ref(&self) -> &SharedSecretBytes {
&self.0
}
}
impl AsArrayRef<SharedSecretBytes> for &SharedSecret {
fn as_array_ref(&self) -> &SharedSecretBytes {
&self.0
}
}
impl SharedSecret {
pub fn from_bytes(shared_secret_bytes: SharedSecretBytes) -> Self {
Self::from(shared_secret_bytes)
}
pub fn to_bytes(self) -> SharedSecretBytes {
self.into()
}
pub fn as_bytes(&self) -> &SharedSecretBytes {
self.as_array_ref()
}
}
type SecretKeyBytes = [u8; SECRET_KEY_LENGTH];
#[derive(Clone,Debug,PartialEq)]
pub struct SecretKey(SecretKeyBytes);
impl core::convert::TryFrom<&SecretKeyBytes> for SecretKey {
type Error = Error;
fn try_from(secret_key_bytes: &SecretKeyBytes) -> Result<SecretKey> {
Ok(Keypair::try_from(secret_key_bytes)?.secret)
}
}
impl Into<SecretKeyBytes> for SecretKey {
fn into(self) -> SecretKeyBytes {
self.0
}
}
impl AsArrayRef<SecretKeyBytes> for SecretKey {
fn as_array_ref(&self) -> &SecretKeyBytes {
&self.0
}
}
impl AsArrayRef<SecretKeyBytes> for &SecretKey {
fn as_array_ref(&self) -> &SecretKeyBytes {
&self.0
}
}
impl SecretKey {
pub fn sign_with(secret_bytes: SecretKeyBytes, message: &[u8]) -> Signature {
let secret_key = SecretKey(secret_bytes);
let prehashed_message = prehash(message);
secret_key.sign_prehashed(&prehashed_message)
}
pub fn sign(&self, message: &[u8]) -> Signature {
let prehashed_message = prehash(message);
self.sign_prehashed(&prehashed_message)
}
pub fn sign_prehashed(&self, prehashed_message: &[u8; DIGEST_LENGTH]) -> Signature {
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(),
};
unsafe { uecc::uECC_set_rng(None) };
let return_code = unsafe {
uecc::uECC_sign_deterministic(
&self.0[0],
&prehashed_message[0],
prehashed_message.len() as u32,
&hash_context,
&mut signature.0[0],
nist_p256(),
)
};
assert_eq!(return_code, 1);
signature
}
}
impl SecretKey {
pub fn agree(&self, public_key: &PublicKey) -> Result<SharedSecret> {
let mut shared_secret = SharedSecret([0u8; SHARED_SECRET_LENGTH]);
unsafe { uecc::uECC_set_rng(None) };
let return_code = unsafe {
uecc::uECC_shared_secret(
&public_key.0[0],
&self.0[0],
&mut shared_secret.0[0],
nist_p256(),
)
};
if return_code == 1 {
Ok(shared_secret)
} else {
Err(Error)
}
}
}
impl SecretKey {
pub fn try_from_bytes(secret_key_bytes: &SecretKeyBytes) -> Result<Self> {
use core::convert::TryFrom;
Self::try_from(secret_key_bytes)
}
pub fn to_bytes(self) -> SecretKeyBytes {
self.into()
}
pub fn as_bytes(&self) -> &SecretKeyBytes {
self.as_array_ref()
}
}
type PublicKeyBytes = [u8; PUBLIC_KEY_LENGTH];
type PublicKeyXCoordinate = [u8; PUBLIC_KEY_X_COORDINATES_LENGTH];
type PublicKeyYCoordinate = [u8; PUBLIC_KEY_Y_COORDINATES_LENGTH];
#[derive(Copy,Clone)]
pub struct PublicKey(PublicKeyBytes);
impl core::fmt::Debug for PublicKey {
fn fmt(&self, formatter: &mut core::fmt::Formatter) -> core::fmt::Result {
write!(formatter, "PublicKey({:?})", &self.0[..])
}
}
impl PartialEq for PublicKey {
fn eq(&self, other: &PublicKey) -> bool {
self.0[..] == other.0[..]
}
}
impl core::convert::TryFrom<&PublicKeyBytes> for PublicKey {
type Error = Error;
fn try_from(public_key_bytes: &PublicKeyBytes) -> Result<PublicKey> {
let return_code = unsafe {
uecc::uECC_valid_public_key(
&public_key_bytes[0],
nist_p256(),
)
};
if return_code == 1 {
Ok(PublicKey(*public_key_bytes))
} else {
Err(Error)
}
}
}
impl Into<PublicKeyBytes> for PublicKey {
fn into(self) -> PublicKeyBytes {
self.0
}
}
impl AsArrayRef<PublicKeyBytes> for PublicKey {
fn as_array_ref(&self) -> &PublicKeyBytes {
&self.0
}
}
impl AsArrayRef<PublicKeyBytes> for &PublicKey {
fn as_array_ref(&self) -> &PublicKeyBytes {
&self.0
}
}
impl PublicKey {
pub fn verify(&self, message: &[u8], signature: impl AsArrayRef<SignatureBytes>) -> bool {
let prehashed_message = prehash(message);
self.verify_prehashed(&prehashed_message, signature)
}
pub fn verify_prehashed(&self, prehashed_message: &[u8; DIGEST_LENGTH], signature: impl AsArrayRef<SignatureBytes>) -> bool {
let return_code = unsafe {
uecc::uECC_verify(
&self.0[0],
&prehashed_message[0],
DIGEST_LENGTH as u32,
&signature.as_array_ref()[0],
nist_p256(),
)
};
return_code == 1
}
pub fn compress(&self) -> [u8; PUBLIC_KEY_COMPRESSED_LENGTH] {
let mut compressed = [0u8; PUBLIC_KEY_COMPRESSED_LENGTH];
compressed[0] = 0x2 + (self.0[self.0.len() - 1] & 0x1);
compressed[1..].copy_from_slice(&self.0);
compressed
}
}
impl PublicKey {
pub fn x_coordinate(&self) -> PublicKeyXCoordinate {
let mut x = [0u8; PUBLIC_KEY_X_COORDINATES_LENGTH];
x.copy_from_slice(&self.0[..32]);
x
}
pub fn y_coordinate(&self) -> PublicKeyYCoordinate {
let mut y = [0u8; PUBLIC_KEY_X_COORDINATES_LENGTH];
y.copy_from_slice(&self.0[32..]);
y
}
}
#[cfg(feature = "cose")]
impl Into<CosePublicKey> for PublicKey {
fn into(self) -> CosePublicKey {
CosePublicKey {
x: cosey::ByteBuf::try_from_slice(&self.x_coordinate()).unwrap(),
y: cosey::ByteBuf::try_from_slice(&self.y_coordinate()).unwrap(),
}
}
}
impl PublicKey {
pub fn try_from_bytes(public_key_bytes: &PublicKeyBytes) -> Result<Self> {
use core::convert::TryFrom;
Self::try_from(public_key_bytes)
}
pub fn to_bytes(self) -> PublicKeyBytes {
self.into()
}
pub fn as_bytes(&self) -> &PublicKeyBytes {
self.as_array_ref()
}
}
#[derive(Clone,Debug,PartialEq)]
pub struct Keypair {
pub secret: SecretKey,
pub public: PublicKey,
}
type SignatureBytes = [u8; SIGNATURE_LENGTH];
#[derive(Copy,Clone)]
pub struct Signature(SignatureBytes);
impl core::fmt::Debug for Signature {
fn fmt(&self, formatter: &mut core::fmt::Formatter) -> core::fmt::Result {
write!(formatter, "Signature({:?})", &self.0[..])
}
}
impl PartialEq for Signature {
fn eq(&self, other: &Signature) -> bool {
self.0[..] == other.0[..]
}
}
impl From<SignatureBytes> for Signature {
fn from(signature_bytes: SignatureBytes) -> Signature {
Signature(signature_bytes)
}
}
impl Into<SignatureBytes> for Signature {
fn into(self) -> SignatureBytes {
self.0
}
}
impl AsArrayRef<SignatureBytes> for Signature {
fn as_array_ref(&self) -> &SignatureBytes {
&self.0
}
}
impl AsArrayRef<SignatureBytes> for &Signature {
fn as_array_ref(&self) -> &SignatureBytes {
&self.0
}
}
impl Signature {
pub fn from_bytes(signature_bytes: SignatureBytes) -> Self {
Self::from(signature_bytes)
}
pub fn to_bytes(self) -> SignatureBytes {
self.into()
}
pub fn as_bytes(&self) -> &SignatureBytes {
self.as_array_ref()
}
#[cfg(feature = "asn1-der")]
pub fn to_asn1_der(&self) -> Bytes::<U72> {
let r = &self.0[..32];
let s = &self.0[32..];
let mut der = asn1derpy::Der::<U72>::new();
der.sequence(|der| Ok({
der.non_negative_integer(r)?;
der.non_negative_integer(s)?;
})).unwrap();
der.into_inner()
}
}
pub fn prehash(message: &[u8]) -> [u8; DIGEST_LENGTH] {
use sha2::digest::Digest;
let mut hash = sha2::Sha256::new();
hash.input(message);
let data = hash.result();
data.into()
}
impl Keypair {
pub fn generate(seed: impl AsArrayRef<SeedBytes>, tries: usize) -> Result<Keypair> {
let mut secret = <[u8; SECRET_KEY_LENGTH]>::from(*seed.as_array_ref());
use core::convert::TryFrom;
for _attempt in 0..tries {
let candidate = Keypair::try_from(&secret);
if candidate.is_ok() {
return candidate;
}
secret = prehash(&secret);
}
Err(Error)
}
pub fn generate_patiently(seed: impl AsArrayRef<SeedBytes>) -> Keypair {
let mut secret = <[u8; SECRET_KEY_LENGTH]>::from(*seed.as_array_ref());
use core::convert::TryFrom;
let mut _i: usize = 0;
loop {
let candidate = Keypair::try_from(&secret);
_i += 1;
match candidate {
Ok(keypair) => {
#[cfg(feature = "logging")]
info!("was patient {} times", _i).ok();
return keypair;
}
_ => {},
};
secret = prehash(&secret);
}
}
pub fn try_from_bytes(secret_key_bytes: &SecretKeyBytes) -> Result<Keypair> {
use core::convert::TryFrom;
Keypair::try_from(secret_key_bytes)
}
pub fn split(self) -> (SecretKey, PublicKey) {
(self.secret, self.public)
}
pub fn sign(&self, message: &[u8]) -> Signature {
self.secret.sign(message)
}
pub fn sign_prehashed(&self, prehashed_message: &[u8; DIGEST_LENGTH]) -> Signature {
self.secret.sign_prehashed(prehashed_message)
}
pub fn verify(&self, message: &[u8], signature: impl AsArrayRef<SignatureBytes>) -> bool {
self.public.verify(message, signature)
}
pub fn verify_prehashed(&self, prehashed_message: &[u8; DIGEST_LENGTH], signature: impl AsArrayRef<SignatureBytes>) -> bool {
self.public.verify_prehashed(prehashed_message, signature)
}
}
impl core::convert::TryFrom<&SecretKeyBytes> for Keypair {
type Error = Error;
fn try_from(secret_key_bytes: &SecretKeyBytes) -> Result<Self> {
let mut keypair = Self {
secret: SecretKey(<[u8; SECRET_KEY_LENGTH]>::from(*secret_key_bytes)),
public: PublicKey([0u8; PUBLIC_KEY_LENGTH]),
};
let return_code = unsafe {
uecc::uECC_compute_public_key(
&mut keypair.secret.0[0],
&mut keypair.public.0[0],
nist_p256(),
)
};
if return_code == 1 {
Ok(keypair)
} else {
Err(Error)
}
}
}
impl From<&SecretKey> for Keypair {
fn from(secret_key: &SecretKey) -> Self {
let public_key = PublicKey::from(secret_key);
Keypair {
secret: secret_key.clone(),
public: public_key,
}
}
}
impl From<&SecretKey> for PublicKey {
fn from(secret_key: &SecretKey) -> Self {
let mut keypair = Keypair {
secret: secret_key.clone(),
public: PublicKey([0u8; PUBLIC_KEY_LENGTH]),
};
let return_code = unsafe {
uecc::uECC_compute_public_key(
&mut keypair.secret.0[0],
&mut keypair.public.0[0],
nist_p256(),
)
};
debug_assert!(return_code == 1);
keypair.public
}
}
#[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 unsafe fn hash_calls() -> u32 {
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, DIGEST_LENGTH) } ;
result.copy_from_slice(&data);
unsafe { HASHES += 1 };
}