pub const MAX_HEADER_METADATA_SIZE: usize = 128;
use super::{Error, GUID_SIZE};
use maidsafe_utilities::serialisation::serialise;
use rand::{self, Rng};
use rust_sodium::crypto::hash::sha256;
use rust_sodium::crypto::sign::{self, PublicKey, SecretKey, Signature};
use std::fmt::{self, Debug, Formatter};
use utils;
use xor_name::XorName;
#[derive(PartialEq, Eq, Hash, Clone, RustcDecodable, RustcEncodable)]
struct Detail {
sender: XorName,
guid: [u8; GUID_SIZE],
metadata: Vec<u8>,
}
#[derive(PartialEq, Eq, Hash, Clone, RustcDecodable, RustcEncodable)]
pub struct MpidHeader {
detail: Detail,
signature: Signature,
}
impl MpidHeader {
pub fn new(sender: XorName,
metadata: Vec<u8>,
secret_key: &SecretKey)
-> Result<MpidHeader, Error> {
if metadata.len() > MAX_HEADER_METADATA_SIZE {
return Err(Error::MetadataTooLarge);
}
let mut detail = Detail {
sender: sender,
guid: [0u8; GUID_SIZE],
metadata: metadata,
};
rand::thread_rng().fill_bytes(&mut detail.guid);
let encoded = serialise(&detail)?;
Ok(MpidHeader {
detail: detail,
signature: sign::sign_detached(&encoded, secret_key),
})
}
pub fn sender(&self) -> &XorName {
&self.detail.sender
}
pub fn guid(&self) -> &[u8; GUID_SIZE] {
&self.detail.guid
}
pub fn metadata(&self) -> &Vec<u8> {
&self.detail.metadata
}
pub fn signature(&self) -> &Signature {
&self.signature
}
pub fn name(&self) -> Result<XorName, Error> {
let encoded = serialise(self)?;
Ok(XorName(sha256::hash(&encoded[..]).0))
}
pub fn verify(&self, public_key: &PublicKey) -> bool {
match serialise(&self.detail) {
Ok(encoded) => sign::verify_detached(&self.signature, &encoded, public_key),
Err(_) => false,
}
}
}
impl Debug for MpidHeader {
fn fmt(&self, formatter: &mut Formatter) -> Result<(), fmt::Error> {
write!(formatter,
"MpidHeader {{ sender: {:?}, guid: {}, metadata: {}, signature: {} }}",
self.detail.sender,
utils::format_binary_array(&self.detail.guid),
utils::format_binary_array(&self.detail.metadata),
utils::format_binary_array(&self.signature))
}
}
#[cfg(test)]
mod tests {
use super::*;
use messaging;
use rand;
use rust_sodium::crypto::sign;
use xor_name::XorName;
#[test]
fn full() {
let (mut public_key, secret_key) = sign::gen_keypair();
let sender: XorName = rand::random();
{
let header = unwrap!(MpidHeader::new(sender.clone(), vec![], &secret_key));
assert!(header.metadata().is_empty());
}
let mut metadata = messaging::generate_random_bytes(MAX_HEADER_METADATA_SIZE);
let header = unwrap!(MpidHeader::new(sender.clone(), metadata.clone(), &secret_key));
assert_eq!(*header.metadata(), metadata);
metadata.push(0);
assert!(MpidHeader::new(sender.clone(), metadata.clone(), &secret_key).is_err());
let _ = metadata.pop();
assert!(header.verify(&public_key));
if public_key.0[0] != 255 {
public_key.0[0] += 1;
} else {
public_key.0[0] = 0;
}
assert!(!header.verify(&public_key));
let header1 = unwrap!(MpidHeader::new(sender.clone(), metadata.clone(), &secret_key));
let header2 = unwrap!(MpidHeader::new(sender.clone(), metadata.clone(), &secret_key));
assert_ne!(header1, header2);
assert_eq!(*header1.sender(), sender);
assert_eq!(header1.sender(), header2.sender());
assert_eq!(*header1.metadata(), metadata);
assert_eq!(header1.metadata(), header2.metadata());
assert_ne!(header1.guid(), header2.guid());
assert_ne!(header1.signature(), header2.signature());
let name1 = unwrap!(header1.name());
let name2 = unwrap!(header2.name());
assert_ne!(name1, name2);
}
}