add Ed25519 and Ed25519Extended asymmetric keys
This commit is contained in:
parent
b74f46fb4a
commit
1cb1b3e4ab
6 changed files with 774 additions and 2 deletions
|
|
@ -16,4 +16,10 @@ authors = [
|
|||
minicbor = { version = "0.12" }
|
||||
hex = "0.4"
|
||||
cryptoxide = { version = "0.3.6" }
|
||||
thiserror = "1.0"
|
||||
rand_core = "0.6"
|
||||
|
||||
[dev-dependencies]
|
||||
quickcheck = "1.0"
|
||||
quickcheck_macros = "1.0"
|
||||
rand = "0.8"
|
||||
|
|
|
|||
|
|
@ -4,8 +4,8 @@ Crate with all the cryptographic material to support Cardano protocol:
|
|||
|
||||
- [x] Blake2b 256
|
||||
- [x] Blake2b 224
|
||||
- [ ] Ed25519 asymmetric key pair and ECDSA
|
||||
- [ ] Ed25519 Extended asymmetric key pair
|
||||
- [x] Ed25519 asymmetric key pair and EdDSA
|
||||
- [x] Ed25519 Extended asymmetric key pair
|
||||
- [ ] Bip32-Ed25519 key derivation
|
||||
- [ ] BIP39 mnemonics
|
||||
- [ ] VRF
|
||||
|
|
|
|||
528
pallas-crypto/src/key/ed25519.rs
Normal file
528
pallas-crypto/src/key/ed25519.rs
Normal file
|
|
@ -0,0 +1,528 @@
|
|||
//! Ed25519 and Ed25519Extended Asymmetric Keys
|
||||
//!
|
||||
//! In this module we have both [`SecretKey`] which is a normal Ed25519
|
||||
//! asymmetric key and [`SecretKeyExtended`] asymmetric key.
|
||||
//! They can both be used to generate [`Signature`] and submit valid
|
||||
//! transactions.
|
||||
//!
|
||||
//! However, only the [`SecretKeyExtended`] can be used for HD derivation
|
||||
//! (using [ed25519_bip32] or otherwise).
|
||||
//!
|
||||
|
||||
use crate::memsec::Scrubbed as _;
|
||||
use cryptoxide::ed25519::{
|
||||
self, PRIVATE_KEY_LENGTH, PUBLIC_KEY_LENGTH, SEED_LENGTH, SIGNATURE_LENGTH,
|
||||
};
|
||||
use rand_core::{CryptoRng, RngCore};
|
||||
use std::{any::type_name, convert::TryFrom, fmt, str::FromStr};
|
||||
use thiserror::Error;
|
||||
|
||||
/// Ed25519 Secret Key
|
||||
///
|
||||
#[derive(Clone)]
|
||||
pub struct SecretKey([u8; Self::SIZE]);
|
||||
|
||||
/// Ed25519 Extended Secret Key
|
||||
///
|
||||
/// unlike [`SecretKey`], an extended key can be derived see [`pallas_crypto::derivation`]
|
||||
#[derive(Clone)]
|
||||
pub struct SecretKeyExtended([u8; Self::SIZE]);
|
||||
|
||||
/// Ed25519 Public Key. Can be used to verify a [`Signature`]. A [`PublicKey`]
|
||||
/// is associated to a [`SecretKey`]
|
||||
#[derive(Clone, Copy, PartialEq, Eq, Hash)]
|
||||
pub struct PublicKey([u8; Self::SIZE]);
|
||||
|
||||
/// Ed25519 Signature. Is created by a [`SecretKey`] and is verified
|
||||
/// with a [`PublicKey`].
|
||||
#[derive(Clone, Copy, PartialEq, Eq, Hash)]
|
||||
pub struct Signature([u8; Self::SIZE]);
|
||||
|
||||
/// Error type used when retrieving a [`PublicKey`] via the [`TryFrom`]
|
||||
/// trait.
|
||||
#[derive(Debug, Error)]
|
||||
pub enum TryFromPublicKeyError {
|
||||
#[error("Invalid size, expecting {}", PublicKey::SIZE)]
|
||||
InvalidSize,
|
||||
}
|
||||
|
||||
/// Error type used when retrieving a [`Signature`] via the [`TryFrom`]
|
||||
/// trait.
|
||||
#[derive(Debug, Error)]
|
||||
pub enum TryFromSignatureError {
|
||||
#[error("Invalid size, expecting {}", Signature::SIZE)]
|
||||
InvalidSize,
|
||||
}
|
||||
|
||||
macro_rules! impl_size_zero {
|
||||
($Type:ty, $Size:expr) => {
|
||||
impl $Type {
|
||||
/// This is the size of the type in bytes.
|
||||
pub const SIZE: usize = $Size;
|
||||
|
||||
/// create a zero object. This is not a _"valid"_ one. It is
|
||||
/// used to initialize a ready to use data structure in this module.
|
||||
#[inline]
|
||||
fn zero() -> Self {
|
||||
Self([0; Self::SIZE])
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
impl_size_zero!(SecretKey, SEED_LENGTH);
|
||||
impl_size_zero!(SecretKeyExtended, PRIVATE_KEY_LENGTH);
|
||||
impl_size_zero!(PublicKey, PUBLIC_KEY_LENGTH);
|
||||
impl_size_zero!(Signature, SIGNATURE_LENGTH);
|
||||
|
||||
impl SecretKey {
|
||||
/// generate a new [`SecretKey`] with the given random number generator
|
||||
///
|
||||
pub fn new<Rng>(mut rng: Rng) -> Self
|
||||
where
|
||||
Rng: RngCore + CryptoRng,
|
||||
{
|
||||
let mut s = Self::zero();
|
||||
rng.fill_bytes(&mut s.0);
|
||||
s
|
||||
}
|
||||
|
||||
/// get the [`PublicKey`] associated to this key
|
||||
///
|
||||
/// Unlike the [`SecretKey`], the [`PublicKey`] can be safely
|
||||
/// publicly shared. The key can then be used to verify any
|
||||
/// [`Signature`] generated with this [`SecretKey`] and the original
|
||||
/// message.
|
||||
pub fn public_key(&self) -> PublicKey {
|
||||
let (mut sk, pk) = ed25519::keypair(&self.0);
|
||||
|
||||
// the `sk` is a private component, scrubbing it reduce the
|
||||
// risk of an adversary accessing the memory remains of this
|
||||
// value
|
||||
sk.scrub();
|
||||
|
||||
PublicKey(pk)
|
||||
}
|
||||
|
||||
/// create a [`Signature`] for the given message with this [`SecretKey`].
|
||||
///
|
||||
/// The [`Signature`] can then be verified against the associated [`PublicKey`]
|
||||
/// and the original message.
|
||||
pub fn sign<T>(&self, msg: T) -> Signature
|
||||
where
|
||||
T: AsRef<[u8]>,
|
||||
{
|
||||
let (mut sk, _) = ed25519::keypair(&self.0);
|
||||
|
||||
let signature = ed25519::signature(msg.as_ref(), &sk);
|
||||
|
||||
// we don't need this signature component, make sure to scrub the
|
||||
// content before releasing the results
|
||||
sk.scrub();
|
||||
|
||||
Signature(signature)
|
||||
}
|
||||
}
|
||||
|
||||
impl SecretKeyExtended {
|
||||
/// generate a new [`SecretKeyExtended`] with the given random number generator
|
||||
///
|
||||
pub fn new<Rng>(mut rng: Rng) -> Self
|
||||
where
|
||||
Rng: RngCore + CryptoRng,
|
||||
{
|
||||
let mut s = Self::zero();
|
||||
rng.fill_bytes(&mut s.0);
|
||||
|
||||
s.0[0] &= 0b1111_1000;
|
||||
s.0[31] &= 0b0011_1111;
|
||||
s.0[31] |= 0b0100_0000;
|
||||
|
||||
debug_assert!(
|
||||
s.check_structure(),
|
||||
"checking we properly set the bit tweaks for the extended Ed25519"
|
||||
);
|
||||
|
||||
s
|
||||
}
|
||||
|
||||
#[inline]
|
||||
#[allow(clippy::verbose_bit_mask)]
|
||||
fn check_structure(&self) -> bool {
|
||||
(self.0[0] & 0b0000_0111) == 0
|
||||
&& (self.0[31] & 0b0100_0000) == 0b0100_0000
|
||||
&& (self.0[31] & 0b1000_0000) == 0
|
||||
}
|
||||
|
||||
/// get the [`PublicKey`] associated to this key
|
||||
///
|
||||
/// Unlike the [`SecretKeyExtended`], the [`PublicKey`] can be safely
|
||||
/// publicly shared. The key can then be used to verify any
|
||||
/// [`Signature`] generated with this [`SecretKeyExtended`] and the original
|
||||
/// message.
|
||||
pub fn public_key(&self) -> PublicKey {
|
||||
let pk = ed25519::to_public(&self.0);
|
||||
|
||||
PublicKey::from(pk)
|
||||
}
|
||||
|
||||
/// create a `Signature` for the given message with this `SecretKey`.
|
||||
///
|
||||
/// The `Signature` can then be verified against the associated `PublicKey`
|
||||
/// and the original message.
|
||||
pub fn sign<T: AsRef<[u8]>>(&self, msg: T) -> Signature {
|
||||
let signature = ed25519::signature_extended(msg.as_ref(), &self.0);
|
||||
|
||||
Signature::from(signature)
|
||||
}
|
||||
}
|
||||
|
||||
impl PublicKey {
|
||||
/// verify the cryptographic [`Signature`] against the `message` and the
|
||||
/// [`PublicKey`] `self`.
|
||||
///
|
||||
#[inline]
|
||||
pub fn verify<T>(&self, message: T, signature: &Signature) -> bool
|
||||
where
|
||||
T: AsRef<[u8]>,
|
||||
{
|
||||
ed25519::verify(message.as_ref(), &self.0, signature.as_ref())
|
||||
}
|
||||
}
|
||||
|
||||
/* Drop ******************************************************************** */
|
||||
|
||||
impl Drop for SecretKey {
|
||||
fn drop(&mut self) {
|
||||
self.0.scrub()
|
||||
}
|
||||
}
|
||||
|
||||
impl Drop for SecretKeyExtended {
|
||||
fn drop(&mut self) {
|
||||
self.0.scrub()
|
||||
}
|
||||
}
|
||||
|
||||
/* Format ****************************************************************** */
|
||||
|
||||
impl fmt::Display for Signature {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
f.write_str(&hex::encode(self.as_ref()))
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Display for PublicKey {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
f.write_str(&hex::encode(self.as_ref()))
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Debug for Signature {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
f.debug_tuple("Signature<Ed25519>")
|
||||
.field(&hex::encode(self.as_ref()))
|
||||
.finish()
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Debug for PublicKey {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
f.debug_tuple("PublicKey<Ed25519>")
|
||||
.field(&hex::encode(self.as_ref()))
|
||||
.finish()
|
||||
}
|
||||
}
|
||||
|
||||
macro_rules! impl_secret_fmt {
|
||||
($Type:ty) => {
|
||||
/// conveniently provide a proper implementation to debug for the
|
||||
/// SecretKey types when only *testing* the library
|
||||
#[cfg(test)]
|
||||
impl fmt::Debug for $Type {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
f.debug_tuple(&format!(
|
||||
"SecretKey<{typename}>",
|
||||
typename = type_name::<Self>()
|
||||
))
|
||||
.field(&hex::encode(&self.0))
|
||||
.finish()
|
||||
}
|
||||
}
|
||||
|
||||
/// conveniently provide an incomplete implementation of Debug for the
|
||||
/// SecretKey.
|
||||
#[cfg(not(test))]
|
||||
impl fmt::Debug for $Type {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
f.debug_struct(&format!(
|
||||
"SecretKey<{typename}>",
|
||||
typename = type_name::<Self>()
|
||||
))
|
||||
.finish_non_exhaustive()
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
impl_secret_fmt!(SecretKey);
|
||||
impl_secret_fmt!(SecretKeyExtended);
|
||||
|
||||
/* AsRef ******************************************************************* */
|
||||
|
||||
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()
|
||||
}
|
||||
}
|
||||
|
||||
/* Conversion ************************************************************** */
|
||||
|
||||
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 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)
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> TryFrom<&'a [u8]> for PublicKey {
|
||||
type Error = TryFromPublicKeyError;
|
||||
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 = TryFromSignatureError;
|
||||
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 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()
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
use quickcheck::{Arbitrary, Gen, TestResult};
|
||||
use quickcheck_macros::quickcheck;
|
||||
|
||||
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 SecretKeyExtended {
|
||||
fn arbitrary(g: &mut Gen) -> Self {
|
||||
let mut s = Self::zero();
|
||||
s.0.iter_mut().for_each(|byte| {
|
||||
*byte = u8::arbitrary(g);
|
||||
});
|
||||
|
||||
s.0[0] &= 0b1111_1000;
|
||||
s.0[31] &= 0b0011_1111;
|
||||
s.0[31] |= 0b0100_0000;
|
||||
|
||||
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 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 signing_verify_works_extended(signing_key: SecretKeyExtended, 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 {
|
||||
// NOTE: this test may fail but it is impossible to see this happening in normal condition.
|
||||
// we are generating 32 random bytes of public key and 64 random bytes
|
||||
// of signature with an randomly generated message of a random number
|
||||
// of bytes in. If the message were empty, the probability to have
|
||||
// a signature that matches the verify key would still be 1 out of 2^96.
|
||||
//
|
||||
// if this test fails and it is not a bug, go buy a lottery ticket.
|
||||
!public_key.verify(message, &signature)
|
||||
}
|
||||
|
||||
#[quickcheck]
|
||||
fn public_key_try_from_correct_size(public_key: PublicKey) -> TestResult {
|
||||
match PublicKey::try_from(public_key.as_ref()) {
|
||||
Ok(_) => TestResult::passed(),
|
||||
Err(TryFromPublicKeyError::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(TryFromPublicKeyError::InvalidSize) => TestResult::passed(),
|
||||
}
|
||||
}
|
||||
|
||||
#[quickcheck]
|
||||
fn signature_try_from_correct_size(signature: Signature) -> TestResult {
|
||||
match Signature::try_from(signature.as_ref()) {
|
||||
Ok(_) => TestResult::passed(),
|
||||
Err(TryFromSignatureError::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(TryFromSignatureError::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()),
|
||||
}
|
||||
}
|
||||
}
|
||||
1
pallas-crypto/src/key/mod.rs
Normal file
1
pallas-crypto/src/key/mod.rs
Normal file
|
|
@ -0,0 +1 @@
|
|||
pub mod ed25519;
|
||||
|
|
@ -1 +1,3 @@
|
|||
pub mod hash;
|
||||
pub mod key;
|
||||
pub mod memsec;
|
||||
|
|
|
|||
235
pallas-crypto/src/memsec.rs
Normal file
235
pallas-crypto/src/memsec.rs
Normal file
|
|
@ -0,0 +1,235 @@
|
|||
/*!
|
||||
# Memsec utility functions
|
||||
Most of the types defined here implements `Scrubbed` trait.
|
||||
*/
|
||||
|
||||
use std::ptr;
|
||||
|
||||
/// Types implementing this can be scrubbed, the memory is cleared and
|
||||
/// erased with a dummy value.
|
||||
pub trait Scrubbed {
|
||||
fn scrub(&mut self);
|
||||
}
|
||||
|
||||
/// Perform a secure memset. This function is guaranteed not to be elided
|
||||
/// or reordered.
|
||||
///
|
||||
/// # Performance consideration
|
||||
///
|
||||
/// On `nightly`, the function use a more efficient.
|
||||
///
|
||||
/// # Safety
|
||||
///
|
||||
/// The destination memory (`dst` to `dst+count`) must be properly allocated
|
||||
/// and ready to use.
|
||||
#[inline(never)]
|
||||
pub unsafe fn memset(dst: *mut u8, val: u8, count: usize) {
|
||||
for i in 0..count {
|
||||
ptr::write_volatile(dst.add(i), val);
|
||||
}
|
||||
}
|
||||
|
||||
/// compare the equality of the 2 given arrays, constant in time
|
||||
///
|
||||
/// # Panics
|
||||
///
|
||||
/// The function will panic if it is called with a `len` of 0.
|
||||
///
|
||||
/// # Safety
|
||||
///
|
||||
/// Expecting to have both valid pointer and the count to fit in
|
||||
/// both the allocated memories
|
||||
#[inline(never)]
|
||||
pub unsafe fn memeq(v1: *const u8, v2: *const u8, len: usize) -> bool {
|
||||
let mut sum = 0;
|
||||
|
||||
assert!(
|
||||
len != 0,
|
||||
"Cannot perform equality comparison if the length is 0"
|
||||
);
|
||||
|
||||
for i in 0..len {
|
||||
let val1 = ptr::read_volatile(v1.add(i));
|
||||
let val2 = ptr::read_volatile(v2.add(i));
|
||||
|
||||
let xor = val1 ^ val2;
|
||||
|
||||
sum |= xor;
|
||||
}
|
||||
|
||||
sum == 0
|
||||
}
|
||||
|
||||
/// Constant time comparison
|
||||
///
|
||||
/// # Panics
|
||||
///
|
||||
/// The function will panic if it is called with a `len` of 0.
|
||||
///
|
||||
/// # Safety
|
||||
///
|
||||
/// Expecting to have both valid pointer and the count to fit in
|
||||
/// both the allocated memories
|
||||
#[inline(never)]
|
||||
pub unsafe fn memcmp(v1: *const u8, v2: *const u8, len: usize) -> std::cmp::Ordering {
|
||||
let mut res = 0;
|
||||
|
||||
assert!(
|
||||
len != 0,
|
||||
"Cannot perform ordering comparison if the length is 0"
|
||||
);
|
||||
|
||||
for i in (0..len).rev() {
|
||||
let val1 = ptr::read_volatile(v1.add(i)) as i32;
|
||||
let val2 = ptr::read_volatile(v2.add(i)) as i32;
|
||||
let diff = val1 - val2;
|
||||
res = (res & (((diff - 1) & !diff) >> 8)) | diff;
|
||||
}
|
||||
let res = ((res - 1) >> 8) + (res >> 8) + 1;
|
||||
|
||||
res.cmp(&0)
|
||||
}
|
||||
|
||||
macro_rules! impl_scrubbed_primitive {
|
||||
($t:ty) => {
|
||||
impl Scrubbed for $t {
|
||||
#[inline(never)]
|
||||
fn scrub(&mut self) {
|
||||
*self = 0;
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
impl_scrubbed_primitive!(u8);
|
||||
impl_scrubbed_primitive!(u16);
|
||||
impl_scrubbed_primitive!(u32);
|
||||
impl_scrubbed_primitive!(u64);
|
||||
impl_scrubbed_primitive!(u128);
|
||||
impl_scrubbed_primitive!(usize);
|
||||
impl_scrubbed_primitive!(i8);
|
||||
impl_scrubbed_primitive!(i16);
|
||||
impl_scrubbed_primitive!(i32);
|
||||
impl_scrubbed_primitive!(i64);
|
||||
impl_scrubbed_primitive!(i128);
|
||||
impl_scrubbed_primitive!(isize);
|
||||
|
||||
macro_rules! impl_scrubbed_array {
|
||||
($t:ty) => {
|
||||
impl Scrubbed for $t {
|
||||
fn scrub(&mut self) {
|
||||
unsafe { memset(self.as_mut_ptr(), 0, self.len()) }
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
impl_scrubbed_array!([u8]);
|
||||
impl_scrubbed_array!(str);
|
||||
|
||||
impl<const N: usize> Scrubbed for [u8; N] {
|
||||
fn scrub(&mut self) {
|
||||
unsafe { memset(self.as_mut_ptr(), 0, self.len()) }
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Scrubbed> Scrubbed for Option<T> {
|
||||
fn scrub(&mut self) {
|
||||
self.as_mut().map(Scrubbed::scrub);
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Scrubbed> Scrubbed for Vec<T> {
|
||||
fn scrub(&mut self) {
|
||||
self.iter_mut().for_each(Scrubbed::scrub)
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Scrubbed> Scrubbed for Box<T> {
|
||||
fn scrub(&mut self) {
|
||||
self.as_mut().scrub()
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Scrubbed> Scrubbed for std::cell::Cell<T> {
|
||||
fn scrub(&mut self) {
|
||||
self.get_mut().scrub()
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Scrubbed> Scrubbed for std::cell::RefCell<T> {
|
||||
fn scrub(&mut self) {
|
||||
self.get_mut().scrub()
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use std::cmp::Ordering;
|
||||
|
||||
use super::*;
|
||||
use quickcheck::TestResult;
|
||||
use quickcheck_macros::quickcheck;
|
||||
|
||||
#[test]
|
||||
#[should_panic]
|
||||
fn eq_empty() {
|
||||
let bytes = Vec::new();
|
||||
unsafe { memeq(bytes.as_ptr(), bytes.as_ptr(), bytes.len()) };
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[should_panic]
|
||||
fn ord_empty() {
|
||||
let bytes = Vec::new();
|
||||
unsafe { memcmp(bytes.as_ptr(), bytes.as_ptr(), bytes.len()) };
|
||||
}
|
||||
|
||||
#[quickcheck]
|
||||
fn eq(bytes: Vec<u8>) -> TestResult {
|
||||
if bytes.is_empty() {
|
||||
TestResult::discard()
|
||||
} else {
|
||||
let b = unsafe { memeq(bytes.as_ptr(), bytes.as_ptr(), bytes.len()) };
|
||||
TestResult::from_bool(b)
|
||||
}
|
||||
}
|
||||
|
||||
#[quickcheck]
|
||||
fn ord_eq(bytes: Vec<u8>) -> TestResult {
|
||||
if bytes.is_empty() {
|
||||
TestResult::discard()
|
||||
} else {
|
||||
let ord = unsafe { memcmp(bytes.as_ptr(), bytes.as_ptr(), bytes.len()) };
|
||||
TestResult::from_bool(ord == Ordering::Equal)
|
||||
}
|
||||
}
|
||||
|
||||
#[quickcheck]
|
||||
fn neq(a: Vec<u8>, b: Vec<u8>) -> TestResult {
|
||||
let len = std::cmp::min(a.len(), b.len());
|
||||
|
||||
if a[..len] == b[..len] || len == 0 {
|
||||
TestResult::discard()
|
||||
} else {
|
||||
let b = unsafe { memeq(a.as_ptr(), b.as_ptr(), len) };
|
||||
|
||||
TestResult::from_bool(!b)
|
||||
}
|
||||
}
|
||||
|
||||
#[quickcheck]
|
||||
fn ord(a: Vec<u8>, b: Vec<u8>) -> TestResult {
|
||||
let len = std::cmp::min(a.len(), b.len());
|
||||
|
||||
if len == 0 {
|
||||
TestResult::discard()
|
||||
} else {
|
||||
let a = &a[..len];
|
||||
let b = &b[..len];
|
||||
let ord = unsafe { memcmp(a.as_ptr(), b.as_ptr(), len) };
|
||||
|
||||
TestResult::from_bool(ord == a.cmp(b))
|
||||
}
|
||||
}
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue