pub const MAX_STRUCTURED_DATA_SIZE_IN_BYTES: usize = 102400;
#[derive(Eq, PartialEq, PartialOrd, Ord, Clone, RustcDecodable, RustcEncodable)]
pub struct StructuredData {
type_tag: u64,
identifier: ::NameType,
data: Vec<u8>,
previous_owner_keys: Vec<::sodiumoxide::crypto::sign::PublicKey>,
version: u64,
current_owner_keys: Vec<::sodiumoxide::crypto::sign::PublicKey>,
previous_owner_signatures: Vec<::sodiumoxide::crypto::sign::Signature>,
}
impl StructuredData {
pub fn new(type_tag: u64,
identifier: ::NameType,
version: u64,
data: Vec<u8>,
current_owner_keys: Vec<::sodiumoxide::crypto::sign::PublicKey>,
previous_owner_keys: Vec<::sodiumoxide::crypto::sign::PublicKey>,
signing_key: Option<&::sodiumoxide::crypto::sign::SecretKey>)
-> Result<StructuredData, ::error::RoutingError> {
let mut structured_data = StructuredData {
type_tag: type_tag,
identifier: identifier,
data: data,
previous_owner_keys: previous_owner_keys,
version: version,
current_owner_keys: current_owner_keys,
previous_owner_signatures: vec![],
};
if let Some(key) = signing_key {
let _ = try!(structured_data.add_signature(key));
}
Ok(structured_data)
}
pub fn compute_name(type_tag: u64, identifier: &::NameType) -> ::NameType {
let type_tag_as_string = type_tag.to_string();
let chain = identifier.0.iter()
.chain(type_tag_as_string.as_bytes().iter())
.map(|a|*a);
::NameType(::sodiumoxide::crypto::hash::sha512::hash(&chain.collect::<Vec<_>>()[..]).0)
}
pub fn replace_with_other(&mut self, other: StructuredData)
-> Result<(), ::error::RoutingError> {
try!(self.validate_self_against_successor(&other));
self.type_tag = other.type_tag;
self.identifier = other.identifier;
self.data = other.data;
self.previous_owner_keys = other.previous_owner_keys;
self.version = other.version;
self.current_owner_keys = other.current_owner_keys;
self.previous_owner_signatures = other.previous_owner_signatures;
Ok(())
}
pub fn name(&self) -> ::NameType {
StructuredData::compute_name(self.type_tag, &self.identifier)
}
pub fn validate_self_against_successor(&self, other: &StructuredData)
-> Result<(), ::error::RoutingError> {
let owner_keys_to_match = if other.previous_owner_keys.is_empty() {
&other.current_owner_keys
} else {
&other.previous_owner_keys
};
if other.type_tag != self.type_tag || other.identifier != self.identifier ||
other.version != self.version + 1 ||
*owner_keys_to_match != self.current_owner_keys {
return Err(::error::RoutingError::UnknownMessageType)
}
other.verify_previous_owner_signatures(owner_keys_to_match)
}
fn verify_previous_owner_signatures(&self,
owner_keys: &Vec<::sodiumoxide::crypto::sign::PublicKey>)
-> Result<(), ::error::RoutingError> {
if self.previous_owner_signatures.iter().filter(|&sig| self.previous_owner_signatures.iter()
.any(|ref sig_check| ::NameType(sig.0) == ::NameType(sig_check.0)))
.count() > (owner_keys.len() + 1) / 2 {
return Err(::error::RoutingError::DuplicateSignatures);
}
if self.previous_owner_signatures.len() < (owner_keys.len() + 1) / 2 {
return Err(::error::RoutingError::NotEnoughSignatures);
}
let data = try!(self.data_to_sign());
if self.previous_owner_signatures.iter()
.filter(|&sig| owner_keys
.iter()
.any(|ref pub_key| ::sodiumoxide::crypto::sign::verify_detached(&sig, &data, pub_key)))
.count() < (owner_keys.len() / 2 + owner_keys.len() % 2) {
return Err(::error::RoutingError::NotEnoughSignatures);
}
Ok(())
}
fn data_to_sign(&self) -> Result<Vec<u8>, ::error::RoutingError> {
let mut enc = ::cbor::Encoder::from_memory();
try!(enc.encode(self.type_tag.to_string().as_bytes()));
try!(enc.encode(&[self.identifier]));
try!(enc.encode(&self.data));
try!(enc.encode(&self.previous_owner_keys));
try!(enc.encode(&self.current_owner_keys));
try!(enc.encode(self.version.to_string().as_bytes()));
Ok(enc.into_bytes())
}
pub fn add_signature(&mut self, secret_key: &::sodiumoxide::crypto::sign::SecretKey)
-> Result<isize, ::error::RoutingError> {
let data = try!(self.data_to_sign());
let sig = ::sodiumoxide::crypto::sign::sign_detached(&data, secret_key);
self.previous_owner_signatures.push(sig);
let owner_keys = if self.previous_owner_keys.is_empty() {
&self.current_owner_keys
} else {
&self.previous_owner_keys
};
Ok(((owner_keys.len() + 1) as isize / 2) - self.previous_owner_signatures.len() as isize)
}
pub fn replace_signatures(&mut self,
new_signatures: Vec<::sodiumoxide::crypto::sign::Signature>) {
self.previous_owner_signatures = new_signatures;
}
pub fn get_type_tag(&self) -> u64 {
self.type_tag.clone()
}
pub fn get_identifier(&self) -> &::NameType {
&self.identifier
}
pub fn get_data(&self) -> &Vec<u8> {
&self.data
}
pub fn get_previous_owner_keys(&self) -> &Vec<::sodiumoxide::crypto::sign::PublicKey> {
&self.previous_owner_keys
}
pub fn get_version(&self) -> u64 {
self.version
}
pub fn get_owner_keys(&self) -> &Vec<::sodiumoxide::crypto::sign::PublicKey> {
&self.current_owner_keys
}
pub fn get_previous_owner_signatures(&self) -> &Vec<::sodiumoxide::crypto::sign::Signature> {
&self.previous_owner_signatures
}
pub fn payload_size(&self) -> usize {
self.data.len()
}
}
impl ::std::fmt::Debug for StructuredData {
fn fmt(&self, formatter: &mut ::std::fmt::Formatter) -> Result<(), ::std::fmt::Error> {
let _ = formatter.write_str(
&format!(" type_tag: {:?} , name: {:?} , version: {:?} , data: {:?}",
self.type_tag, self.name(), self.version,
::utils::get_debug_id(self.data.clone())));
let prev_owner_keys : Vec<String> = self.previous_owner_keys.iter().map(|pub_key|
::utils::get_debug_id(::types::array_as_vector(&pub_key.0))).collect();
let _ = formatter.write_str(&format!(" , previous_owner_keys : ("));
for itr in prev_owner_keys.iter() {
let _ = formatter.write_str(&format!("{:?} ", itr));
}
let _ = formatter.write_str(&format!(")"));
let current_owner_keys : Vec<String> = self.current_owner_keys.iter().map(|pub_key|
::utils::get_debug_id(::types::array_as_vector(&pub_key.0))).collect();
let _ = formatter.write_str(&format!(" , current_owner_keys : ("));
for itr in current_owner_keys.iter() {
let _ = formatter.write_str(&format!("{:?} ", itr));
}
let _ = formatter.write_str(&format!(") "));
let prev_owner_signatures: Vec<String> =
self.previous_owner_signatures.iter().map(|signature|
::utils::get_debug_id(::types::array_as_vector(&signature.0))).collect();
let _ = formatter.write_str(&format!(" , prev_owner_signatures : ("));
for itr in prev_owner_signatures.iter() {
let _ = formatter.write_str(&format!("{:?} ", itr));
}
formatter.write_str(&format!(") "))
}
}
#[cfg(test)]
mod test {
#[test]
fn single_owner() {
let keys = ::sodiumoxide::crypto::sign::gen_keypair();
let owner_keys = vec![keys.0];
match super::StructuredData::new(0,
::test_utils::Random::generate_random(),
0,
vec![],
owner_keys.clone(),
vec![],
Some(&keys.1)) {
Ok(structured_data) =>
assert_eq!(
structured_data.verify_previous_owner_signatures(&owner_keys).ok(), Some(())),
Err(error) => panic!("Error: {:?}", error),
}
}
#[test] #[should_panic(expected = "assertion failed")]
fn single_owner_unsigned() {
let keys = ::sodiumoxide::crypto::sign::gen_keypair();
let owner_keys = vec![keys.0];
match super::StructuredData::new(0,
::test_utils::Random::generate_random(),
0,
vec![],
owner_keys.clone(),
vec![],
None) {
Ok(structured_data) =>
assert_eq!(
structured_data.verify_previous_owner_signatures(&owner_keys).ok(), Some(())),
Err(error) => panic!("Error: {:?}", error),
}
}
#[test] #[should_panic(expected = "assertion failed")]
fn single_owner_other_signing_key() {
let keys = ::sodiumoxide::crypto::sign::gen_keypair();
let owner_keys = vec![keys.0];
let other_keys = ::sodiumoxide::crypto::sign::gen_keypair();
match super::StructuredData::new(0,
::test_utils::Random::generate_random(),
0,
vec![],
owner_keys.clone(),
vec![],
Some(&other_keys.1)) {
Ok(structured_data) => {
assert_eq!(
structured_data.verify_previous_owner_signatures(&owner_keys).ok(), Some(()))
},
Err(error) => panic!("Error: {:?}", error),
}
}
#[test] #[should_panic(expected = "assertion failed")]
fn single_owner_other_signature() {
let keys = ::sodiumoxide::crypto::sign::gen_keypair();
let owner_keys = vec![keys.0];
let other_keys = ::sodiumoxide::crypto::sign::gen_keypair();
match super::StructuredData::new(0,
::test_utils::Random::generate_random(),
0,
vec![],
owner_keys.clone(),
vec![],
None) {
Ok(mut structured_data) => {
assert_eq!(structured_data.add_signature(&other_keys.1).ok(), Some(0));
assert_eq!(
structured_data.verify_previous_owner_signatures(&owner_keys).ok(), Some(()))
},
Err(error) => panic!("Error: {:?}", error),
}
}
#[test]
fn three_owners() {
let keys1 = ::sodiumoxide::crypto::sign::gen_keypair();
let keys2 = ::sodiumoxide::crypto::sign::gen_keypair();
let keys3 = ::sodiumoxide::crypto::sign::gen_keypair();
let owner_keys = vec![keys1.0, keys2.0, keys3.0];
match super::StructuredData::new(0,
::test_utils::Random::generate_random(),
0,
vec![],
owner_keys.clone(),
vec![],
Some(&keys1.1)) {
Ok(mut structured_data) => {
assert_eq!(
structured_data.verify_previous_owner_signatures(&owner_keys).ok(), None);
assert_eq!(structured_data.add_signature(&keys2.1).ok(), Some(0));
assert_eq!(
structured_data.verify_previous_owner_signatures(&owner_keys).ok(), Some(()));
}
Err(error) => panic!("Error: {:?}", error),
}
}
#[test]
fn transfer_owners() {
let keys1 = ::sodiumoxide::crypto::sign::gen_keypair();
let keys2 = ::sodiumoxide::crypto::sign::gen_keypair();
let keys3 = ::sodiumoxide::crypto::sign::gen_keypair();
let new_owner = ::sodiumoxide::crypto::sign::gen_keypair();
let identifier: ::NameType = ::test_utils::Random::generate_random();
match super::StructuredData::new(0,
identifier.clone(),
0,
vec![],
vec![keys1.0, keys2.0, keys3.0],
vec![],
Some(&keys1.1)) {
Ok(mut orig_structured_data) => {
assert_eq!(orig_structured_data.add_signature(&keys2.1).ok(), Some(0));
match super::StructuredData::new(0,
identifier.clone(),
1,
vec![],
vec![new_owner.0],
vec![keys1.0, keys2.0, keys3.0],
Some(&keys1.1)) {
Ok(mut new_structured_data) => {
assert_eq!(new_structured_data.add_signature(&keys2.1).ok(), Some(0));
match orig_structured_data.replace_with_other(new_structured_data) {
Ok(()) => println!("All good"),
Err(e) => panic!("Error {}", e),
}
match super::StructuredData::new(0,
identifier,
2,
vec![],
vec![keys1.0],
vec![new_owner.0],
Some(&new_owner.1)) {
Ok(another_new_structured_data) => {
match orig_structured_data.replace_with_other(
another_new_structured_data) {
Ok(()) => println!("All good"),
Err(e) => panic!("Error {}", e),
}
}
Err(error) => panic!("Error: {:?}", error),
}
}
Err(error) => panic!("Error: {:?}", error),
}
}
Err(error) => panic!("Error: {:?}", error),
}
}
}