use crate::{
key::SharedSecret,
memsec::{self, Scrubbed as _},
};
use cryptoxide::ed25519;
use packtool::Packed;
use rand_core::{CryptoRng, RngCore};
use std::{
cmp::Ordering,
convert::TryFrom,
fmt::{self, Debug, Display, Formatter},
hash::{Hash, Hasher},
str::FromStr,
};
use thiserror::Error;
#[derive(Clone)]
pub struct SecretKey([u8; Self::SIZE]);
#[derive(Packed, Clone, Copy)]
pub struct PublicKey(#[packed(accessor = false)] [u8; Self::SIZE]);
#[derive(Packed, Clone, Copy)]
pub struct Signature(#[packed(accessor = false)] [u8; Self::SIZE]);
impl SecretKey {
pub const SIZE: usize = ed25519::PRIVATE_KEY_LENGTH;
#[inline(always)]
const fn zero() -> Self {
Self([0; Self::SIZE])
}
pub fn new<Rng>(mut rng: Rng) -> Self
where
Rng: RngCore + CryptoRng,
{
let mut s = Self::zero();
rng.fill_bytes(&mut s.0);
s
}
pub fn exchange(&self, public_key: &PublicKey) -> SharedSecret {
SharedSecret::new(ed25519::exchange(public_key.bytes(), &self.0))
}
pub fn public_key(&self) -> PublicKey {
let (mut sk, pk) = ed25519::keypair(&self.0);
sk.scrub();
PublicKey(pk)
}
pub fn sign<T: AsRef<[u8]>>(&self, msg: T) -> Signature {
let (mut sk, _) = ed25519::keypair(&self.0);
let signature = ed25519::signature(msg.as_ref(), &sk);
sk.scrub();
Signature(signature)
}
pub fn leak_as_ref(&self) -> &[u8; Self::SIZE] {
&self.0
}
}
impl PublicKey {
pub const SIZE: usize = ed25519::PUBLIC_KEY_LENGTH;
#[inline(always)]
const fn zero() -> Self {
Self([0; Self::SIZE])
}
pub fn verify<T: AsRef<[u8]>>(&self, msg: T, signature: &Signature) -> bool {
ed25519::verify(msg.as_ref(), &self.0, &signature.0)
}
pub fn bytes(&self) -> &[u8; 32] {
&self.0
}
}
impl Signature {
pub const SIZE: usize = ed25519::SIGNATURE_LENGTH;
#[inline(always)]
const fn zero() -> Self {
Self([0; Self::SIZE])
}
}
impl Display for Signature {
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
Display::fmt(&hex::encode(self.as_ref()), f)
}
}
impl Display for PublicKey {
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
Display::fmt(&hex::encode(self.as_ref()), f)
}
}
impl Debug for Signature {
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
f.debug_tuple("Signature<Ed25519>")
.field(&hex::encode(self.as_ref()))
.finish()
}
}
impl Debug for PublicKey {
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
f.debug_tuple("PublicKey<Ed25519>")
.field(&hex::encode(self.as_ref()))
.finish()
}
}
#[cfg(test)]
impl Debug for SecretKey {
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
f.debug_tuple("SecretKey<Ed25519>")
.field(&hex::encode(&self.0))
.finish()
}
}
#[cfg(not(test))]
impl Debug for SecretKey {
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
f.debug_tuple("SecretKey<Ed25519>").field(&"...").finish()
}
}
impl<'a> From<&'a Signature> for String {
fn from(s: &'a Signature) -> Self {
s.to_string()
}
}
impl From<Signature> for String {
fn from(s: Signature) -> Self {
s.to_string()
}
}
impl From<[u8; Self::SIZE]> for SecretKey {
fn from(bytes: [u8; Self::SIZE]) -> Self {
Self(bytes)
}
}
impl From<[u8; Self::SIZE]> for PublicKey {
fn from(bytes: [u8; Self::SIZE]) -> Self {
Self(bytes)
}
}
impl From<PublicKey> for [u8; PublicKey::SIZE] {
fn from(pk: PublicKey) -> Self {
pk.0
}
}
impl From<[u8; Self::SIZE]> for Signature {
fn from(bytes: [u8; Self::SIZE]) -> Self {
Self(bytes)
}
}
#[derive(Debug, Error)]
pub enum SecretKeyError {
#[error("Invalid size, expecting {}", SecretKey::SIZE)]
InvalidSize,
}
#[derive(Debug, Error)]
pub enum PublicKeyError {
#[error("Invalid size, expecting {}", PublicKey::SIZE)]
InvalidSize,
}
#[derive(Debug, Error)]
pub enum SignatureError {
#[error("Invalid size, expecting {}", PublicKey::SIZE)]
InvalidSize,
}
impl<'a> TryFrom<&'a [u8]> for SecretKey {
type Error = SecretKeyError;
fn try_from(value: &'a [u8]) -> Result<Self, Self::Error> {
if value.len() != Self::SIZE {
Err(Self::Error::InvalidSize)
} else {
let mut s = Self::zero();
s.0.copy_from_slice(value);
Ok(s)
}
}
}
impl<'a> TryFrom<&'a [u8]> for PublicKey {
type Error = PublicKeyError;
fn try_from(value: &'a [u8]) -> Result<Self, Self::Error> {
if value.len() != Self::SIZE {
Err(Self::Error::InvalidSize)
} else {
let mut s = Self::zero();
s.0.copy_from_slice(value);
Ok(s)
}
}
}
impl<'a> TryFrom<&'a [u8]> for Signature {
type Error = SignatureError;
fn try_from(value: &'a [u8]) -> Result<Self, Self::Error> {
if value.len() != Self::SIZE {
Err(Self::Error::InvalidSize)
} else {
let mut s = Self::zero();
s.0.copy_from_slice(value);
Ok(s)
}
}
}
impl FromStr for SecretKey {
type Err = hex::FromHexError;
fn from_str(s: &str) -> Result<Self, Self::Err> {
let mut r = Self::zero();
hex::decode_to_slice(s, &mut r.0)?;
Ok(r)
}
}
impl FromStr for PublicKey {
type Err = hex::FromHexError;
fn from_str(s: &str) -> Result<Self, Self::Err> {
let mut r = Self::zero();
hex::decode_to_slice(s, &mut r.0)?;
Ok(r)
}
}
impl FromStr for Signature {
type Err = hex::FromHexError;
fn from_str(s: &str) -> Result<Self, Self::Err> {
let mut r = Self::zero();
hex::decode_to_slice(s, &mut r.0)?;
Ok(r)
}
}
impl<'a> TryFrom<&'a str> for Signature {
type Error = <Self as FromStr>::Err;
fn try_from(s: &'a str) -> Result<Self, Self::Error> {
s.parse()
}
}
impl PartialEq<Self> for Signature {
fn eq(&self, other: &Self) -> bool {
unsafe { memsec::memeq(self.0.as_ptr(), other.0.as_ptr(), Self::SIZE) }
}
}
impl PartialEq<Self> for PublicKey {
fn eq(&self, other: &Self) -> bool {
unsafe { memsec::memeq(self.0.as_ptr(), other.0.as_ptr(), Self::SIZE) }
}
}
impl PartialEq<Self> for SecretKey {
fn eq(&self, other: &Self) -> bool {
unsafe { memsec::memeq(self.0.as_ptr(), other.0.as_ptr(), Self::SIZE) }
}
}
impl Eq for Signature {}
impl Eq for PublicKey {}
impl Eq for SecretKey {}
impl PartialOrd<Self> for Signature {
fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
Some(self.cmp(other))
}
}
impl PartialOrd<Self> for PublicKey {
fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
Some(self.cmp(other))
}
}
impl Ord for Signature {
fn cmp(&self, other: &Self) -> Ordering {
unsafe { memsec::memcmp(self.0.as_ptr(), other.0.as_ptr(), Self::SIZE) }
}
}
impl Ord for PublicKey {
fn cmp(&self, other: &Self) -> Ordering {
unsafe { memsec::memcmp(self.0.as_ptr(), other.0.as_ptr(), Self::SIZE) }
}
}
impl Hash for Signature {
fn hash<H: Hasher>(&self, state: &mut H) {
self.as_ref().hash(state)
}
}
impl Hash for PublicKey {
fn hash<H: Hasher>(&self, state: &mut H) {
self.as_ref().hash(state)
}
}
impl Hash for SecretKey {
fn hash<H: Hasher>(&self, state: &mut H) {
self.0.as_ref().hash(state)
}
}
impl AsRef<[u8]> for PublicKey {
fn as_ref(&self) -> &[u8] {
self.0.as_ref()
}
}
impl AsRef<[u8]> for Signature {
fn as_ref(&self) -> &[u8] {
self.0.as_ref()
}
}
impl Drop for SecretKey {
fn drop(&mut self) {
self.0.scrub()
}
}
#[cfg(test)]
mod tests {
use super::*;
use quickcheck::{Arbitrary, Gen, TestResult};
impl Arbitrary for SecretKey {
fn arbitrary(g: &mut Gen) -> Self {
let mut s = Self::zero();
s.0.iter_mut().for_each(|byte| {
*byte = u8::arbitrary(g);
});
s
}
}
impl Arbitrary for PublicKey {
fn arbitrary(g: &mut Gen) -> Self {
let mut s = Self::zero();
s.0.iter_mut().for_each(|byte| {
*byte = u8::arbitrary(g);
});
s
}
}
impl Arbitrary for Signature {
fn arbitrary(g: &mut Gen) -> Self {
let mut s = Self::zero();
s.0.iter_mut().for_each(|byte| {
*byte = u8::arbitrary(g);
});
s
}
}
#[quickcheck]
fn verify_exchange_works(alice: SecretKey, bob: SecretKey) -> bool {
let alice_pk = alice.public_key();
let bob_pk = bob.public_key();
alice.exchange(&bob_pk) == bob.exchange(&alice_pk)
}
#[quickcheck]
fn signing_verify_works(signing_key: SecretKey, message: Vec<u8>) -> bool {
let public_key = signing_key.public_key();
let signature = signing_key.sign(&message);
public_key.verify(message, &signature)
}
#[quickcheck]
fn verify_random_signature_does_not_work(
public_key: PublicKey,
signature: Signature,
message: Vec<u8>,
) -> bool {
!public_key.verify(message, &signature)
}
#[quickcheck]
fn signing_key_try_from_correct_size(signing_key: SecretKey) -> TestResult {
match SecretKey::try_from(signing_key.leak_as_ref().as_ref()) {
Ok(_) => TestResult::passed(),
Err(SecretKeyError::InvalidSize) => TestResult::error("was expecting the test to pass"),
}
}
#[quickcheck]
fn signing_key_try_from_incorrect_size(bytes: Vec<u8>) -> TestResult {
if bytes.len() == SecretKey::SIZE {
return TestResult::discard();
}
match SecretKey::try_from(bytes.as_slice()) {
Ok(_) => TestResult::error(
"Expecting to fail with invalid size instead of having a valid value",
),
Err(SecretKeyError::InvalidSize) => TestResult::passed(),
}
}
#[quickcheck]
fn public_key_try_from_correct_size(public_key: PublicKey) -> TestResult {
match PublicKey::try_from(public_key.as_ref()) {
Ok(_) => TestResult::passed(),
Err(PublicKeyError::InvalidSize) => TestResult::error("was expecting the test to pass"),
}
}
#[quickcheck]
fn public_key_try_from_incorrect_size(bytes: Vec<u8>) -> TestResult {
if bytes.len() == PublicKey::SIZE {
return TestResult::discard();
}
match PublicKey::try_from(bytes.as_slice()) {
Ok(_) => TestResult::error(
"Expecting to fail with invalid size instead of having a valid value",
),
Err(PublicKeyError::InvalidSize) => TestResult::passed(),
}
}
#[quickcheck]
fn signature_try_from_correct_size(signature: Signature) -> TestResult {
match Signature::try_from(signature.as_ref()) {
Ok(_) => TestResult::passed(),
Err(SignatureError::InvalidSize) => TestResult::error("was expecting the test to pass"),
}
}
#[quickcheck]
fn signature_try_from_incorrect_size(bytes: Vec<u8>) -> TestResult {
if bytes.len() == Signature::SIZE {
return TestResult::discard();
}
match Signature::try_from(bytes.as_slice()) {
Ok(_) => TestResult::error(
"Expecting to fail with invalid size instead of having a valid value",
),
Err(SignatureError::InvalidSize) => TestResult::passed(),
}
}
#[quickcheck]
fn public_key_from_str(public_key: PublicKey) -> TestResult {
let s = public_key.to_string();
match s.parse::<PublicKey>() {
Ok(decoded) => {
if decoded == public_key {
TestResult::passed()
} else {
TestResult::error("the decoded key is not equal")
}
}
Err(error) => TestResult::error(error.to_string()),
}
}
#[quickcheck]
fn signature_from_str(signature: Signature) -> TestResult {
let s = signature.to_string();
match s.parse::<Signature>() {
Ok(decoded) => {
if decoded == signature {
TestResult::passed()
} else {
TestResult::error("the decoded signature is not equal")
}
}
Err(error) => TestResult::error(error.to_string()),
}
}
}