use super::{PrivateKey as PrivateKeyMethods, PublicKey as PublicKeyMethods};
use crate::bases::*;
use base58::ToBase58;
use base64;
use crypto;
use serde::de::{Deserialize, Deserializer, Error, SeqAccess, Visitor};
use serde::ser::{Serialize, SerializeTuple, Serializer};
use std::collections::hash_map::DefaultHasher;
use std::fmt;
use std::fmt::{Debug, Display, Formatter};
use std::hash::{Hash, Hasher};
use std::marker::PhantomData;
pub static PUBKEY_SIZE_IN_BYTES: &'static usize = &32;
pub static SIG_SIZE_IN_BYTES: &'static usize = &64;
#[derive(Clone, Copy)]
pub struct Signature(pub [u8; 64]);
impl Hash for Signature {
fn hash<H: Hasher>(&self, _state: &mut H) {
let mut hasher = DefaultHasher::new();
Hash::hash_slice(&self.0, &mut hasher);
}
}
impl Serialize for Signature {
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
where
S: Serializer,
{
let mut seq = serializer.serialize_tuple(self.0.len())?;
for elem in &self.0[..] {
seq.serialize_element(elem)?;
}
seq.end()
}
}
impl<'de> Deserialize<'de> for Signature {
fn deserialize<D>(deserializer: D) -> Result<Signature, D::Error>
where
D: Deserializer<'de>,
{
struct ArrayVisitor {
element: PhantomData<u8>,
}
impl<'de> Visitor<'de> for ArrayVisitor {
type Value = Signature;
fn expecting(&self, formatter: &mut Formatter) -> fmt::Result {
formatter.write_str(concat!("an array of length ", 64))
}
fn visit_seq<A>(self, mut seq: A) -> Result<Signature, A::Error>
where
A: SeqAccess<'de>,
{
let mut arr = [0u8; 64];
for (i, byte) in arr.iter_mut().take(64).enumerate() {
*byte = seq
.next_element()?
.ok_or_else(|| Error::invalid_length(i, &self))?;
}
Ok(Signature(arr))
}
}
let visitor: ArrayVisitor = ArrayVisitor {
element: PhantomData,
};
deserializer.deserialize_tuple(64, visitor)
}
}
impl super::Signature for Signature {
#[inline]
fn from_base64(base64_data: &str) -> Result<Signature, BaseConvertionError> {
Ok(Signature(b64::str_base64_to64bytes(base64_data)?))
}
fn to_bytes_vector(&self) -> Vec<u8> {
self.0.to_vec()
}
fn to_base64(&self) -> String {
base64::encode(&self.0[..])
}
}
impl Display for Signature {
fn fmt(&self, f: &mut Formatter) -> Result<(), fmt::Error> {
use super::Signature;
write!(f, "{}", self.to_base64())
}
}
impl Debug for Signature {
fn fmt(&self, f: &mut ::std::fmt::Formatter) -> ::std::fmt::Result {
write!(f, "Signature {{ {} }}", self)
}
}
impl PartialEq<Signature> for Signature {
fn eq(&self, other: &Signature) -> bool {
self.0[0..32] == other.0[0..32] && self.0[32..64] == other.0[32..64]
}
}
impl Eq for Signature {}
#[derive(Copy, Clone, Deserialize, PartialEq, Eq, Hash, Serialize)]
pub struct PublicKey(pub [u8; 32]);
impl ToBase58 for PublicKey {
fn to_base58(&self) -> String {
self.0.to_base58()
}
}
impl Display for PublicKey {
fn fmt(&self, f: &mut Formatter) -> Result<(), fmt::Error> {
write!(f, "{}", self.to_base58())
}
}
impl Debug for PublicKey {
fn fmt(&self, f: &mut Formatter) -> Result<(), fmt::Error> {
write!(f, "PublicKey {{ {} }}", self)
}
}
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)?))
}
fn to_bytes_vector(&self) -> Vec<u8> {
self.0.to_vec()
}
fn verify(&self, message: &[u8], signature: &Self::Signature) -> bool {
crypto::ed25519::verify(message, &self.0, &signature.0)
}
}
#[derive(Copy, Clone)]
pub struct PrivateKey(pub [u8; 64]);
impl ToBase58 for PrivateKey {
fn to_base58(&self) -> String {
self.0.to_base58()
}
}
impl Display for PrivateKey {
fn fmt(&self, f: &mut Formatter) -> Result<(), fmt::Error> {
write!(f, "{}", self.to_base58())
}
}
impl Debug for PrivateKey {
fn fmt(&self, f: &mut Formatter) -> Result<(), fmt::Error> {
write!(f, "PrivateKey {{ {} }}", self)
}
}
impl PartialEq<PrivateKey> for PrivateKey {
fn eq(&self, other: &PrivateKey) -> bool {
self.0[0..32] == other.0[0..32] && self.0[32..64] == other.0[32..64]
}
}
impl Eq for PrivateKey {}
impl super::PrivateKey for PrivateKey {
type Signature = Signature;
#[inline]
fn from_base58(base58_data: &str) -> Result<Self, BaseConvertionError> {
Ok(PrivateKey(b58::str_base58_to_64bytes(base58_data)?))
}
fn sign(&self, message: &[u8]) -> Self::Signature {
Signature(crypto::ed25519::signature(message, &self.0))
}
}
#[derive(Debug, Copy, Clone, Eq)]
pub struct KeyPair {
pub pubkey: PublicKey,
pub privkey: PrivateKey,
}
impl Display for KeyPair {
fn fmt(&self, f: &mut Formatter) -> Result<(), fmt::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 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)
}
}
#[derive(Debug, Copy, Clone)]
pub struct KeyPairFromSeedGenerator {}
impl KeyPairFromSeedGenerator {
pub fn generate(seed: &[u8; 32]) -> KeyPair {
let (private, public) = crypto::ed25519::keypair(seed);
KeyPair {
pubkey: PublicKey(public),
privkey: PrivateKey(private),
}
}
}
#[derive(Debug, Copy, Clone)]
pub struct KeyPairFromSaltedPasswordGenerator {
log_n: u8,
r: u32,
p: u32,
}
impl KeyPairFromSaltedPasswordGenerator {
pub fn with_default_parameters() -> KeyPairFromSaltedPasswordGenerator {
KeyPairFromSaltedPasswordGenerator {
log_n: 12,
r: 16,
p: 1,
}
}
pub fn with_parameters(log_n: u8, r: u32, p: u32) -> KeyPairFromSaltedPasswordGenerator {
KeyPairFromSaltedPasswordGenerator { log_n, r, p }
}
pub fn generate_seed(&self, password: &[u8], salt: &[u8]) -> [u8; 32] {
let mut seed = [0u8; 32];
crypto::scrypt::scrypt(
salt,
password,
&crypto::scrypt::ScryptParams::new(self.log_n, self.r, self.p),
&mut seed,
);
seed
}
pub fn generate(&self, password: &[u8], salt: &[u8]) -> KeyPair {
let seed: [u8; 32] = self.generate_seed(password, salt);
KeyPairFromSeedGenerator::generate(&seed)
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::keys::{KeyPair, Signature};
use base58::FromBase58;
#[test]
fn base58_private_key() {
let private58 =
"468Q1XtTq7h84NorZdWBZFJrGkB18CbmbHr9tkp9snt5GiERP7ySs3wM8myLccbAAGejgMRC9r\
qnXuW3iAfZACm7";
let private_key = super::PrivateKey::from_base58(private58).unwrap();
let private_raw = private58.from_base58().unwrap();
for (key, raw) in private_key.0.iter().zip(private_raw.iter()) {
assert_eq!(key, raw);
}
assert_eq!(private_key.to_base58(), private58);
assert_eq!(
private_key,
super::PrivateKey::from_base58(private58).unwrap()
);
assert_eq!(
super::PrivateKey::from_base58(
"468Q1XtTq7h84NorZdWBZFJrGkB18CbmbHr9tkp9snt5GiERP7ySs3wM8myLccbAAGejgMRC9rqnXuW3iA\
fZACm7djh",
).unwrap_err(),
BaseConvertionError::InvalidLength { found: 67, expected: 64 }
);
assert_eq!(
super::PrivateKey::from_base58(
"468Q1XtTq7h84NorZdWBZFJrGkB18CbmbHr9tkp9snt5GiERP7ySs3wM8myLccbAAGejgMRC9",
)
.unwrap_err(),
BaseConvertionError::InvalidLength {
found: 53,
expected: 64
}
);
assert_eq!(
super::PrivateKey::from_base58(
"468Q1XtTq7h84NorZdWBZFJrGkB18CbmbHr9tkp9snt5GiERP7ySs3wM8myLccbAAGejgMRC9<<",
)
.unwrap_err(),
BaseConvertionError::InvalidCharacter {
character: '<',
offset: 73
}
);
}
#[test]
fn base58_public_key() {
let public58 = "DNann1Lh55eZMEDXeYt59bzHbA3NJR46DeQYCS2qQdLV";
let public_key = super::PublicKey::from_base58(public58).unwrap();
let public_raw = public58.from_base58().unwrap();
for (key, raw) in public_key.0.iter().zip(public_raw.iter()) {
assert_eq!(key, raw);
}
assert_eq!(public_key.to_base58(), public58);
assert_eq!(public_key, super::PublicKey::from_base58(public58).unwrap());
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
}
);
assert_eq!(
super::PublicKey::from_base58("DNann1Lh55eZMEDXeYt59bzHbA3NJR46DeQYCS2qQd<<")
.unwrap_err(),
BaseConvertionError::InvalidCharacter {
character: '<',
offset: 42
}
);
}
#[test]
fn base64_signature() {
let signature64 = "1eubHHbuNfilHMM0G2bI30iZzebQ2cQ1PC7uPAw08FG\
MMmQCRerlF/3pc4sAcsnexsxBseA/3lY03KlONqJBAg==";
let signature = super::Signature::from_base64(signature64).unwrap();
let signature_raw = base64::decode(signature64).unwrap();
for (sig, raw) in signature.0.iter().zip(signature_raw.iter()) {
assert_eq!(sig, raw);
}
assert_eq!(
super::Signature::from_base64("YmhlaW9iaHNlcGlvaGVvaXNlcGl2ZXBvdm5pc2U=").unwrap_err(),
BaseConvertionError::InvalidLength {
found: 29,
expected: 64
}
);
assert_eq!(
super::Signature::from_base64(
"YmhlaW9iaHNlcGlvaGVvaXNlcGl2ZXBvdm5pc2V2c2JlaW9idmVpb3Zqc\
2V2Z3BpaHNlamVwZ25qZXNqb2dwZWpnaW9zZXNkdnNic3JicmJyZGJyZGI=",
)
.unwrap_err(),
BaseConvertionError::InvalidLength {
found: 86,
expected: 64
}
);
assert_eq!(
super::Signature::from_base64(
"1eubHHbuNfilHMM0G2bI30iZzebQ2cQ1PC7uPAw08FGMM\
mQCRerlF/3pc4sAcsnexsxBseA/3lY03KlONqJBAgdha<<",
)
.unwrap_err(),
BaseConvertionError::InvalidCharacter {
character: '<',
offset: 89
}
);
}
#[test]
fn message_sign_verify() {
let pubkey =
super::PublicKey::from_base58("DNann1Lh55eZMEDXeYt59bzHbA3NJR46DeQYCS2qQdLV").unwrap();
let prikey = super::PrivateKey::from_base58(
"468Q1XtTq7h84NorZdWBZFJrGkB18CbmbHr9tkp9snt\
5GiERP7ySs3wM8myLccbAAGejgMRC9rqnXuW3iAfZACm7",
)
.unwrap();
let expected_signature = super::Signature::from_base64(
"1eubHHbuNfilHMM0G2bI30iZzebQ2cQ1PC7uPAw08FG\
MMmQCRerlF/3pc4sAcsnexsxBseA/3lY03KlONqJBAg==",
)
.unwrap();
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());
assert_eq!(sig, expected_signature);
assert!(pubkey.verify(message.as_bytes(), &sig));
}
#[test]
fn keypair_generate_() {
let keypair = KeyPairFromSaltedPasswordGenerator::with_default_parameters().generate(
"JhxtHB7UcsDbA9wMSyMKXUzBZUQvqVyB32KwzS9SWoLkjrUhHV".as_bytes(),
"JhxtHB7UcsDbA9wMSyMKXUzBZUQvqVyB32KwzS9SWoLkjrUhHV_".as_bytes(),
);
assert_eq!(
keypair.pubkey.to_string(),
"7iMV3b6j2hSj5WtrfchfvxivS9swN3opDgxudeHq64fb"
);
}
#[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));
}
}