diff --git a/src/keys.rs b/src/keys.rs index ef984056..a72de0f0 100644 --- a/src/keys.rs +++ b/src/keys.rs @@ -2,7 +2,7 @@ use std::{cmp::Ordering, fmt, str::FromStr}; -use ed25519_dalek::{Signature, SignatureError, Signer, SigningKey, VerifyingKey}; +use iroh::{KeyParsingError, PublicKey, SecretKey, Signature, SignatureError}; use rand::CryptoRng; use serde::{Deserialize, Serialize}; @@ -10,21 +10,23 @@ use crate::store::PublicKeyStore; /// Author key to insert entries in a [`crate::Replica`] /// -/// Internally, an author is a [`SigningKey`] which is used to sign entries. +/// Internally, an author wraps an [`iroh::SecretKey`] used to sign entries. #[derive(Clone, Serialize, Deserialize)] pub struct Author { - signing_key: SigningKey, + signing_key: SecretKey, } impl Author { /// Create a new [`Author`] with a random key. pub fn new(rng: &mut R) -> Self { - let signing_key = SigningKey::generate(rng); + let mut bytes = [0u8; 32]; + rng.fill_bytes(&mut bytes); + let signing_key = SecretKey::from_bytes(&bytes); Author { signing_key } } /// Create an [`Author`] from a byte array. pub fn from_bytes(bytes: &[u8; 32]) -> Self { - SigningKey::from_bytes(bytes).into() + SecretKey::from_bytes(bytes).into() } /// Returns the [`Author`] byte representation. @@ -34,7 +36,7 @@ impl Author { /// Get the [`AuthorPublicKey`] for this author. pub fn public_key(&self) -> AuthorPublicKey { - AuthorPublicKey(self.signing_key.verifying_key()) + AuthorPublicKey(self.signing_key.public()) } /// Get the [`AuthorId`] for this author. @@ -49,22 +51,22 @@ impl Author { /// Strictly verify a signature on a message with this [`Author`]'s public key. pub fn verify(&self, msg: &[u8], signature: &Signature) -> Result<(), SignatureError> { - self.signing_key.verify_strict(msg, signature) + self.signing_key.public().verify(msg, signature) } } /// Identifier for an [`Author`] /// -/// This is the corresponding [`VerifyingKey`] for an author. It is used as an identifier, and can -/// be used to verify [`Signature`]s. -#[derive(Default, Clone, Copy, PartialEq, Eq, Serialize, Deserialize, Hash, derive_more::From)] -pub struct AuthorPublicKey(VerifyingKey); +/// This is the corresponding [`iroh::PublicKey`] for an author. It is used as an identifier, +/// and can be used to verify [`Signature`]s. +#[derive(Clone, Copy, PartialEq, Eq, Serialize, Deserialize, Hash, derive_more::From)] +pub struct AuthorPublicKey(PublicKey); impl AuthorPublicKey { /// Verify that a signature matches the `msg` bytes and was created with the [`Author`] /// that corresponds to this [`AuthorId`]. pub fn verify(&self, msg: &[u8], signature: &Signature) -> Result<(), SignatureError> { - self.0.verify_strict(msg, signature) + self.0.verify(msg, signature) } /// Get the byte representation of this [`AuthorId`]. @@ -74,34 +76,35 @@ impl AuthorPublicKey { /// Create from a slice of bytes. /// - /// Will return an error if the input bytes do not represent a valid [`ed25519_dalek`] + /// Will return an error if the input bytes do not represent a valid ed25519 /// curve point. Will never fail for a byte array returned from [`Self::as_bytes`]. - /// See [`VerifyingKey::from_bytes`] for details. - pub fn from_bytes(bytes: &[u8; 32]) -> Result { - Ok(AuthorPublicKey(VerifyingKey::from_bytes(bytes)?)) + /// See [`iroh::PublicKey::from_bytes`] for details. + pub fn from_bytes(bytes: &[u8; 32]) -> Result { + Ok(AuthorPublicKey(PublicKey::from_bytes(bytes)?)) } } /// Namespace key of a [`crate::Replica`]. /// /// Holders of this key can insert new entries into a [`crate::Replica`]. -/// Internally, a [`NamespaceSecret`] is a [`SigningKey`] which is used to sign entries. +/// Internally, a [`NamespaceSecret`] wraps an [`iroh::SecretKey`] used to sign entries. #[derive(Clone, Serialize, Deserialize)] pub struct NamespaceSecret { - signing_key: SigningKey, + signing_key: SecretKey, } impl NamespaceSecret { /// Create a new [`NamespaceSecret`] with a random key. pub fn new(rng: &mut R) -> Self { - let signing_key = SigningKey::generate(rng); - + let mut bytes = [0u8; 32]; + rng.fill_bytes(&mut bytes); + let signing_key = SecretKey::from_bytes(&bytes); NamespaceSecret { signing_key } } /// Create a [`NamespaceSecret`] from a byte array. pub fn from_bytes(bytes: &[u8; 32]) -> Self { - SigningKey::from_bytes(bytes).into() + SecretKey::from_bytes(bytes).into() } /// Returns the [`NamespaceSecret`] byte representation. @@ -111,7 +114,7 @@ impl NamespaceSecret { /// Get the [`NamespacePublicKey`] for this namespace. pub fn public_key(&self) -> NamespacePublicKey { - NamespacePublicKey(self.signing_key.verifying_key()) + NamespacePublicKey(self.signing_key.public()) } /// Get the [`NamespaceId`] for this namespace. @@ -126,20 +129,20 @@ impl NamespaceSecret { /// Strictly verify a signature on a message with this [`NamespaceSecret`]'s public key. pub fn verify(&self, msg: &[u8], signature: &Signature) -> Result<(), SignatureError> { - self.signing_key.verify_strict(msg, signature) + self.signing_key.public().verify(msg, signature) } } -/// The corresponding [`VerifyingKey`] for a [`NamespaceSecret`]. +/// The corresponding [`iroh::PublicKey`] for a [`NamespaceSecret`]. /// It is used as an identifier, and can be used to verify [`Signature`]s. -#[derive(Default, Copy, Clone, PartialEq, Eq, Serialize, Deserialize, Hash, derive_more::From)] -pub struct NamespacePublicKey(VerifyingKey); +#[derive(Copy, Clone, PartialEq, Eq, Serialize, Deserialize, Hash, derive_more::From)] +pub struct NamespacePublicKey(PublicKey); impl NamespacePublicKey { /// Verify that a signature matches the `msg` bytes and was created with the [`NamespaceSecret`] /// that corresponds to this [`NamespaceId`]. pub fn verify(&self, msg: &[u8], signature: &Signature) -> Result<(), SignatureError> { - self.0.verify_strict(msg, signature) + self.0.verify(msg, signature) } /// Get the byte representation of this [`NamespaceId`]. @@ -149,11 +152,11 @@ impl NamespacePublicKey { /// Create from a slice of bytes. /// - /// Will return an error if the input bytes do not represent a valid [`ed25519_dalek`] + /// Will return an error if the input bytes do not represent a valid ed25519 /// curve point. Will never fail for a byte array returned from [`Self::as_bytes`]. - /// See [`VerifyingKey::from_bytes`] for details. - pub fn from_bytes(bytes: &[u8; 32]) -> Result { - Ok(NamespacePublicKey(VerifyingKey::from_bytes(bytes)?)) + /// See [`iroh::PublicKey::from_bytes`] for details. + pub fn from_bytes(bytes: &[u8; 32]) -> Result { + Ok(NamespacePublicKey(PublicKey::from_bytes(bytes)?)) } } @@ -267,14 +270,14 @@ impl FromStr for NamespacePublicKey { } } -impl From for Author { - fn from(signing_key: SigningKey) -> Self { +impl From for Author { + fn from(signing_key: SecretKey) -> Self { Self { signing_key } } } -impl From for NamespaceSecret { - fn from(signing_key: SigningKey) -> Self { +impl From for NamespaceSecret { + fn from(signing_key: SecretKey) -> Self { Self { signing_key } } } @@ -376,18 +379,18 @@ impl AuthorId { /// Convert into [`AuthorPublicKey`] by fetching from a [`PublicKeyStore`]. /// - /// Fails if the bytes of this [`AuthorId`] are not a valid [`ed25519_dalek`] curve point. + /// Fails if the bytes of this [`AuthorId`] are not a valid ed25519 curve point. pub fn public_key( &self, store: &S, - ) -> Result { + ) -> Result { store.author_key(self) } /// Convert into [`AuthorPublicKey`]. /// - /// Fails if the bytes of this [`AuthorId`] are not a valid [`ed25519_dalek`] curve point. - pub fn into_public_key(&self) -> Result { + /// Fails if the bytes of this [`AuthorId`] are not a valid ed25519 curve point. + pub fn into_public_key(&self) -> Result { AuthorPublicKey::from_bytes(&self.0) } @@ -411,18 +414,18 @@ impl NamespaceId { /// Convert into [`NamespacePublicKey`] by fetching from a [`PublicKeyStore`]. /// - /// Fails if the bytes of this [`NamespaceId`] are not a valid [`ed25519_dalek`] curve point. + /// Fails if the bytes of this [`NamespaceId`] are not a valid ed25519 curve point. pub fn public_key( &self, store: &S, - ) -> Result { + ) -> Result { store.namespace_key(self) } /// Convert into [`NamespacePublicKey`]. /// - /// Fails if the bytes of this [`NamespaceId`] are not a valid [`ed25519_dalek`] curve point. - pub fn into_public_key(&self) -> Result { + /// Fails if the bytes of this [`NamespaceId`] are not a valid ed25519 curve point. + pub fn into_public_key(&self) -> Result { NamespacePublicKey::from_bytes(&self.0) } @@ -491,14 +494,14 @@ impl From for NamespaceId { } impl TryFrom for NamespacePublicKey { - type Error = SignatureError; + type Error = KeyParsingError; fn try_from(value: NamespaceId) -> Result { Self::from_bytes(&value.0) } } impl TryFrom for AuthorPublicKey { - type Error = SignatureError; + type Error = KeyParsingError; fn try_from(value: AuthorId) -> Result { Self::from_bytes(&value.0) } diff --git a/src/store/fs.rs b/src/store/fs.rs index 46485d2e..f72f1e6a 100644 --- a/src/store/fs.rs +++ b/src/store/fs.rs @@ -9,7 +9,7 @@ use std::{ }; use anyhow::{anyhow, Result}; -use ed25519_dalek::{SignatureError, VerifyingKey}; +use iroh::{KeyParsingError, PublicKey}; use iroh_blobs::Hash; use n0_future::time::SystemTime; use rand::CryptoRng; @@ -611,7 +611,7 @@ impl Store { } impl PublicKeyStore for Store { - fn public_key(&self, id: &[u8; 32]) -> Result { + fn public_key(&self, id: &[u8; 32]) -> Result { self.pubkeys.public_key(id) } } @@ -648,7 +648,7 @@ impl<'a> StoreInstance<'a> { } impl PublicKeyStore for StoreInstance<'_> { - fn public_key(&self, id: &[u8; 32]) -> std::result::Result { + fn public_key(&self, id: &[u8; 32]) -> std::result::Result { self.store.public_key(id) } } diff --git a/src/store/pubkeys.rs b/src/store/pubkeys.rs index 005e8675..89b823fc 100644 --- a/src/store/pubkeys.rs +++ b/src/store/pubkeys.rs @@ -3,51 +3,51 @@ use std::{ sync::{Arc, RwLock}, }; -use ed25519_dalek::{SignatureError, VerifyingKey}; +use iroh::{KeyParsingError, PublicKey}; use crate::{AuthorId, AuthorPublicKey, NamespaceId, NamespacePublicKey}; /// Store trait for expanded public keys for authors and namespaces. /// -/// Used to cache [`ed25519_dalek::VerifyingKey`]. +/// Used to cache [`iroh::PublicKey`]. /// /// This trait is implemented for the unit type `()`, where no caching is used. pub trait PublicKeyStore { - /// Convert a byte array into a [`VerifyingKey`]. + /// Convert a byte array into a [`iroh::PublicKey`]. /// - /// New keys are inserted into the [`PublicKeyStore ] and reused on subsequent calls. - fn public_key(&self, id: &[u8; 32]) -> Result; + /// New keys are inserted into the [`PublicKeyStore`] and reused on subsequent calls. + fn public_key(&self, id: &[u8; 32]) -> Result; /// Convert a [`NamespaceId`] into a [`NamespacePublicKey`]. /// - /// New keys are inserted into the [`PublicKeyStore ] and reused on subsequent calls. - fn namespace_key(&self, bytes: &NamespaceId) -> Result { + /// New keys are inserted into the [`PublicKeyStore`] and reused on subsequent calls. + fn namespace_key(&self, bytes: &NamespaceId) -> Result { self.public_key(bytes.as_bytes()).map(Into::into) } /// Convert a [`AuthorId`] into a [`AuthorPublicKey`]. /// - /// New keys are inserted into the [`PublicKeyStore ] and reused on subsequent calls. - fn author_key(&self, bytes: &AuthorId) -> Result { + /// New keys are inserted into the [`PublicKeyStore`] and reused on subsequent calls. + fn author_key(&self, bytes: &AuthorId) -> Result { self.public_key(bytes.as_bytes()).map(Into::into) } } impl PublicKeyStore for &T { - fn public_key(&self, id: &[u8; 32]) -> Result { + fn public_key(&self, id: &[u8; 32]) -> Result { (*self).public_key(id) } } impl PublicKeyStore for &mut T { - fn public_key(&self, id: &[u8; 32]) -> Result { + fn public_key(&self, id: &[u8; 32]) -> Result { PublicKeyStore::public_key(*self, id) } } impl PublicKeyStore for () { - fn public_key(&self, id: &[u8; 32]) -> Result { - VerifyingKey::from_bytes(id) + fn public_key(&self, id: &[u8; 32]) -> Result { + PublicKey::from_bytes(id) } } @@ -55,15 +55,15 @@ impl PublicKeyStore for () { // TODO: Make max number of keys stored configurable. #[derive(Debug, Clone, Default)] pub struct MemPublicKeyStore { - keys: Arc>>, + keys: Arc>>, } impl PublicKeyStore for MemPublicKeyStore { - fn public_key(&self, bytes: &[u8; 32]) -> Result { + fn public_key(&self, bytes: &[u8; 32]) -> Result { if let Some(id) = self.keys.read().unwrap().get(bytes) { return Ok(*id); } - let id = VerifyingKey::from_bytes(bytes)?; + let id = PublicKey::from_bytes(bytes)?; self.keys.write().unwrap().insert(*bytes, id); Ok(id) } diff --git a/src/sync.rs b/src/sync.rs index 3506b93d..d6f99f4c 100644 --- a/src/sync.rs +++ b/src/sync.rs @@ -14,13 +14,12 @@ use std::{ }; use bytes::{Bytes, BytesMut}; -use ed25519_dalek::SignatureError; // `iroh::Signature` is a newtype wrapper around `ed25519_dalek::Signature` with a // hand-written, wire-stable `serialize_tuple` serde impl (the same impl every other // iroh crate uses for handshake / discovery payloads). Embedding it inside // `EntrySignature` — rather than the raw dalek type — keeps the on-wire // `SignedEntry` format independent of upstream `ed25519` serde changes. -use iroh::Signature; +use iroh::{KeyParsingError, Signature, SignatureError}; use iroh_blobs::Hash; use n0_future::{ time::{Duration, SystemTime}, @@ -668,6 +667,17 @@ pub enum InsertError { Closed, } +/// Reason why verifying a [`SignedEntry`] failed. +#[derive(thiserror::Error, Debug)] +pub enum SignedEntryVerifyError { + /// One of the entry's public key bytes is not a valid ed25519 curve point. + #[error(transparent)] + KeyParsing(#[from] KeyParsingError), + /// One of the entry's signatures failed verification against the recovered key. + #[error(transparent)] + Signature(#[from] SignatureError), +} + /// Reason why entry validation failed #[derive(thiserror::Error, Debug)] pub enum ValidationFailure { @@ -748,12 +758,16 @@ impl SignedEntry { } /// Verify the signatures on this entry. - pub fn verify(&self, store: &S) -> Result<(), SignatureError> { + pub fn verify( + &self, + store: &S, + ) -> Result<(), SignedEntryVerifyError> { self.signature.verify( &self.entry, &self.entry.namespace().public_key(store)?, &self.entry.author().public_key(store)?, - ) + )?; + Ok(()) } /// Get the signature. @@ -866,12 +880,8 @@ impl EntrySignature { author: &AuthorPublicKey, ) -> Result<(), SignatureError> { let bytes = entry.to_vec(); - let namespace_signature = - ed25519_dalek::Signature::from_bytes(&self.namespace_signature.to_bytes()); - let author_signature = - ed25519_dalek::Signature::from_bytes(&self.author_signature.to_bytes()); - namespace.verify(&bytes, &namespace_signature)?; - author.verify(&bytes, &author_signature)?; + namespace.verify(&bytes, &self.namespace_signature)?; + author.verify(&bytes, &self.author_signature)?; Ok(()) }