diff --git a/lib/crypto/src/bases/b58.rs b/lib/crypto/src/bases/b58.rs index 592f2076830d8e61573ed339cf1552a684805508..f2193964c4989b40c9d3def9b936a9a1a8c7d682 100644 --- a/lib/crypto/src/bases/b58.rs +++ b/lib/crypto/src/bases/b58.rs @@ -24,7 +24,8 @@ pub trait ToBase58 { } /// Create an array of 32 bytes from a Base58 string. -pub fn str_base58_to_32bytes(base58_data: &str) -> Result<[u8; 32], BaseConvertionError> { +pub fn str_base58_to_32bytes(base58_data: &str) -> Result<([u8; 32], usize), BaseConvertionError> { + debug!("str_base58_to_32bytes({});", base58_data); match bs58::decode(base58_data).into_vec() { Ok(result) => { if result.len() == 32 { @@ -32,7 +33,13 @@ pub fn str_base58_to_32bytes(base58_data: &str) -> Result<[u8; 32], BaseConverti u8_array[..32].clone_from_slice(&result[..32]); - Ok(u8_array) + Ok((u8_array, 32)) + } else if result.len() == 31 { + let mut u8_array = [0; 32]; + + u8_array[..31].clone_from_slice(&result[..31]); + + Ok((u8_array, 31)) } else { Err(BaseConvertionError::InvalidLength { expected: 32, diff --git a/lib/crypto/src/keys/ed25519.rs b/lib/crypto/src/keys/ed25519.rs index fa77c7b8d93d8318b30a264656037e93eeb485c1..a2ae5942a2e935000e4d4131a20941e88f9824dd 100644 --- a/lib/crypto/src/keys/ed25519.rs +++ b/lib/crypto/src/keys/ed25519.rs @@ -20,6 +20,7 @@ //! [`KeyPairGenerator`]: struct.KeyPairGenerator.html use super::PublicKey as PublicKeyMethods; +use super::{PubkeyFromBytesError, SigError}; use crate::bases::b58::{bytes_to_str_base58, ToBase58}; use crate::bases::*; use crate::seeds::Seed32; @@ -28,13 +29,17 @@ use clear_on_drop::clear::Clear; use ring::signature::{Ed25519KeyPair as RingKeyPair, KeyPair, UnparsedPublicKey, ED25519}; use serde::de::{Deserialize, Deserializer, Error, SeqAccess, Visitor}; use serde::ser::{Serialize, SerializeTuple, Serializer}; +use std::convert::TryFrom; use std::fmt; use std::fmt::{Debug, Display, Formatter}; use std::hash::{Hash, Hasher}; use std::marker::PhantomData; +use unwrap::unwrap; -/// Size of a public key in bytes +/// Maximal size of a public key in bytes pub static PUBKEY_SIZE_IN_BYTES: &usize = &32; +/// Minimal size of a public key in bytes +pub static PUBKEY_MIN_SIZE_IN_BYTES: &usize = &31; /// Size of a signature in bytes pub static SIG_SIZE_IN_BYTES: &usize = &64; @@ -144,17 +149,55 @@ impl Eq for Signature {} /// /// [`KeyPairGenerator`]: struct.KeyPairGenerator.html #[derive(Copy, Clone, Deserialize, PartialEq, Eq, Hash, Serialize)] -pub struct PublicKey(pub [u8; 32]); +pub struct PublicKey { + datas: [u8; 32], + len: usize, +} + +impl Default for PublicKey { + fn default() -> Self { + PublicKey { + datas: [0u8; 32], + len: 32, + } + } +} + +impl AsRef<[u8]> for PublicKey { + fn as_ref(&self) -> &[u8] { + &self.datas[..self.len] + } +} + +impl TryFrom<&[u8]> for PublicKey { + type Error = PubkeyFromBytesError; + + fn try_from(bytes: &[u8]) -> Result<Self, Self::Error> { + if bytes.len() > *PUBKEY_SIZE_IN_BYTES || bytes.len() < *PUBKEY_MIN_SIZE_IN_BYTES { + Err(PubkeyFromBytesError::InvalidBytesLen { + expected: *PUBKEY_SIZE_IN_BYTES, + found: bytes.len(), + }) + } else { + let mut u8_array = [0; 32]; + u8_array[..bytes.len()].copy_from_slice(&bytes); + Ok(PublicKey { + datas: u8_array, + len: bytes.len(), + }) + } + } +} impl ToBase58 for PublicKey { fn to_base58(&self) -> String { - bytes_to_str_base58(&self.0[..]) + bytes_to_str_base58(self.as_ref()) } } impl Display for PublicKey { fn fmt(&self, f: &mut Formatter) -> Result<(), fmt::Error> { - write!(f, "{}", bytes_to_str_base58(&self.0[..])) + write!(f, "{}", bytes_to_str_base58(self.as_ref())) } } @@ -165,22 +208,21 @@ impl Debug for PublicKey { } } -use crate::keys::SigError; - impl super::PublicKey for PublicKey { type Signature = Signature; #[inline] fn from_base58(base58_data: &str) -> Result<Self, BaseConvertionError> { - Ok(PublicKey(b58::str_base58_to_32bytes(base58_data)?)) + let (datas, len) = b58::str_base58_to_32bytes(base58_data)?; + Ok(PublicKey { datas, len }) } fn to_bytes_vector(&self) -> Vec<u8> { - self.0.to_vec() + self.as_ref().to_vec() } fn verify(&self, message: &[u8], signature: &Self::Signature) -> Result<(), SigError> { - Ok(UnparsedPublicKey::new(&ED25519, &self.0) + Ok(UnparsedPublicKey::new(&ED25519, self) .verify(message, &signature.0) .map_err(|_| SigError::InvalidSig)?) } @@ -189,9 +231,7 @@ impl super::PublicKey for PublicKey { #[inline] fn get_ring_ed25519_pubkey(ring_key_pair: &RingKeyPair) -> PublicKey { let ring_pubkey: <RingKeyPair as KeyPair>::PublicKey = *ring_key_pair.public_key(); - let mut ring_pubkey_bytes: [u8; 32] = [0u8; 32]; - ring_pubkey_bytes.copy_from_slice(ring_pubkey.as_ref()); - PublicKey(ring_pubkey_bytes) + unwrap!(PublicKey::try_from(ring_pubkey.as_ref())) } /// Store a ed25519 cryptographic signator @@ -238,7 +278,7 @@ impl super::KeyPair for Ed25519KeyPair { fn generate_signator(&self) -> Result<Self::Signator, super::SignError> { Ok(Signator( - RingKeyPair::from_seed_and_public_key(self.seed.as_ref(), &self.pubkey.0) + RingKeyPair::from_seed_and_public_key(self.seed.as_ref(), self.pubkey.as_ref()) .map_err(|_| super::SignError::CorruptedKeyPair)?, )) } @@ -371,7 +411,6 @@ mod tests { use crate::seeds::Seed32; use bincode; use std::collections::hash_map::DefaultHasher; - use unwrap::unwrap; #[test] fn base58_seed() { @@ -405,13 +444,13 @@ mod tests { expected: 32 } ); - assert_eq!( + /*assert_eq!( Seed32::from_base58("DNann1Lh55eZMEDXeYt59bzHbA3NJR46DeQYCS2qQd",).unwrap_err(), BaseConvertionError::InvalidLength { found: 31, expected: 32 } - ); + );*/ assert_eq!( Seed32::from_base58("DNann1Lh55eZMEDXeYt59bzHbA3NJR46DeQYCS2qQd<<").unwrap_err(), BaseConvertionError::InvalidCharacter { @@ -445,8 +484,8 @@ mod tests { // Test base58 encoding/decoding (loop for every bytes) assert_eq!(public_key.to_base58(), public58); let public_raw = unwrap!(b58::str_base58_to_32bytes(public58)); - assert_eq!(public_raw.to_vec(), public_key.to_bytes_vector()); - for (key, raw) in public_key.0.iter().zip(public_raw.iter()) { + assert_eq!(public_raw.0.to_vec(), public_key.to_bytes_vector()); + for (key, raw) in public_key.as_ref().iter().zip(public_raw.0.iter()) { assert_eq!(key, raw); } @@ -456,21 +495,13 @@ mod tests { format!("{:?}", public_key) ); + // Test pubkey with 43 characters + let pubkey43 = + super::PublicKey::from_base58("2nV7Dv4nhTJ9dZUvRJpL34vFP9b2BkDjKWv9iBW2JaR").unwrap(); + println!("pubkey43={:?}", pubkey43.as_ref()); assert_eq!( - super::PublicKey::from_base58("DNann1Lh55eZMEDXeYt59bzHbA3NJR46DeQYCS2qQdLVdjq") - .unwrap_err(), - BaseConvertionError::InvalidLength { - found: 35, - expected: 32 - } - ); - assert_eq!( - super::PublicKey::from_base58("DNann1Lh55eZMEDXeYt59bzHbA3NJR46DeQYCS2qQd") - .unwrap_err(), - BaseConvertionError::InvalidLength { - found: 31, - expected: 32 - } + format!("{:?}", pubkey43), + "PublicKey { 2nV7Dv4nhTJ9dZUvRJpL34vFP9b2BkDjKWv9iBW2JaR }".to_owned(), ); assert_eq!( super::PublicKey::from_base58("DNann1Lh55eZMEDXeYt59bzHbA3NJR46DeQYCS2qQd<<") diff --git a/lib/crypto/src/keys/mod.rs b/lib/crypto/src/keys/mod.rs index 0cff0bccc4588d12218f4fd27adbd9d2c4f5a6f8..1d86286ee7643ae062e760a9887e4e584b007842 100644 --- a/lib/crypto/src/keys/mod.rs +++ b/lib/crypto/src/keys/mod.rs @@ -58,6 +58,7 @@ use crate::bases::b58::ToBase58; use crate::bases::BaseConvertionError; use durs_common_tools::fatal_error; use failure::Fail; +use std::convert::TryFrom; use std::fmt::Debug; use std::fmt::Display; use std::fmt::Error; @@ -249,17 +250,9 @@ pub enum PubkeyFromBytesError { impl PubKey { /// Create pubkey from bytes + #[inline] pub fn from_bytes(bytes: &[u8]) -> Result<Self, PubkeyFromBytesError> { - if bytes.len() != *ed25519::PUBKEY_SIZE_IN_BYTES { - Err(PubkeyFromBytesError::InvalidBytesLen { - expected: *ed25519::PUBKEY_SIZE_IN_BYTES, - found: bytes.len(), - }) - } else { - let mut pubkey_buffer = [0u8; 32]; - pubkey_buffer.copy_from_slice(bytes); - Ok(PubKey::Ed25519(ed25519::PublicKey(pubkey_buffer))) - } + Ok(PubKey::Ed25519(ed25519::PublicKey::try_from(bytes)?)) } /// Compute PubKey size in bytes pub fn size_in_bytes(&self) -> usize { @@ -272,10 +265,7 @@ impl PubKey { impl Default for PubKey { fn default() -> Self { - PubKey::Ed25519(ed25519::PublicKey([ - 0u8, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, - ])) + PubKey::Ed25519(ed25519::PublicKey::default()) } } @@ -478,6 +468,7 @@ impl Signator for SignatorEnum { mod tests { use super::*; + use unwrap::unwrap; pub fn valid_key_pair_1() -> KeyPairEnum { KeyPairEnum::Ed25519(ed25519::KeyPairFromSeed32Generator::generate(Seed32::new( @@ -516,10 +507,10 @@ mod tests { 0u8, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, ]; - let pubkey_str_b58 = "11111111111111111111111111111111".to_owned(); - let pubkey = PubKey::Ed25519(ed25519::PublicKey(pubkey_bytes)); - + let pubkey = PubKey::Ed25519(unwrap!(ed25519::PublicKey::try_from(&pubkey_bytes[..]))); assert_eq!(pubkey_default, pubkey); + + let pubkey_str_b58 = "11111111111111111111111111111111".to_owned(); assert_eq!( pubkey_default, PubKey::from_str(&pubkey_str_b58).expect("Fail to parse pubkey !") @@ -730,10 +721,20 @@ mod tests { expected: *ed25519::PUBKEY_SIZE_IN_BYTES, found: 2, }), - PubKey::from_bytes(&[0u8, 1u8]), + PubKey::from_bytes(&[0u8, 1]), + ); + assert_eq!( + Err(PubkeyFromBytesError::InvalidBytesLen { + expected: *ed25519::PUBKEY_SIZE_IN_BYTES, + found: 33, + }), + PubKey::from_bytes(&[ + 0u8, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, + 23, 24, 25, 26, 27, 28, 29, 30, 31, 31 + ]), ); assert_eq!( - Ok(PubKey::Ed25519(ed25519::PublicKey([0u8; 32]))), + Ok(PubKey::Ed25519(ed25519::PublicKey::default())), PubKey::from_bytes(&[0u8; 32]), ); } diff --git a/lib/crypto/src/seeds.rs b/lib/crypto/src/seeds.rs index 41a716d7ba3fd3e2e9eae3836cc0d4723c6d85da..cfa560465f03e6d55ab943fcf1956b0efb6364b6 100644 --- a/lib/crypto/src/seeds.rs +++ b/lib/crypto/src/seeds.rs @@ -67,7 +67,7 @@ impl Seed32 { #[inline] /// Create seed from base58 str pub fn from_base58(base58_str: &str) -> Result<Self, BaseConvertionError> { - Ok(Seed32::new(b58::str_base58_to_32bytes(base58_str)?)) + Ok(Seed32::new(b58::str_base58_to_32bytes(base58_str)?.0)) } #[inline] /// Generate random seed