diff --git a/Cargo.toml b/Cargo.toml index 4cf3522..291d1dd 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -23,8 +23,11 @@ name = "sovrin" path = "src/main.rs" [features] -default = ["bn_openssl"] +default = ["bn_openssl", "ed25519_sodium", "base58_rust_base58", "xsalsa20_sodium"] bn_openssl = ["openssl", "int_traits"] +ed25519_sodium = ["sodiumoxide"] +base58_rust_base58 = ["rust-base58"] +xsalsa20_sodium = ["sodiumoxide"] [dependencies] int_traits = { version = "0.1.1", optional = true } @@ -35,8 +38,8 @@ openssl = { version = "0.9.9", optional = true } milagro-crypto = "0.1.0" rand = "0.3" rusqlite = "0.10.1" -rust-base58 = "0.0.4" +rust-base58 = {version = "0.0.4", optional = true} rustc-serialize = "0.3" -sodiumoxide = "0.0.14" +sodiumoxide = {version = "0.0.14", optional = true} zmq = "0.8.1" lazy_static = "0.2" diff --git a/src/services/crypto/anoncreds/mod.rs b/src/services/crypto/anoncreds/mod.rs index 0b1f98d..7206d5a 100644 --- a/src/services/crypto/anoncreds/mod.rs +++ b/src/services/crypto/anoncreds/mod.rs @@ -8,15 +8,15 @@ use services::crypto::anoncreds::issuer::Issuer; use services::crypto::anoncreds::prover::Prover; use services::crypto::anoncreds::verifier::Verifier; -pub struct AnoncredsService { +pub struct Anoncreds { issuer: Issuer, prover: Prover, verifier: Verifier } -impl AnoncredsService { - pub fn new() -> AnoncredsService { - AnoncredsService { +impl Anoncreds { + pub fn new() -> Anoncreds { + Anoncreds { issuer: Issuer::new(), prover: Prover::new(), verifier: Verifier::new() diff --git a/src/services/crypto/ed25519.rs b/src/services/crypto/ed25519.rs deleted file mode 100644 index 1b6bc41..0000000 --- a/src/services/crypto/ed25519.rs +++ /dev/null @@ -1,182 +0,0 @@ -extern crate rust_base58; -extern crate sodiumoxide; - -use errors::crypto::CryptoError; -use self::rust_base58::{ToBase58, FromBase58}; -use self::sodiumoxide::crypto::box_; -use self::sodiumoxide::crypto::secretbox; -use self::sodiumoxide::crypto::sign; -use self::sodiumoxide::randombytes; -use std::convert::AsMut; - -pub struct ED25519 {} - -impl ED25519 { - pub fn new() -> ED25519 { - ED25519 {} - } - - pub fn symmetric_create_key(&self) -> Vec { - secretbox::gen_key()[..].to_vec() - } - - pub fn symmetric_create_nonce(&self) -> Vec { - secretbox::gen_nonce()[..].to_vec() - } - - pub fn symmetric_encrypt(&self, key: &[u8], nonce: &[u8], doc: &[u8]) -> Vec { - let sodium = ED25519::new(); - secretbox::seal( - doc, - &secretbox::Nonce(sodium.clone_into_array(nonce)), - &secretbox::Key(sodium.clone_into_array(key)) - ) - } - - pub fn symmetric_decrypt(&self, key: &[u8], nonce: &[u8], doc: &[u8]) -> Result, CryptoError> { - let sodium = ED25519::new(); - secretbox::open( - doc, - &secretbox::Nonce(sodium.clone_into_array(nonce)), - &secretbox::Key(sodium.clone_into_array(key)) - ) - .map_err(|_| CryptoError::InvalidStructure("Unable to decrypt data".to_string())) - } - - - pub fn box_create_key_pair(&self) -> (Vec, Vec) { - let (public_key, private_key) = box_::gen_keypair(); - (public_key[..].to_vec(), private_key[..].to_vec()) - } - - pub fn encrypt(&self, private_key: &[u8], public_key: &[u8], doc: &[u8], nonce: &[u8]) -> Vec { - let sodium = ED25519::new(); - box_::seal( - doc, - &box_::Nonce(sodium.clone_into_array(nonce)), - &box_::PublicKey(sodium.clone_into_array(public_key)), - &box_::SecretKey(sodium.clone_into_array(private_key)) - ) - } - - pub fn decrypt(&self, private_key: &[u8], public_key: &[u8], doc: &[u8], nonce: &[u8]) -> Result, CryptoError> { - let sodium = ED25519::new(); - box_::open( - doc, - &box_::Nonce(sodium.clone_into_array(nonce)), - &box_::PublicKey(sodium.clone_into_array(public_key)), - &box_::SecretKey(sodium.clone_into_array(private_key)) - ) - .map_err(|_| CryptoError::InvalidStructure("Unable to decrypt data".to_string())) - } - - pub fn get_nonce(&self) -> Vec { - box_::gen_nonce()[..].to_vec() - } - - pub fn create_key_pair_for_signature(&self, seed: Option<&[u8]>) -> (Vec, Vec) { - let sodium = ED25519::new(); - let (public_key, private_key) = - sign::keypair_from_seed( - &sign::Seed( - sodium.clone_into_array( - seed.unwrap_or(&randombytes::randombytes(32)[..]) - ) - ) - ); - - (public_key[..].to_vec(), private_key[..].to_vec()) - } - - pub fn sign(&self, private_key: &[u8], doc: &[u8]) -> Vec { - let mut pr_key: [u8; 64] = [0; 64]; - pr_key.clone_from_slice(private_key); - - sign::sign( - doc, - &sign::SecretKey(pr_key) - ) - } - - pub fn verify(&self, public_key: &[u8], doc: &[u8]) -> Result, CryptoError> { - let sodium = ED25519::new(); - sign::verify( - doc, - &sign::PublicKey(sodium.clone_into_array(public_key)) - ) - .map_err(|_| CryptoError::InvalidStructure("Unable to decrypt data".to_string())) - } - - pub fn base58_encode(&self, doc: &[u8]) -> String { - doc.to_base58() - } - - pub fn base58_decode(&self, doc: &String) -> Result, CryptoError> { - doc.from_base58() - .map_err(|err| CryptoError::InvalidStructure(format!("{}", err))) - } - - fn clone_into_array(&self, slice: &[T]) -> A - where A: Sized + Default + AsMut<[T]>, T: Clone - { - let mut a = Default::default(); - >::as_mut(&mut a).clone_from_slice(slice); - a - } -} - - -#[cfg(test)] -mod tests { - use super::*; - - #[test] - fn crypto_service_can_encode_decode_string() { - let sodium = ED25519::new(); - let data = &[1, 2, 3]; - - let encode_result = sodium.base58_encode(&data[..]); - assert_eq!("Ldp", &encode_result, "Success encrypt data"); - - let decrypted_data = sodium.base58_decode(&encode_result); - assert!(decrypted_data.is_ok(), "Success decrypt data"); - assert_eq!(data, &decrypted_data.unwrap()[..], "Get correct data"); - } - - #[test] - fn crypto_service_encode_decode_test() { - let sodium = ED25519::new(); - let (alice_pk, alice_sk) = sodium.box_create_key_pair(); - let (bob_pk, bob_sk) = sodium.box_create_key_pair(); - - let text = randombytes::randombytes(16); - let nonce = sodium.get_nonce(); - - let bob_encrypted_text = sodium.encrypt(&bob_sk, &alice_pk, &text[..], &nonce); - let bob_decrypt_result = sodium.decrypt(&alice_sk, &bob_pk, &bob_encrypted_text, &nonce); - assert!(bob_decrypt_result.is_ok()); - assert_eq!(text, bob_decrypt_result.unwrap()); - - let alice_encrypted_text = sodium.encrypt(&alice_sk, &bob_pk, &text[..], &nonce); - let alice_decrypted_text = sodium.decrypt(&bob_sk, &alice_pk, &alice_encrypted_text, &nonce); - assert!(alice_decrypted_text.is_ok()); - assert_eq!(text, alice_decrypted_text.unwrap()); - } - - #[test] - fn crypto_service_signin_verify_test() { - let sodium = ED25519::new(); - let seed = randombytes::randombytes(32); - - let (public_key, secret_key) = sodium.create_key_pair_for_signature(Some(&seed[..])); - - let text = randombytes::randombytes(16); - - let alice_signed_text = sodium.sign(&secret_key, &text[..]); - - let verified_data = sodium.verify(&public_key, &alice_signed_text); - assert!(verified_data.is_ok()); - - assert_eq!(text, verified_data.unwrap()); - } -} \ No newline at end of file diff --git a/src/services/crypto/mod.rs b/src/services/crypto/mod.rs index 60152d0..80220b7 100644 --- a/src/services/crypto/mod.rs +++ b/src/services/crypto/mod.rs @@ -1,21 +1,26 @@ pub mod helpers; pub mod anoncreds; -pub mod ed25519; pub mod wrappers; -use self::anoncreds::AnoncredsService; -use self::ed25519::ED25519; +use self::anoncreds::Anoncreds; +use self::wrappers::base58::Base58; +use self::wrappers::ed25519::ED25519; +use self::wrappers::xsalsa20::XSalsa20; pub struct CryptoService { + anoncreds: Anoncreds, + base58: Base58, ed25519: ED25519, - anoncreds: AnoncredsService + xsalsa20: XSalsa20 } impl CryptoService { pub fn new() -> CryptoService { CryptoService { + anoncreds: Anoncreds::new(), + base58: Base58::new(), ed25519: ED25519::new(), - anoncreds: AnoncredsService::new() + xsalsa20: XSalsa20::new() } } } \ No newline at end of file diff --git a/src/services/crypto/wrappers/base58/rust_base58.rs b/src/services/crypto/wrappers/base58/rust_base58.rs new file mode 100644 index 0000000..1acb967 --- /dev/null +++ b/src/services/crypto/wrappers/base58/rust_base58.rs @@ -0,0 +1,44 @@ +extern crate rust_base58; + +use errors::crypto::CryptoError; + +use self::rust_base58::{ToBase58, FromBase58}; + +pub struct Base58 {} + +impl Base58 { + pub fn new() -> Base58 { + Base58 {} + } + + pub fn encode(&self, doc: &[u8]) -> String { + doc.to_base58() + } + + pub fn decode(&self, doc: &str) -> Result, CryptoError> { + doc.from_base58() + .map_err(|err| CryptoError::InvalidStructure(format!("{}", err))) + } +} + + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn encode_works() { + let base58 = Base58::new(); + let result = base58.encode(&[1, 2, 3]); + assert_eq!("Ldp", &result, "Got unexpected data"); + } + + #[test] + fn decode_works() { + let base58 = Base58::new(); + let result = base58.decode("Ldp"); + + assert!(result.is_ok(), "Got error"); + assert_eq!(&[1, 2, 3], &result.unwrap()[..], "Get unexpected data"); + } +} \ No newline at end of file diff --git a/src/services/crypto/wrappers/ed25519.rs b/src/services/crypto/wrappers/ed25519.rs deleted file mode 100644 index e69de29..0000000 diff --git a/src/services/crypto/wrappers/ed25519/sodium.rs b/src/services/crypto/wrappers/ed25519/sodium.rs new file mode 100644 index 0000000..8904670 --- /dev/null +++ b/src/services/crypto/wrappers/ed25519/sodium.rs @@ -0,0 +1,121 @@ +extern crate sodiumoxide; + +use errors::crypto::CryptoError; +use self::sodiumoxide::crypto::box_; +use self::sodiumoxide::crypto::sign; +use self::sodiumoxide::randombytes; +use std::convert::AsMut; + +pub struct ED25519 {} + +impl ED25519 { + pub fn new() -> ED25519 { + ED25519 {} + } + + pub fn create_key_pair(&self) -> (Vec, Vec) { + let (public_key, private_key) = box_::gen_keypair(); + (public_key[..].to_vec(), private_key[..].to_vec()) + } + + pub fn encrypt(&self, private_key: &[u8], public_key: &[u8], doc: &[u8], nonce: &[u8]) -> Vec { + box_::seal( + doc, + &box_::Nonce(ED25519::_clone_into_array(nonce)), + &box_::PublicKey(ED25519::_clone_into_array(public_key)), + &box_::SecretKey(ED25519::_clone_into_array(private_key)) + ) + } + + pub fn decrypt(&self, private_key: &[u8], public_key: &[u8], doc: &[u8], nonce: &[u8]) -> Result, CryptoError> { + box_::open( + doc, + &box_::Nonce(ED25519::_clone_into_array(nonce)), + &box_::PublicKey(ED25519::_clone_into_array(public_key)), + &box_::SecretKey(ED25519::_clone_into_array(private_key)) + ) + .map_err(|_| CryptoError::InvalidStructure("Unable to decrypt data".to_string())) + } + + pub fn gen_nonce(&self) -> Vec { + box_::gen_nonce()[..].to_vec() + } + + pub fn create_key_pair_for_signature(&self, seed: Option<&[u8]>) -> (Vec, Vec) { + let (public_key, private_key) = + sign::keypair_from_seed( + &sign::Seed( + ED25519::_clone_into_array( + seed.unwrap_or(&randombytes::randombytes(32)[..]) + ) + ) + ); + + (public_key[..].to_vec(), private_key[..].to_vec()) + } + + pub fn sign(&self, private_key: &[u8], doc: &[u8]) -> Vec { + let mut pr_key: [u8; 64] = [0; 64]; + pr_key.clone_from_slice(private_key); + + sign::sign( + doc, + &sign::SecretKey(pr_key) + ) + } + + pub fn verify(&self, public_key: &[u8], doc: &[u8]) -> Result, CryptoError> { + sign::verify( + doc, + &sign::PublicKey(ED25519::_clone_into_array(public_key)) + ) + .map_err(|_| CryptoError::InvalidStructure("Unable to decrypt data".to_string())) + } + + fn _clone_into_array(slice: &[T]) -> A + where A: Sized + Default + AsMut<[T]>, T: Clone + { + let mut a = Default::default(); + >::as_mut(&mut a).clone_from_slice(slice); + a + } +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn encrypt_decrypt_works() { + let ed25519 = ED25519::new(); + let text = randombytes::randombytes(16); + let nonce = ed25519.gen_nonce(); + + let (alice_pk, alice_sk) = ed25519.create_key_pair(); + let (bob_pk, bob_sk) = ed25519.create_key_pair(); + + let bob_encrypted_text = ed25519.encrypt(&bob_sk, &alice_pk, &text, &nonce); + let bob_decrypt_result = ed25519.decrypt(&alice_sk, &bob_pk, &bob_encrypted_text, &nonce); + assert!(bob_decrypt_result.is_ok()); + assert_eq!(text, bob_decrypt_result.unwrap()); + + let alice_encrypted_text = ed25519.encrypt(&alice_sk, &bob_pk, &text, &nonce); + let alice_decrypted_text = ed25519.decrypt(&bob_sk, &alice_pk, &alice_encrypted_text, &nonce); + assert!(alice_decrypted_text.is_ok()); + assert_eq!(text, alice_decrypted_text.unwrap()); + } + + #[test] + fn signin_verify_works() { + let ed25519 = ED25519::new(); + let seed = randombytes::randombytes(32); + let text = randombytes::randombytes(16); + + let (public_key, secret_key) = ed25519.create_key_pair_for_signature(Some(&seed)); + let alice_signed_text = ed25519.sign(&secret_key, &text); + let verified_data = ed25519.verify(&public_key, &alice_signed_text); + + assert!(verified_data.is_ok()); + assert_eq!(text, verified_data.unwrap()); + } +} \ No newline at end of file diff --git a/src/services/crypto/wrappers/mod.rs b/src/services/crypto/wrappers/mod.rs index 79fbf5b..054f95f 100644 --- a/src/services/crypto/wrappers/mod.rs +++ b/src/services/crypto/wrappers/mod.rs @@ -1,5 +1,17 @@ #[cfg(feature = "bn_openssl")] -#[path="bn/openssl.rs"] pub mod bn; +#[path = "bn/openssl.rs"] +pub mod bn; +#[cfg(feature = "ed25519_sodium")] +#[path = "ed25519/sodium.rs"] pub mod ed25519; -pub mod pair; \ No newline at end of file + +#[cfg(feature = "base58_rust_base58")] +#[path = "base58/rust_base58.rs"] +pub mod base58; + +pub mod pair; + +#[cfg(feature = "xsalsa20_sodium")] +#[path = "xsalsa20/sodium.rs"] +pub mod xsalsa20; \ No newline at end of file diff --git a/src/services/crypto/wrappers/xsalsa20/sodium.rs b/src/services/crypto/wrappers/xsalsa20/sodium.rs new file mode 100644 index 0000000..28a8201 --- /dev/null +++ b/src/services/crypto/wrappers/xsalsa20/sodium.rs @@ -0,0 +1,71 @@ +extern crate sodiumoxide; + +use errors::crypto::CryptoError; + +use self::sodiumoxide::crypto::secretbox; +use std::convert::AsMut; + +pub struct XSalsa20 {} + +impl XSalsa20 { + pub fn new() -> XSalsa20 { + XSalsa20 {} + } + + pub fn create_key(&self) -> Vec { + secretbox::gen_key()[..].to_vec() + } + + pub fn gen_nonce(&self) -> Vec { + secretbox::gen_nonce()[..].to_vec() + } + + pub fn encrypt(&self, key: &[u8], nonce: &[u8], doc: &[u8]) -> Vec { + secretbox::seal( + doc, + &secretbox::Nonce(XSalsa20::_clone_into_array(nonce)), + &secretbox::Key(XSalsa20::_clone_into_array(key)) + ) + } + + pub fn decrypt(&self, key: &[u8], nonce: &[u8], doc: &[u8]) -> Result, CryptoError> { + secretbox::open( + doc, + &secretbox::Nonce(XSalsa20::_clone_into_array(nonce)), + &secretbox::Key(XSalsa20::_clone_into_array(key)) + ) + .map_err(|_| CryptoError::InvalidStructure("Unable to decrypt data".to_string())) + } + + fn _clone_into_array(slice: &[T]) -> A + where A: Sized + Default + AsMut<[T]>, T: Clone + { + let mut a = Default::default(); + >::as_mut(&mut a).clone_from_slice(slice); + a + } +} + + +#[cfg(test)] +mod tests { + use super::*; + + use self::sodiumoxide::randombytes; + + #[test] + fn encrypt_decrypt_works() { + let xsalsa20 = XSalsa20::new(); + + let nonce = xsalsa20.gen_nonce(); + let key = xsalsa20.create_key(); + let data = randombytes::randombytes(16); + + let encrypted_data = xsalsa20.encrypt(&key, &nonce, &data); + let decrypt_result = xsalsa20.decrypt(&key, &nonce, &encrypted_data); + + assert!(decrypt_result.is_ok()); + assert_eq!(data, decrypt_result.unwrap()); + } + +} \ No newline at end of file