crypto: Cleanup `hash` module

Remove `Digest` type and use `ssh-key` for formatting public ssh key.
This commit is contained in:
Alexis Sellier 2023-02-16 23:47:48 +01:00
parent ef0628c48e
commit 7e1b2b6222
No known key found for this signature in database
9 changed files with 16 additions and 123 deletions

1
Cargo.lock generated
View File

@ -1929,7 +1929,6 @@ dependencies = [
"qcheck-macros", "qcheck-macros",
"radicle-ssh", "radicle-ssh",
"serde", "serde",
"sha2 0.10.6",
"sqlite", "sqlite",
"ssh-key", "ssh-key",
"tempfile", "tempfile",

View File

@ -21,7 +21,7 @@ $ rad self
ID did:key:z6MknSLrJoTcukLrE435hVNQT4JUhbvWLX4kUzqkEStBU8Vi ID did:key:z6MknSLrJoTcukLrE435hVNQT4JUhbvWLX4kUzqkEStBU8Vi
Node ID z6MknSLrJoTcukLrE435hVNQT4JUhbvWLX4kUzqkEStBU8Vi Node ID z6MknSLrJoTcukLrE435hVNQT4JUhbvWLX4kUzqkEStBU8Vi
Key (hash) SHA256:UIedaL6Cxm6OUErh9GQUzzglSk7VpQlVTI1TAFB/HWA Key (hash) SHA256:UIedaL6Cxm6OUErh9GQUzzglSk7VpQlVTI1TAFB/HWA
Key (full) AAAAC3NzaC1lZDI1NTE5AAAAIHahWSBEpuT1ESZbynOmBNkLBSnR32Ar4woZqSV2YNH1 Key (full) ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIHahWSBEpuT1ESZbynOmBNkLBSnR32Ar4woZqSV2YNH1
Storage (git) [..]/storage Storage (git) [..]/storage
Storage (keys) [..]/keys Storage (keys) [..]/keys
Node (socket) [..]/node/radicle.sock Node (socket) [..]/node/radicle.sock

View File

@ -18,7 +18,6 @@ cyphernet = { version = "0.2.0", optional = true }
multibase = { version = "0.9.1" } multibase = { version = "0.9.1" }
ec25519 = { version = "0.1.0", features = [] } ec25519 = { version = "0.1.0", features = [] }
serde = { version = "1", features = ["derive"] } serde = { version = "1", features = ["derive"] }
sha2 = { version = "0.10.2" }
sqlite = { version = "0.30.3", optional = true } sqlite = { version = "0.30.3", optional = true }
thiserror = { version = "1" } thiserror = { version = "1" }
zeroize = { version = "1.5.7" } zeroize = { version = "1.5.7" }

View File

@ -1,69 +0,0 @@
use std::{convert::TryInto, fmt};
use serde::{Deserialize, Serialize};
use sha2::{
digest::{generic_array::GenericArray, OutputSizeUser},
Digest as _, Sha256,
};
use thiserror::Error;
#[derive(Debug, Clone, PartialEq, Eq, Error)]
pub enum DecodeError {
#[error("invalid digest length {0}")]
InvalidLength(usize),
}
/// A SHA-256 hash.
#[derive(Serialize, Deserialize, Default, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub struct Digest([u8; 32]);
impl Digest {
pub fn new(bytes: impl AsRef<[u8]>) -> Self {
Self::from(Sha256::digest(bytes))
}
}
impl AsRef<[u8; 32]> for Digest {
fn as_ref(&self) -> &[u8; 32] {
&self.0
}
}
impl fmt::Debug for Digest {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "Hash({self})")
}
}
impl fmt::Display for Digest {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
for byte in &self.0 {
write!(f, "{byte:02x}")?;
}
Ok(())
}
}
impl From<[u8; 32]> for Digest {
fn from(bytes: [u8; 32]) -> Self {
Self(bytes)
}
}
impl TryFrom<&[u8]> for Digest {
type Error = DecodeError;
fn try_from(bytes: &[u8]) -> Result<Self, DecodeError> {
let bytes: [u8; 32] = bytes
.try_into()
.map_err(|_| DecodeError::InvalidLength(bytes.len()))?;
Ok(bytes.into())
}
}
impl From<GenericArray<u8, <Sha256 as OutputSizeUser>::OutputSize>> for Digest {
fn from(array: GenericArray<u8, <Sha256 as OutputSizeUser>::OutputSize>) -> Self {
Self(array.into())
}
}

View File

@ -8,7 +8,6 @@ use thiserror::Error;
pub use ed25519::{edwards25519, Error, KeyPair, Seed}; pub use ed25519::{edwards25519, Error, KeyPair, Seed};
pub mod hash;
#[cfg(feature = "ssh")] #[cfg(feature = "ssh")]
pub mod ssh; pub mod ssh;
#[cfg(any(test, feature = "test"))] #[cfg(any(test, feature = "test"))]
@ -166,6 +165,13 @@ impl cyphernet::display::MultiDisplay<cyphernet::display::Encoding> for PublicKe
} }
} }
#[cfg(feature = "ssh")]
impl From<PublicKey> for ssh_key::PublicKey {
fn from(key: PublicKey) -> Self {
ssh_key::PublicKey::from(ssh_key::public::Ed25519PublicKey(**key))
}
}
#[cfg(feature = "cyphernet")] #[cfg(feature = "cyphernet")]
impl cyphernet::EcPk for PublicKey { impl cyphernet::EcPk for PublicKey {
const COMPRESSED_LEN: usize = 32; const COMPRESSED_LEN: usize = 32;

View File

@ -16,35 +16,20 @@ use crate::PublicKey;
pub use keystore::{Keystore, Passphrase}; pub use keystore::{Keystore, Passphrase};
pub mod fmt { pub mod fmt {
use radicle_ssh::encoding::Encoding as _;
use crate::PublicKey; use crate::PublicKey;
/// Get the SSH long key from a public key. /// Get the SSH long key from a public key.
/// This is the output of `ssh-add -L`. /// This is the output of `ssh-add -L`.
pub fn key(key: &PublicKey) -> String { pub fn key(key: &PublicKey) -> String {
let mut buf = Vec::new(); ssh_key::PublicKey::from(*key).to_string()
buf.extend_ssh_string(b"ssh-ed25519");
buf.extend_ssh_string(key.as_ref());
base64::encode_config(buf, base64::STANDARD_NO_PAD)
} }
/// Get the SSH key fingerprint from a public key. /// Get the SSH key fingerprint from a public key.
/// This is the output of `ssh-add -l`. /// This is the output of `ssh-add -l`.
pub fn fingerprint(key: &PublicKey) -> String { pub fn fingerprint(key: &PublicKey) -> String {
use sha2::Digest; ssh_key::PublicKey::from(*key)
.fingerprint(Default::default())
let mut buf = Vec::new(); .to_string()
buf.extend_ssh_string(b"ssh-ed25519");
buf.extend_ssh_string(key.as_ref());
let sha = sha2::Sha256::digest(&buf).to_vec();
let encoded = base64::encode_config(sha, base64::STANDARD_NO_PAD);
format!("SHA256:{encoded}")
} }
#[cfg(test)] #[cfg(test)]
@ -61,7 +46,7 @@ pub mod fmt {
assert_eq!( assert_eq!(
key(&pk), key(&pk),
"AAAAC3NzaC1lZDI1NTE5AAAAINDoXIrhcnRjnLGUXUFdxhkuy08lkTOwrj2IoGsEX6+Q" "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAINDoXIrhcnRjnLGUXUFdxhkuy08lkTOwrj2IoGsEX6+Q"
); );
} }
@ -431,7 +416,8 @@ AAAAQI84aPZsXxlQigpy1/Y/iJSmHSS//CIgvqvUMQIb/TM2vhCKruduH0cK02k9G8wOI+
EUMf2bSDyxbJyZThOEiAs= EUMf2bSDyxbJyZThOEiAs=
-----END SSH SIGNATURE-----"; -----END SSH SIGNATURE-----";
let public_key = "AAAAC3NzaC1lZDI1NTE5AAAAIL460KIEccS4881p7PPpiiQBsxF+H5tgC6De6crw9rbU"; let public_key =
"ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIL460KIEccS4881p7PPpiiQBsxF+H5tgC6De6crw9rbU";
let signature = ExtendedSignature::from_armored(armored).unwrap(); let signature = ExtendedSignature::from_armored(armored).unwrap();
assert_eq!(signature.version, 1); assert_eq!(signature.version, 1);

View File

@ -1,6 +1,6 @@
use qcheck::Arbitrary; use qcheck::Arbitrary;
use crate::{hash, test::signer::MockSigner, KeyPair, PublicKey, SecretKey, Seed}; use crate::{test::signer::MockSigner, KeyPair, PublicKey, SecretKey, Seed};
impl Arbitrary for MockSigner { impl Arbitrary for MockSigner {
fn arbitrary(g: &mut qcheck::Gen) -> Self { fn arbitrary(g: &mut qcheck::Gen) -> Self {
@ -21,10 +21,3 @@ impl Arbitrary for PublicKey {
PublicKey(keypair.pk) PublicKey(keypair.pk)
} }
} }
impl Arbitrary for hash::Digest {
fn arbitrary(g: &mut qcheck::Gen) -> Self {
let bytes: Vec<u8> = Arbitrary::arbitrary(g);
hash::Digest::new(bytes)
}
}

View File

@ -23,7 +23,6 @@ pub use runtime::Runtime;
pub mod prelude { pub mod prelude {
pub use crate::bounded::BoundedVec; pub use crate::bounded::BoundedVec;
pub use crate::clock::Timestamp; pub use crate::clock::Timestamp;
pub use crate::crypto::hash::Digest;
pub use crate::crypto::{PublicKey, Signature, Signer}; pub use crate::crypto::{PublicKey, Signature, Signer};
pub use crate::deserializer::Deserializer; pub use crate::deserializer::Deserializer;
pub use crate::identity::{Did, Id}; pub use crate::identity::{Did, Id};

View File

@ -12,7 +12,6 @@ use std::{io, mem};
use byteorder::{NetworkEndian, ReadBytesExt, WriteBytesExt}; use byteorder::{NetworkEndian, ReadBytesExt, WriteBytesExt};
use crate::crypto::hash::Digest;
use crate::crypto::{PublicKey, Signature, Unverified}; use crate::crypto::{PublicKey, Signature, Unverified};
use crate::git; use crate::git;
use crate::git::fmt; use crate::git::fmt;
@ -189,12 +188,6 @@ impl Encode for git::Url {
} }
} }
impl Encode for Digest {
fn encode<W: io::Write + ?Sized>(&self, writer: &mut W) -> Result<usize, io::Error> {
self.as_ref().encode(writer)
}
}
impl Encode for Id { impl Encode for Id {
fn encode<W: io::Write + ?Sized>(&self, writer: &mut W) -> Result<usize, io::Error> { fn encode<W: io::Write + ?Sized>(&self, writer: &mut W) -> Result<usize, io::Error> {
self.deref().encode(writer) self.deref().encode(writer)
@ -396,14 +389,6 @@ impl Decode for Id {
} }
} }
impl Decode for Digest {
fn decode<R: io::Read + ?Sized>(reader: &mut R) -> Result<Self, Error> {
let bytes: [u8; 32] = Decode::decode(reader)?;
Ok(Self::from(bytes))
}
}
impl Encode for filter::Filter { impl Encode for filter::Filter {
fn encode<W: io::Write + ?Sized>(&self, writer: &mut W) -> Result<usize, io::Error> { fn encode<W: io::Write + ?Sized>(&self, writer: &mut W) -> Result<usize, io::Error> {
let mut n = 0; let mut n = 0;
@ -531,11 +516,6 @@ mod tests {
assert_eq!(deserialize::<Id>(&serialize(&input)).unwrap(), input); assert_eq!(deserialize::<Id>(&serialize(&input)).unwrap(), input);
} }
#[quickcheck]
fn prop_digest(input: Digest) {
assert_eq!(deserialize::<Digest>(&serialize(&input)).unwrap(), input);
}
#[quickcheck] #[quickcheck]
fn prop_refs(input: Refs) { fn prop_refs(input: Refs) {
assert_eq!(deserialize::<Refs>(&serialize(&input)).unwrap(), input); assert_eq!(deserialize::<Refs>(&serialize(&input)).unwrap(), input);