use ed25519_dalek::Signer;
use crate::{
hash::{Hash, MAX_HASH_VERSION, MIN_HASH_VERSION},
lock::LockId,
lockbox::*,
stream::StreamKey,
CryptoError, CryptoSrc,
};
use rand_core::{CryptoRng, RngCore};
use zeroize::Zeroize;
use std::{convert::TryFrom, fmt, sync::Arc};
pub const DEFAULT_SIGN_VERSION: u8 = 1;
pub const MIN_SIGN_VERSION: u8 = 1;
pub const MAX_SIGN_VERSION: u8 = 1;
const V1_IDENTITY_KEY_SIZE: usize = ed25519_dalek::SECRET_KEY_LENGTH;
const V1_IDENTITY_ID_SIZE: usize = ed25519_dalek::PUBLIC_KEY_LENGTH;
const V1_IDENTITY_SIGN_SIZE: usize = ed25519_dalek::SIGNATURE_LENGTH;
#[derive(Clone)]
pub struct IdentityKey {
interface: Arc<dyn SignInterface>,
}
impl IdentityKey {
pub fn new_temp<R>(csprng: &mut R) -> IdentityKey
where
R: rand_core::CryptoRng + rand_core::RngCore,
{
let interface = Arc::new(ContainedIdKey::generate(csprng));
new_identity_key(interface)
}
pub fn new_temp_with_version<R>(csprng: &mut R, version: u8) -> Result<IdentityKey, CryptoError>
where
R: rand_core::CryptoRng + rand_core::RngCore,
{
let interface = Arc::new(ContainedIdKey::with_version(csprng, version)?);
Ok(new_identity_key(interface))
}
pub fn version(&self) -> u8 {
self.id().version()
}
pub fn id(&self) -> &Identity {
self.interface.id()
}
pub fn sign(&self, hash: &Hash) -> Signature {
self.interface.sign(hash)
}
pub fn max_signature_size(&self) -> usize {
1 + V1_IDENTITY_SIGN_SIZE + self.id().size()
}
pub fn export_for_lock<R: CryptoRng + RngCore>(
&self,
csprng: &mut R,
lock: &LockId,
) -> Option<IdentityLockbox> {
self.interface.self_export_lock(csprng, lock)
}
pub fn export_for_stream<R: CryptoRng + RngCore>(
&self,
csprng: &mut R,
stream: &StreamKey,
) -> Option<IdentityLockbox> {
self.interface.self_export_stream(csprng, stream)
}
}
impl fmt::Display for IdentityKey {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
fmt::Display::fmt(self.id(), f)
}
}
impl fmt::Debug for IdentityKey {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
f.debug_struct("IdentityKey")
.field("version", &self.version())
.field("public_key", &self.id().raw_public_key())
.finish()
}
}
pub fn new_identity_key(interface: Arc<dyn SignInterface>) -> IdentityKey {
IdentityKey { interface }
}
#[derive(Clone)]
pub struct Identity {
id: ed25519_dalek::PublicKey,
}
impl Identity {
pub fn version(&self) -> u8 {
1u8
}
pub fn raw_public_key(&self) -> &[u8] {
self.id.as_ref()
}
pub fn as_vec(&self) -> Vec<u8> {
let mut v = Vec::new();
self.encode_vec(&mut v);
v
}
pub fn from_base58(s: &str) -> Result<Self, CryptoError> {
let raw = bs58::decode(s)
.into_vec()
.or(Err(CryptoError::BadFormat("Not valid Base58")))?;
Self::try_from(&raw[..])
}
pub fn to_base58(&self) -> String {
bs58::encode(&(self.as_vec())).into_string()
}
pub fn encode_vec(&self, buf: &mut Vec<u8>) {
let id = self.id.as_bytes();
buf.reserve(self.size());
buf.push(self.version());
buf.extend_from_slice(id);
}
pub fn size(&self) -> usize {
1 + self.id.as_ref().len()
}
}
impl TryFrom<&[u8]> for Identity {
type Error = CryptoError;
fn try_from(value: &[u8]) -> Result<Self, Self::Error> {
let (&version, data) = value.split_first().ok_or(CryptoError::BadLength {
step: "get Identity version",
expected: 1,
actual: 0,
})?;
if version != 1u8 {
return Err(CryptoError::UnsupportedVersion(version));
}
if data.len() != V1_IDENTITY_ID_SIZE {
return Err(CryptoError::BadLength {
step: "get Identity public key",
expected: V1_IDENTITY_ID_SIZE,
actual: data.len(),
});
}
let id = ed25519_dalek::PublicKey::from_bytes(data).or(Err(CryptoError::BadKey))?;
Ok(Identity { id })
}
}
impl fmt::Debug for Identity {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
f.debug_struct("Identity")
.field("version", &self.version())
.field("public_key", &self.raw_public_key())
.finish()
}
}
impl fmt::Display for Identity {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "{}", self.to_base58())
}
}
impl fmt::LowerHex for Identity {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
for byte in self.as_vec().iter() {
write!(f, "{:x}", byte)?;
}
Ok(())
}
}
impl fmt::UpperHex for Identity {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
for byte in self.as_vec().iter() {
write!(f, "{:X}", byte)?;
}
Ok(())
}
}
impl std::cmp::PartialEq for Identity {
fn eq(&self, other: &Self) -> bool {
self.id == other.id
}
}
impl std::cmp::Eq for Identity {}
impl std::hash::Hash for Identity {
fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
self.id.as_bytes().hash(state);
}
}
pub trait SignInterface {
fn id(&self) -> &Identity;
fn sign(&self, hash: &Hash) -> Signature;
fn self_export_lock(
&self,
csprng: &mut dyn CryptoSrc,
receive_lock: &LockId,
) -> Option<IdentityLockbox>;
fn self_export_stream(
&self,
csprng: &mut dyn CryptoSrc,
receive_stream: &StreamKey,
) -> Option<IdentityLockbox>;
}
pub struct ContainedIdKey {
id: Identity,
inner: ed25519_dalek::Keypair,
}
impl ContainedIdKey {
pub fn generate<R>(csprng: &mut R) -> Self
where
R: rand_core::CryptoRng + rand_core::RngCore,
{
Self::with_version(csprng, DEFAULT_SIGN_VERSION).unwrap()
}
pub fn with_version<R>(csprng: &mut R, version: u8) -> Result<Self, CryptoError>
where
R: rand_core::CryptoRng + rand_core::RngCore,
{
if (version < MIN_SIGN_VERSION) || (version > MAX_SIGN_VERSION) {
return Err(CryptoError::UnsupportedVersion(version));
}
let inner = ed25519_dalek::Keypair::generate(csprng);
let id = Identity { id: inner.public };
Ok(Self { id, inner })
}
pub fn encode_vec(&self, buf: &mut Vec<u8>) {
buf.reserve(1 + ed25519_dalek::SECRET_KEY_LENGTH);
buf.push(1u8);
buf.extend_from_slice(self.inner.secret.as_bytes())
}
}
impl TryFrom<&[u8]> for ContainedIdKey {
type Error = CryptoError;
fn try_from(value: &[u8]) -> Result<Self, Self::Error> {
let (version, key) = value.split_first().ok_or(CryptoError::BadLength {
step: "get IdentityKey version",
expected: 1,
actual: 0,
})?;
let version = *version;
if version < MIN_SIGN_VERSION {
return Err(CryptoError::OldVersion(version));
}
if version > MAX_SIGN_VERSION {
return Err(CryptoError::UnsupportedVersion(version));
}
if key.len() != V1_IDENTITY_KEY_SIZE {
return Err(CryptoError::BadLength {
step: "get IdentityKey key bytes",
expected: V1_IDENTITY_KEY_SIZE,
actual: key.len(),
});
}
let secret = ed25519_dalek::SecretKey::from_bytes(key).map_err(|_| CryptoError::BadKey)?;
let public = ed25519_dalek::PublicKey::from(&secret);
let inner = ed25519_dalek::Keypair { secret, public };
Ok(Self {
inner,
id: Identity { id: public },
})
}
}
impl SignInterface for ContainedIdKey {
fn sign(&self, hash: &Hash) -> Signature {
let inner = self.inner.sign(hash.digest());
Signature {
hash_version: hash.version(),
id: self.id.clone(),
inner,
}
}
fn id(&self) -> &Identity {
&self.id
}
fn self_export_lock(
&self,
csprng: &mut dyn CryptoSrc,
receive_lock: &LockId,
) -> Option<IdentityLockbox> {
let mut raw_secret = Vec::new();
self.encode_vec(&mut raw_secret);
let lockbox_vec = crate::lock::lock_id_encrypt(
receive_lock,
csprng,
LockboxType::Identity(false),
&raw_secret,
);
raw_secret.zeroize();
debug_assert!(raw_secret.iter().all(|&x| x == 0));
Some(identity_lockbox_from_parts(lockbox_vec))
}
fn self_export_stream(
&self,
csprng: &mut dyn CryptoSrc,
receive_stream: &StreamKey,
) -> Option<IdentityLockbox> {
let mut raw_secret = Vec::new();
self.encode_vec(&mut raw_secret);
let lockbox_vec = crate::stream::stream_key_encrypt(
receive_stream,
csprng,
LockboxType::Identity(true),
&raw_secret,
);
raw_secret.zeroize();
debug_assert!(raw_secret.iter().all(|&x| x == 0));
Some(identity_lockbox_from_parts(lockbox_vec))
}
}
#[derive(Clone, PartialEq, Eq)]
pub struct Signature {
hash_version: u8,
id: Identity,
inner: ed25519_dalek::Signature,
}
impl Signature {
pub fn hash_version(&self) -> u8 {
self.hash_version
}
pub fn signer(&self) -> &Identity {
&self.id
}
pub fn encode_vec(&self, buf: &mut Vec<u8>) {
let signature = self.inner.as_ref();
buf.push(self.hash_version);
self.id.encode_vec(buf);
buf.extend_from_slice(signature);
}
pub fn size(&self) -> usize {
1 + V1_IDENTITY_SIGN_SIZE + self.id.size()
}
}
impl fmt::Debug for Signature {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
f.debug_struct("Signature")
.field("hash_version", &self.hash_version)
.field("signer", &self.id)
.field("signature", &self.inner)
.finish()
}
}
#[derive(Clone, PartialEq, Eq)]
pub struct UnverifiedSignature {
hash_version: u8,
signature: ed25519_dalek::Signature,
id: ed25519_dalek::PublicKey,
}
impl UnverifiedSignature {
pub fn hash_version(&self) -> u8 {
self.hash_version
}
pub fn verify(self, hash: &Hash) -> Result<Signature, CryptoError> {
if hash.version() != self.hash_version {
return Err(CryptoError::ObjectMismatch(
"Verification step got wrong version of hash",
));
}
if self
.id
.verify_strict(hash.digest(), &self.signature)
.is_err()
{
return Err(CryptoError::SignatureFailed);
}
Ok(Signature {
hash_version: self.hash_version,
id: Identity { id: self.id },
inner: self.signature,
})
}
}
impl fmt::Debug for UnverifiedSignature {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
f.debug_struct("UnverifiedSignature")
.field("hash_version", &self.hash_version)
.field("signer", &self.id)
.field("signature", &self.signature.as_ref())
.finish()
}
}
impl TryFrom<&[u8]> for UnverifiedSignature {
type Error = CryptoError;
fn try_from(value: &[u8]) -> Result<Self, Self::Error> {
let (hash_version, value) = value.as_ref().split_first().ok_or(CryptoError::BadLength {
step: "get signature hash version",
expected: 1,
actual: 0,
})?;
let hash_version = *hash_version;
if hash_version < MIN_HASH_VERSION || hash_version > MAX_HASH_VERSION {
return Err(CryptoError::UnsupportedVersion(hash_version));
}
let (&id_version, data) = value.split_first().ok_or(CryptoError::BadLength {
step: "get signature id version",
expected: 1,
actual: 0,
})?;
if id_version != 1 {
return Err(CryptoError::UnsupportedVersion(id_version));
}
let id_len = V1_IDENTITY_ID_SIZE;
let raw_id = data.get(0..id_len).ok_or(CryptoError::BadLength {
step: "get signature signer",
expected: id_len,
actual: data.len(),
})?;
let raw_signature = data.get(id_len..).ok_or(CryptoError::BadLength {
step: "get signature data",
expected: V1_IDENTITY_SIGN_SIZE,
actual: data.len() - id_len,
})?;
let id = ed25519_dalek::PublicKey::from_bytes(raw_id).or(Err(CryptoError::BadKey))?;
let signature = ed25519_dalek::Signature::try_from(raw_signature)
.or(Err(CryptoError::SignatureFailed))?;
Ok(UnverifiedSignature {
hash_version,
id,
signature,
})
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn basics() {
let mut csprng = rand::rngs::OsRng;
let key = IdentityKey::new_temp(&mut csprng);
assert_eq!(key.version(), DEFAULT_SIGN_VERSION);
let key = IdentityKey::new_temp_with_version(&mut csprng, DEFAULT_SIGN_VERSION).unwrap();
assert_eq!(key.version(), DEFAULT_SIGN_VERSION);
let result = IdentityKey::new_temp_with_version(&mut csprng, 99u8);
if let Err(CryptoError::UnsupportedVersion(99u8)) = result {
} else {
panic!("Didn't get expected error on new_temp_with_version");
}
}
#[test]
fn display() {
let mut csprng = rand::rngs::OsRng;
let key = IdentityKey::new_temp(&mut csprng);
let disp_key = format!("{}", &key);
let disp_id = format!("{}", key.id());
let base58 = key.id().to_base58();
assert_eq!(disp_key, disp_id);
assert_eq!(disp_key, base58);
assert!(disp_key.len() > 1);
}
#[test]
fn base58() {
let mut csprng = rand::rngs::OsRng;
let key = IdentityKey::new_temp(&mut csprng);
let mut base58 = key.id().to_base58();
assert!(base58.len() > 1);
let id = Identity::from_base58(&base58).unwrap();
assert_eq!(&id, key.id());
base58.push('a');
base58.push('a');
assert!(Identity::from_base58(&base58).is_err());
base58.pop();
base58.pop();
base58.pop();
assert!(Identity::from_base58(&base58).is_err());
}
#[test]
fn encode() {
let mut csprng = rand::rngs::OsRng;
let key = IdentityKey::new_temp(&mut csprng);
let id = key.id();
let id_v0 = id.as_vec();
let mut id_v1 = Vec::new();
id.encode_vec(&mut id_v1);
assert_eq!(id_v0.len(), id.size());
assert_eq!(id_v0, id_v1);
let id = Identity::try_from(&id_v0[..]).unwrap();
assert_eq!(&id, key.id());
}
#[test]
fn id_len() {
let mut csprng = rand::rngs::OsRng;
let key = IdentityKey::new_temp(&mut csprng);
let id = key.id();
let len = id.size();
let mut enc = Vec::new();
id.encode_vec(&mut enc);
assert_eq!(len, enc.len());
assert_eq!(len, id.as_vec().len());
}
#[test]
fn signature_len() {
let mut csprng = rand::rngs::OsRng;
let key = IdentityKey::new_temp(&mut csprng);
let hash = Hash::new(b"I am a test string");
let sign = key.sign(&hash);
let len = sign.size();
let mut enc = Vec::new();
sign.encode_vec(&mut enc);
assert_eq!(len, enc.len());
}
#[test]
fn sign() {
let mut csprng = rand::rngs::OsRng;
let key = IdentityKey::new_temp(&mut csprng);
let test_data = b"This is a test";
let hash = Hash::new(test_data);
let sign = key.sign(&hash);
assert_eq!(
sign.hash_version(),
hash.version(),
"Hash version in signature should match Hash's"
);
assert_eq!(
sign.signer(),
key.id(),
"Identity in signature should match original Id"
);
let mut enc = Vec::new();
sign.encode_vec(&mut enc);
let dec_sign = UnverifiedSignature::try_from(&enc[..])
.expect("Wasn't able to decode an unverified signature")
.verify(&hash)
.expect("Wasn't able to verify the signature");
assert_eq!(
dec_sign.signer(),
sign.signer(),
"Signature Identities don't match"
);
assert_eq!(
dec_sign.hash_version(),
sign.hash_version(),
"Signature hash versions don't match"
);
}
#[test]
fn wrong_hashes() {
let mut csprng = rand::rngs::OsRng;
let key = IdentityKey::new_temp(&mut csprng);
let test_data = b"This is a test";
let hash = Hash::new(test_data);
let bad_hash = Hash::new(b"Not the same data");
let sign = key.sign(&hash);
let mut enc = Vec::new();
sign.encode_vec(&mut enc);
let unverified = UnverifiedSignature::try_from(&enc[..])
.expect("Wasn't able to decode an unverified signature");
if let Err(CryptoError::SignatureFailed) = unverified.verify(&bad_hash) {
} else {
panic!(
"Signature verification should fail with SignatureFailed when given the wrong Hash"
);
}
}
#[test]
fn wrong_hash_versions() {
let mut csprng = rand::rngs::OsRng;
let key = IdentityKey::new_temp(&mut csprng);
let test_data = b"This is a test";
let hash = Hash::new(test_data);
let sign = key.sign(&hash);
let mut enc = Vec::new();
sign.encode_vec(&mut enc);
enc[0] = 0;
if let Err(CryptoError::UnsupportedVersion(0)) = UnverifiedSignature::try_from(&enc[..]) {
} else {
panic!("Signature decoding shouldn't permit a hash with version 0");
}
enc[0] = 255;
if let Err(CryptoError::UnsupportedVersion(255)) = UnverifiedSignature::try_from(&enc[..]) {
} else {
panic!("Signature decoding shouldn't permit a hash with version 255");
}
}
#[test]
fn wrong_id_versions() {
let mut csprng = rand::rngs::OsRng;
let key = IdentityKey::new_temp(&mut csprng);
let test_data = b"This is a test";
let hash = Hash::new(test_data);
let sign = key.sign(&hash);
let mut enc = Vec::new();
sign.encode_vec(&mut enc);
enc[1] = 0;
if let Err(CryptoError::UnsupportedVersion(0)) = UnverifiedSignature::try_from(&enc[..]) {
} else {
panic!("Signature decoding shouldn't permit an identity with version 0");
}
enc[1] = 255;
if let Err(CryptoError::UnsupportedVersion(255)) = UnverifiedSignature::try_from(&enc[..]) {
} else {
panic!("Signature decoding shouldn't permit an identity with version 255");
}
}
#[test]
fn corrupted_signature() {
let mut csprng = rand::rngs::OsRng;
let key = IdentityKey::new_temp(&mut csprng);
let test_data = b"This is a test";
let hash = Hash::new(test_data);
let sign = key.sign(&hash);
let mut enc = Vec::new();
sign.encode_vec(&mut enc);
let last = enc.last_mut().unwrap();
*last = !*last;
let unverified = UnverifiedSignature::try_from(&enc[..]);
if let Err(CryptoError::SignatureFailed) = unverified {
} else {
panic!("Should fail with SignatureFailed when the last signature byte is wrong");
}
let last = enc.last_mut().unwrap();
*last = !*last;
let len = enc.len();
let near_last = enc.get_mut(len - 2).unwrap();
*near_last = !*near_last;
let unverified = UnverifiedSignature::try_from(&enc[..]).unwrap();
if let Err(CryptoError::SignatureFailed) = unverified.verify(&hash) {
} else {
panic!("Should fail with SignatureFailed when the signature bytes are wrong");
}
}
#[test]
fn corrupted_id() {
let mut csprng = rand::rngs::OsRng;
let key = IdentityKey::new_temp(&mut csprng);
let test_data = b"This is a test";
let hash = Hash::new(test_data);
let sign = key.sign(&hash);
let mut enc = Vec::new();
sign.encode_vec(&mut enc);
enc[4] = !enc[4];
match UnverifiedSignature::try_from(&enc[..]) {
Err(CryptoError::BadKey) => {}
Ok(unverified) => {
if let Err(CryptoError::SignatureFailed) = unverified.verify(&hash) {
} else {
panic!("Should fail with SignatureFailed when identity is wrong for signature");
}
}
_ => {
panic!("Should fail with BadKey when the identity is corrupted and ed25519_dalek can tell");
}
}
}
#[test]
fn substitute_wrong_id() {
let mut csprng = rand::rngs::OsRng;
let key = IdentityKey::new_temp(&mut csprng);
let other_id = IdentityKey::new_temp(&mut csprng);
let test_data = b"This is a test";
let hash = Hash::new(test_data);
let sign = key.sign(&hash);
let mut enc = Vec::new();
sign.encode_vec(&mut enc);
for (dest, src) in enc.iter_mut().skip(1).zip(other_id.id().as_vec().iter()) {
*dest = *src;
}
match UnverifiedSignature::try_from(&enc[..]) {
Err(CryptoError::BadKey) => {
panic!("Key should be valid, just wrong for the signature");
}
Ok(unverified) => {
if let Err(CryptoError::SignatureFailed) = unverified.verify(&hash) {
} else {
panic!("Should fail with SignatureFailed when identity is wrong for signature");
}
}
_ => {
panic!("Shouldn't fail on the initial decoding to an UnverifiedSignature");
}
}
}
}