indy-crypto 0.1.6-9

This is the shared crypto library for Hyperledger Indy components.
Documentation
extern crate rusty_secrets;

use errors::IndyCryptoError;
use utils::json::{JsonEncodable, JsonDecodable};

#[derive(Debug, Eq, PartialEq, Serialize, Deserialize, Clone)]
pub struct Share {
    value: String
}

impl ToString for Share {
    fn to_string(&self) -> String {
        format!("{}", self.value)
    }
}

impl JsonEncodable for Share {}

impl<'a> JsonDecodable<'a> for Share {}

pub fn shard_secret(m: usize, n: usize, secret: &Vec<u8>, sign_shares: bool) -> Result<Vec<Share>, IndyCryptoError> {
    match rusty_secrets::sss::split_secret(m as u8, n as u8, &secret.as_slice(), sign_shares) {
        Ok(shares) => Ok(shares.into_iter().map(|share| Share { value: share }).collect()),
        Err(msg) => Err(IndyCryptoError::InvalidStructure(format!("Unable to create shares: {:?}", msg)))
    }
}

pub fn recover_secret(shares: Vec<Share>, verify_signature: bool) -> Result<Vec<u8>, IndyCryptoError> {
    let string_shares: Vec<String> = shares.into_iter().map(|share| share.value).collect();
    match rusty_secrets::sss::recover_secret(&string_shares, verify_signature) {
        Ok(secret) => Ok(secret.to_vec()),
        Err(msg) => Err(IndyCryptoError::InvalidStructure(format!("Unable to recreate secret: {:?}", msg)))
    }
}

pub fn get_shard_by_no(shares: &Vec<Share>, number: usize) -> Result<Share, IndyCryptoError> {
    if shares.len() < number {
        return Err(IndyCryptoError::InvalidParam2(format!("number cannot be greater than count of shares")));
    }
    let string_shares = shares.into_iter().map(|share| share.value.clone()).collect::<Vec<String>>();

    for share in &string_shares[..] {
        let split = share.split("-").collect::<Vec<&str>>();
        if split[1].parse::<usize>().unwrap() == number {
            return Ok(Share { value: share.clone() });
        }
    }
    return Err(IndyCryptoError::InvalidStructure(format!("No share found with number {}", number)))
}


#[cfg(test)]
mod tests {
    use super::*;
    use std::str;

    pub const SECRET: &'static str = "this is a really big test string";

    fn check_secret(secret: &str, mut shares: Vec<Share>, p: usize){
        let recovered_secret = recover_secret(shares.split_at_mut(p).0.to_vec(), false).unwrap();
        let recovered_secret_as_str = str::from_utf8(&recovered_secret).unwrap();
        println!("recovered secret={:?}; from {} shares", &recovered_secret_as_str, p);
        assert_eq!(secret, recovered_secret_as_str);
    }

    #[test]
    fn test_create_shards() {
        let shares = shard_secret(3, 5, &SECRET.as_bytes().to_vec(), false).unwrap();
        println!("shares={:?}", shares);
        assert_eq!(shares.len(), 5);
    }

    #[test]
    fn test_recover_secret() {
        let mut shares = shard_secret(3, 5, &SECRET.as_bytes().to_vec(), false).unwrap();

        // Recover with threshold number of shares
        check_secret(&SECRET, shares.clone(), 3);

        // Recover with more than threshold number of shares
        check_secret(&SECRET, shares.clone(), 4);

        // Recover with less than threshold number of shares
        assert!(recover_secret(shares.split_at_mut(2).0.to_vec(), false).is_err());
    }

    #[test]
    fn test_get_shard_by_number() {
        let shards = shard_secret(3, 5, &SECRET.as_bytes().to_vec(), false).unwrap();

        // Valid shard number
        let s = get_shard_by_no(&shards, 2).unwrap();
        println!("{:?}", &s);
        assert_eq!(&s, &shards[1]);

        // Invalid shard number
        assert!(get_shard_by_no(&shards, 7).is_err());
    }
}