pub const MAX_HEADER_METADATA_SIZE: usize = 128;
use std::fmt::{self, Debug, Formatter};
use std::sync::{Once, ONCE_INIT};
use maidsafe_utilities::serialisation::serialise;
use rand::{self, Rng};
use routing::XorName;
use sodiumoxide;
use sodiumoxide::crypto::hash::sha256;
use sodiumoxide::crypto::sign::{self, PublicKey, SecretKey, Signature};
use super::{Error, GUID_SIZE};
use messaging;
static INITIALISE_SODIUMOXIDE: Once = ONCE_INIT;
static mut sodiumoxide_init_result: bool = false;
#[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> {
assert!(Self::initialise_sodiumoxide());
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 = try!(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 = try!(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,
}
}
#[allow(unsafe_code)]
fn initialise_sodiumoxide() -> bool {
unsafe {
INITIALISE_SODIUMOXIDE.call_once(|| {
sodiumoxide_init_result = sodiumoxide::init();
});
sodiumoxide_init_result
}
}
}
impl Debug for MpidHeader {
fn fmt(&self, formatter: &mut Formatter) -> Result<(), fmt::Error> {
write!(formatter,
"MpidHeader {{ sender: {:?}, guid: {}, metadata: {}, signature: {} }}",
self.detail.sender,
messaging::format_binary_array(&self.detail.guid),
messaging::format_binary_array(&self.detail.metadata),
messaging::format_binary_array(&self.signature))
}
}
#[cfg(test)]
mod test {
use super::*;
use rand;
use routing::XorName;
use sodiumoxide::crypto::sign;
use messaging;
#[test]
fn full() {
let (mut public_key, secret_key) = sign::gen_keypair();
let sender: XorName = rand::random();
{
let header = unwrap_result!(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_result!(MpidHeader::new(sender.clone(), metadata.clone(), &secret_key));
assert!(*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_result!(MpidHeader::new(sender.clone(), metadata.clone(), &secret_key));
let header2 =
unwrap_result!(MpidHeader::new(sender.clone(), metadata.clone(), &secret_key));
assert!(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!(header1.guid() != header2.guid());
assert!(header1.signature() != header2.signature());
let name1 = unwrap_result!(header1.name());
let name2 = unwrap_result!(header2.name());
assert!(name1 != name2);
}
}