From 7e1b2b6222ab5b3c98d97ca20b47996d7d961022 Mon Sep 17 00:00:00 2001 From: Alexis Sellier Date: Thu, 16 Feb 2023 23:47:48 +0100 Subject: [PATCH] crypto: Cleanup `hash` module Remove `Digest` type and use `ssh-key` for formatting public ssh key. --- Cargo.lock | 1 - radicle-cli/examples/rad-auth.md | 2 +- radicle-crypto/Cargo.toml | 1 - radicle-crypto/src/hash.rs | 69 ---------------------------- radicle-crypto/src/lib.rs | 8 +++- radicle-crypto/src/ssh.rs | 28 +++-------- radicle-crypto/src/test/arbitrary.rs | 9 +--- radicle-node/src/lib.rs | 1 - radicle-node/src/wire.rs | 20 -------- 9 files changed, 16 insertions(+), 123 deletions(-) delete mode 100644 radicle-crypto/src/hash.rs diff --git a/Cargo.lock b/Cargo.lock index 3fc1210e..6745c554 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1929,7 +1929,6 @@ dependencies = [ "qcheck-macros", "radicle-ssh", "serde", - "sha2 0.10.6", "sqlite", "ssh-key", "tempfile", diff --git a/radicle-cli/examples/rad-auth.md b/radicle-cli/examples/rad-auth.md index 43267738..e7930220 100644 --- a/radicle-cli/examples/rad-auth.md +++ b/radicle-cli/examples/rad-auth.md @@ -21,7 +21,7 @@ $ rad self ID did:key:z6MknSLrJoTcukLrE435hVNQT4JUhbvWLX4kUzqkEStBU8Vi Node ID z6MknSLrJoTcukLrE435hVNQT4JUhbvWLX4kUzqkEStBU8Vi Key (hash) SHA256:UIedaL6Cxm6OUErh9GQUzzglSk7VpQlVTI1TAFB/HWA -Key (full) AAAAC3NzaC1lZDI1NTE5AAAAIHahWSBEpuT1ESZbynOmBNkLBSnR32Ar4woZqSV2YNH1 +Key (full) ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIHahWSBEpuT1ESZbynOmBNkLBSnR32Ar4woZqSV2YNH1 Storage (git) [..]/storage Storage (keys) [..]/keys Node (socket) [..]/node/radicle.sock diff --git a/radicle-crypto/Cargo.toml b/radicle-crypto/Cargo.toml index af2c102a..34c8207b 100644 --- a/radicle-crypto/Cargo.toml +++ b/radicle-crypto/Cargo.toml @@ -18,7 +18,6 @@ cyphernet = { version = "0.2.0", optional = true } multibase = { version = "0.9.1" } ec25519 = { version = "0.1.0", features = [] } serde = { version = "1", features = ["derive"] } -sha2 = { version = "0.10.2" } sqlite = { version = "0.30.3", optional = true } thiserror = { version = "1" } zeroize = { version = "1.5.7" } diff --git a/radicle-crypto/src/hash.rs b/radicle-crypto/src/hash.rs deleted file mode 100644 index 5c676f27..00000000 --- a/radicle-crypto/src/hash.rs +++ /dev/null @@ -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 { - let bytes: [u8; 32] = bytes - .try_into() - .map_err(|_| DecodeError::InvalidLength(bytes.len()))?; - - Ok(bytes.into()) - } -} - -impl From::OutputSize>> for Digest { - fn from(array: GenericArray::OutputSize>) -> Self { - Self(array.into()) - } -} diff --git a/radicle-crypto/src/lib.rs b/radicle-crypto/src/lib.rs index 354ea87a..f6cbbfcb 100644 --- a/radicle-crypto/src/lib.rs +++ b/radicle-crypto/src/lib.rs @@ -8,7 +8,6 @@ use thiserror::Error; pub use ed25519::{edwards25519, Error, KeyPair, Seed}; -pub mod hash; #[cfg(feature = "ssh")] pub mod ssh; #[cfg(any(test, feature = "test"))] @@ -166,6 +165,13 @@ impl cyphernet::display::MultiDisplay for PublicKe } } +#[cfg(feature = "ssh")] +impl From for ssh_key::PublicKey { + fn from(key: PublicKey) -> Self { + ssh_key::PublicKey::from(ssh_key::public::Ed25519PublicKey(**key)) + } +} + #[cfg(feature = "cyphernet")] impl cyphernet::EcPk for PublicKey { const COMPRESSED_LEN: usize = 32; diff --git a/radicle-crypto/src/ssh.rs b/radicle-crypto/src/ssh.rs index e77f9234..0109b1f1 100644 --- a/radicle-crypto/src/ssh.rs +++ b/radicle-crypto/src/ssh.rs @@ -16,35 +16,20 @@ use crate::PublicKey; pub use keystore::{Keystore, Passphrase}; pub mod fmt { - use radicle_ssh::encoding::Encoding as _; - use crate::PublicKey; /// Get the SSH long key from a public key. /// This is the output of `ssh-add -L`. pub fn key(key: &PublicKey) -> String { - let mut buf = Vec::new(); - - buf.extend_ssh_string(b"ssh-ed25519"); - buf.extend_ssh_string(key.as_ref()); - - base64::encode_config(buf, base64::STANDARD_NO_PAD) + ssh_key::PublicKey::from(*key).to_string() } /// Get the SSH key fingerprint from a public key. /// This is the output of `ssh-add -l`. pub fn fingerprint(key: &PublicKey) -> String { - use sha2::Digest; - - let mut buf = Vec::new(); - - 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}") + ssh_key::PublicKey::from(*key) + .fingerprint(Default::default()) + .to_string() } #[cfg(test)] @@ -61,7 +46,7 @@ pub mod fmt { assert_eq!( key(&pk), - "AAAAC3NzaC1lZDI1NTE5AAAAINDoXIrhcnRjnLGUXUFdxhkuy08lkTOwrj2IoGsEX6+Q" + "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAINDoXIrhcnRjnLGUXUFdxhkuy08lkTOwrj2IoGsEX6+Q" ); } @@ -431,7 +416,8 @@ AAAAQI84aPZsXxlQigpy1/Y/iJSmHSS//CIgvqvUMQIb/TM2vhCKruduH0cK02k9G8wOI+ EUMf2bSDyxbJyZThOEiAs= -----END SSH SIGNATURE-----"; - let public_key = "AAAAC3NzaC1lZDI1NTE5AAAAIL460KIEccS4881p7PPpiiQBsxF+H5tgC6De6crw9rbU"; + let public_key = + "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIL460KIEccS4881p7PPpiiQBsxF+H5tgC6De6crw9rbU"; let signature = ExtendedSignature::from_armored(armored).unwrap(); assert_eq!(signature.version, 1); diff --git a/radicle-crypto/src/test/arbitrary.rs b/radicle-crypto/src/test/arbitrary.rs index cb5e1872..452253ac 100644 --- a/radicle-crypto/src/test/arbitrary.rs +++ b/radicle-crypto/src/test/arbitrary.rs @@ -1,6 +1,6 @@ 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 { fn arbitrary(g: &mut qcheck::Gen) -> Self { @@ -21,10 +21,3 @@ impl Arbitrary for PublicKey { PublicKey(keypair.pk) } } - -impl Arbitrary for hash::Digest { - fn arbitrary(g: &mut qcheck::Gen) -> Self { - let bytes: Vec = Arbitrary::arbitrary(g); - hash::Digest::new(bytes) - } -} diff --git a/radicle-node/src/lib.rs b/radicle-node/src/lib.rs index 193b34ce..4e106ca8 100644 --- a/radicle-node/src/lib.rs +++ b/radicle-node/src/lib.rs @@ -23,7 +23,6 @@ pub use runtime::Runtime; pub mod prelude { pub use crate::bounded::BoundedVec; pub use crate::clock::Timestamp; - pub use crate::crypto::hash::Digest; pub use crate::crypto::{PublicKey, Signature, Signer}; pub use crate::deserializer::Deserializer; pub use crate::identity::{Did, Id}; diff --git a/radicle-node/src/wire.rs b/radicle-node/src/wire.rs index 8a3d31cf..b5f6e835 100644 --- a/radicle-node/src/wire.rs +++ b/radicle-node/src/wire.rs @@ -12,7 +12,6 @@ use std::{io, mem}; use byteorder::{NetworkEndian, ReadBytesExt, WriteBytesExt}; -use crate::crypto::hash::Digest; use crate::crypto::{PublicKey, Signature, Unverified}; use crate::git; use crate::git::fmt; @@ -189,12 +188,6 @@ impl Encode for git::Url { } } -impl Encode for Digest { - fn encode(&self, writer: &mut W) -> Result { - self.as_ref().encode(writer) - } -} - impl Encode for Id { fn encode(&self, writer: &mut W) -> Result { self.deref().encode(writer) @@ -396,14 +389,6 @@ impl Decode for Id { } } -impl Decode for Digest { - fn decode(reader: &mut R) -> Result { - let bytes: [u8; 32] = Decode::decode(reader)?; - - Ok(Self::from(bytes)) - } -} - impl Encode for filter::Filter { fn encode(&self, writer: &mut W) -> Result { let mut n = 0; @@ -531,11 +516,6 @@ mod tests { assert_eq!(deserialize::(&serialize(&input)).unwrap(), input); } - #[quickcheck] - fn prop_digest(input: Digest) { - assert_eq!(deserialize::(&serialize(&input)).unwrap(), input); - } - #[quickcheck] fn prop_refs(input: Refs) { assert_eq!(deserialize::(&serialize(&input)).unwrap(), input);