diff --git a/Cargo.lock b/Cargo.lock index 2822d07794c31f548556ccc29f92786ff919d222..c87308c3e0cf5a9e4e336c6fd6a81f77b40ea35b 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -33,7 +33,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" [[package]] name = "duniter-keys" -version = "0.2.0" +version = "0.3.0" dependencies = [ "base58 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)", "base64 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)", diff --git a/keys/Cargo.toml b/keys/Cargo.toml index 3d6199a2c984a17f9d4ac8492a7e2f212f8908f8..4b41f9dca79740bce4565b3b61ba72f5114d01e6 100644 --- a/keys/Cargo.toml +++ b/keys/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "duniter-keys" -version = "0.2.0" +version = "0.3.0" authors = ["nanocryk <nanocryk@duniter.org>"] description = "Manage cryptographic keys for the Duniter project." repository = "https://git.duniter.org/nodes/rust/duniter-rs" diff --git a/keys/ed25519.rs b/keys/ed25519.rs index 8bb13a9108dcc9a44544c50a8b522ca3fc5c2b78..72cc61fbaf750f932ab1ca25536f21c55b3b481b 100644 --- a/keys/ed25519.rs +++ b/keys/ed25519.rs @@ -29,7 +29,7 @@ use base64; use base64::DecodeError; use crypto; -use super::BaseConvertionError; +use super::{BaseConvertionError, PrivateKey as PrivateKeyMethods, PublicKey as PublicKeyMethods}; /// Store a ed25519 signature. #[derive(Clone, Copy)] @@ -211,9 +211,54 @@ impl super::PrivateKey for PrivateKey { } } +/// Store a ed25519 cryptographic key pair (`PublicKey` + `PrivateKey`) +#[derive(Debug, Copy, Clone)] +pub struct KeyPair { + /// Store a Ed25519 public key. + pub pubkey: PublicKey, + /// Store a Ed25519 private key. + pub privkey: PrivateKey, +} + +impl Display for KeyPair { + fn fmt(&self, f: &mut Formatter) -> Result<(), Error> { + write!(f, "({}, hidden)", self.pubkey.to_base58()) + } +} + +impl PartialEq<KeyPair> for KeyPair { + fn eq(&self, other: &KeyPair) -> bool { + self.pubkey.eq(&other.pubkey) && self.privkey.eq(&other.privkey) + } +} + +impl Eq for KeyPair {} + +impl super::KeyPair for KeyPair { + type Signature = Signature; + type PublicKey = PublicKey; + type PrivateKey = PrivateKey; + + fn public_key(&self) -> PublicKey { + self.pubkey + } + + fn private_key(&self) -> PrivateKey { + self.privkey + } + + fn sign(&self, message: &[u8]) -> Signature { + self.private_key().sign(message) + } + + fn verify(&self, message: &[u8], signature: &Self::Signature) -> bool { + self.public_key().verify(message, signature) + } +} + /// Keypair generator with given parameters for `scrypt` keypair function. #[derive(Debug, Copy, Clone)] -pub struct KeyPairGenerator { +pub struct KeyPairFromSaltedPasswordGenerator { /// The log2 of the Scrypt parameter `N`. log_n: u8, /// The Scrypt parameter `r` @@ -222,32 +267,32 @@ pub struct KeyPairGenerator { p: u32, } -impl KeyPairGenerator { +impl KeyPairFromSaltedPasswordGenerator { /// Create a `KeyPairGenerator` with default arguments `(log_n: 12, r: 16, p: 1)` - pub fn with_default_parameters() -> KeyPairGenerator { - KeyPairGenerator { + pub fn with_default_parameters() -> KeyPairFromSaltedPasswordGenerator { + KeyPairFromSaltedPasswordGenerator { log_n: 12, r: 16, p: 1, } } - /// Create a `KeyPairGenerator` with given arguments. + /// Create a `KeyPairFromSaltedPasswordGenerator` with given arguments. /// /// # Arguments /// /// - log_n - The log2 of the Scrypt parameter N /// - r - The Scrypt parameter r /// - p - The Scrypt parameter p - pub fn with_parameters(log_n: u8, r: u32, p: u32) -> KeyPairGenerator { - KeyPairGenerator { log_n, r, p } + pub fn with_parameters(log_n: u8, r: u32, p: u32) -> KeyPairFromSaltedPasswordGenerator { + KeyPairFromSaltedPasswordGenerator { log_n, r, p } } /// Create a keypair based on a given password and salt. /// /// The [`PublicKey`](struct.PublicKey.html) will be able to verify messaged signed with /// the [`PrivateKey`](struct.PrivateKey.html). - pub fn generate(&self, password: &[u8], salt: &[u8]) -> (PrivateKey, PublicKey) { + pub fn generate(&self, password: &[u8], salt: &[u8]) -> KeyPair { let mut seed = [0u8; 32]; crypto::scrypt::scrypt( password, @@ -257,14 +302,17 @@ impl KeyPairGenerator { ); let (private, public) = crypto::ed25519::keypair(&seed); - (PrivateKey(private), PublicKey(public)) + KeyPair { + pubkey: PublicKey(public), + privkey: PrivateKey(private), + } } } #[cfg(test)] mod tests { use super::*; - use {PrivateKey, PublicKey, Signature}; + use {KeyPair, Signature}; #[test] fn base58_private_key() { @@ -284,12 +332,12 @@ mod tests { super::PrivateKey::from_base58(private58).unwrap() ); - assert_eq!( - super::PrivateKey::from_base58( - "468Q1XtTq7h84NorZdWBZFJrGkB18CbmbHr9tkp9snt5GiERP7ySs3wM8myLccbAAGejgMRC9rqnXuW3iA\ - fZACm7djh", - ).unwrap_err(), - BaseConvertionError::InvalidKeyLendth(67, 64) + assert_eq!( + super::PrivateKey::from_base58( + "468Q1XtTq7h84NorZdWBZFJrGkB18CbmbHr9tkp9snt5GiERP7ySs3wM8myLccbAAGejgMRC9rqnXuW3iA\ + fZACm7djh", + ).unwrap_err(), + BaseConvertionError::InvalidKeyLendth(67, 64) ); assert_eq!( super::PrivateKey::from_base58( @@ -381,12 +429,12 @@ mod tests { MMmQCRerlF/3pc4sAcsnexsxBseA/3lY03KlONqJBAg==", ).unwrap(); - let message = "Version: 10 -Type: Identity -Currency: duniter_unit_test_currency -Issuer: DNann1Lh55eZMEDXeYt59bzHbA3NJR46DeQYCS2qQdLV -UniqueID: tic -Timestamp: 0-E3B0C44298FC1C149AFBF4C8996FB92427AE41E4649B934CA495991B7852B855 + let message = "Version: 10 +Type: Identity +Currency: duniter_unit_test_currency +Issuer: DNann1Lh55eZMEDXeYt59bzHbA3NJR46DeQYCS2qQdLV +UniqueID: tic +Timestamp: 0-E3B0C44298FC1C149AFBF4C8996FB92427AE41E4649B934CA495991B7852B855 "; let sig = prikey.sign(message.as_bytes()); @@ -394,4 +442,21 @@ Timestamp: 0-E3B0C44298FC1C149AFBF4C8996FB92427AE41E4649B934CA495991B7852B855 assert_eq!(sig, expected_signature); assert!(pubkey.verify(message.as_bytes(), &sig)); } + + #[test] + fn keypair_generate_sign_and_verify() { + let keypair = KeyPairFromSaltedPasswordGenerator::with_default_parameters() + .generate("password".as_bytes(), "salt".as_bytes()); + + let message = "Version: 10 +Type: Identity +Currency: duniter_unit_test_currency +Issuer: DNann1Lh55eZMEDXeYt59bzHbA3NJR46DeQYCS2qQdLV +UniqueID: tic +Timestamp: 0-E3B0C44298FC1C149AFBF4C8996FB92427AE41E4649B934CA495991B7852B855 +"; + + let sig = keypair.sign(message.as_bytes()); + assert!(keypair.verify(message.as_bytes(), &sig)); + } } diff --git a/keys/lib.rs b/keys/lib.rs index 49842a68658245ab861addd34c659bd00596a0ab..9b41694458270e08c5381919e60b93f31c1bda12 100644 --- a/keys/lib.rs +++ b/keys/lib.rs @@ -21,21 +21,21 @@ //! # Usage //! //! ``` -//! use duniter_keys::{Signature, PublicKey, PrivateKey}; -//! use duniter_keys::ed25519::KeyPairGenerator; +//! use duniter_keys::{Signature, PublicKey, PrivateKey, KeyPair}; +//! use duniter_keys::ed25519::KeyPairFromSaltedPasswordGenerator; //! -//! let generator = KeyPairGenerator::with_default_parameters(); +//! let generator = KeyPairFromSaltedPasswordGenerator::with_default_parameters(); //! -//! let (private_key, public_key) = generator.generate( +//! let keypair = generator.generate( //! b"password", //! b"salt" //! ); //! //! let message = "Hello, world!"; //! -//! let signature = private_key.sign(&message.as_bytes()); +//! let signature = keypair.sign(&message.as_bytes()); //! -//! assert!(public_key.verify(&message.as_bytes(), &signature)); +//! assert!(keypair.pubkey.verify(&message.as_bytes(), &signature)); //! ``` //! //! # Format @@ -147,3 +147,25 @@ pub trait PrivateKey: Clone + Display + Debug + PartialEq + Eq + ToBase58 { /// Sign a message with this private key. fn sign(&self, message: &[u8]) -> Self::Signature; } + +/// Store a cryptographic key pair (`PublicKey` + `PrivateKey`) +pub trait KeyPair: Clone + Display + Debug + PartialEq + Eq { + /// Signature type of associated cryptosystem. + type Signature: Signature; + /// PublicKey type of associated cryptosystem. + type PublicKey: PublicKey; + /// PrivateKey type of associated cryptosystem. + type PrivateKey: PrivateKey; + + /// Get `PublicKey` + fn public_key(&self) -> Self::PublicKey; + + /// Get `PrivateKey` + fn private_key(&self) -> Self::PrivateKey; + + /// Sign a message with private key. + fn sign(&self, message: &[u8]) -> Self::Signature; + + /// Verify a signature with public key. + fn verify(&self, message: &[u8], signature: &Self::Signature) -> bool; +}