From 2cc39eb4801ec5c09d0b683c2c9f3c403b9fc9df Mon Sep 17 00:00:00 2001 From: Kyle Den Hartog Date: Tue, 11 Dec 2018 15:27:35 +0100 Subject: [PATCH 01/17] added detached with aad functons Signed-off-by: Kyle Den Hartog --- .../crypto/chacha20poly1305_ietf/sodium.rs | 41 ++++++++++++++++++- 1 file changed, 40 insertions(+), 1 deletion(-) diff --git a/libindy/src/utils/crypto/chacha20poly1305_ietf/sodium.rs b/libindy/src/utils/crypto/chacha20poly1305_ietf/sodium.rs index f9a49602a8..2236aa5605 100644 --- a/libindy/src/utils/crypto/chacha20poly1305_ietf/sodium.rs +++ b/libindy/src/utils/crypto/chacha20poly1305_ietf/sodium.rs @@ -17,6 +17,7 @@ pub const TAGBYTES: usize = chacha20poly1305_ietf::TAGBYTES; sodium_type!(Key, chacha20poly1305_ietf::Key, KEYBYTES); sodium_type!(Nonce, chacha20poly1305_ietf::Nonce, NONCEBYTES); +sodium_type!(Tag, chacha20poly1305_ietf::Tag, TAGBYTES); impl Nonce { pub fn increment(&mut self) { @@ -52,6 +53,33 @@ pub fn gen_nonce_and_encrypt(data: &[u8], key: &Key) -> (Vec, Nonce) { (encrypted_data, nonce) } +pub fn gen_nonce_and_encrypt_detached(data: &[u8], aad: &[u8], key: &Key) -> (Vec, Nonce, Tag) { + let nonce = gen_nonce(); + + let mut plain = data.to_vec(); + let tag = chacha20poly1305_ietf::seal_detached( + plain.as_mut_slice(), + Some(aad), + &nonce.0, + &key.0 + ); + + (plain.to_vec(), nonce, Tag(tag)) +} + + +pub fn decrypt_detached(data: &[u8], key: &Key, nonce: &Nonce, tag: &Tag, ad: Option<&[u8]>) -> Result, CommonError> { + let mut plain = data.to_vec(); + chacha20poly1305_ietf::open_detached(plain.as_mut_slice(), + ad, + &tag.0, + &nonce.0, + &key.0, + ).map_err(|err| CommonError::InvalidStructure(format!("Unable to decrypt data: {:?}", err)))?; + + Ok(plain.to_vec()) +} + pub fn encrypt(data: &[u8], key: &Key, nonce: &Nonce) -> Vec { chacha20poly1305_ietf::seal( data, @@ -256,7 +284,7 @@ mod tests { } #[test] - fn encrypt_decrypt_works() { + fn gen_nonce_and_encrypt_decrypt_works() { let data = randombytes(100); let key = gen_key(); @@ -266,6 +294,17 @@ mod tests { assert_eq!(data, u); } + #[test] + pub fn gen_nonce_and_encrypt_detached_decrypt_detached_works() { + let data = randombytes(100); + let key = gen_key(); + let aad= randombytes(100); + + let (c, nonce, tag) = gen_nonce_and_encrypt_detached(&data, aad.as_slice(), &key); + let u = decrypt_detached(&c, &key, &nonce, &tag, Some(aad.as_slice())).unwrap(); + assert_eq!(data, u); +} + #[test] fn encrypt_decrypt_works_for_nonce() { let data = randombytes(16); From 6620915bfd97047b9b17db98dd7b098cdd3211f9 Mon Sep 17 00:00:00 2001 From: Kyle Den Hartog Date: Tue, 11 Dec 2018 19:42:09 +0100 Subject: [PATCH 02/17] refactored encrypt_plaintext and decrypt_ciphertext to use detached functionality now Signed-off-by: Kyle Den Hartog --- libindy/src/services/crypto/mod.rs | 327 ++++++++++------------------- 1 file changed, 110 insertions(+), 217 deletions(-) diff --git a/libindy/src/services/crypto/mod.rs b/libindy/src/services/crypto/mod.rs index baa8ca2f69..331229a9db 100644 --- a/libindy/src/services/crypto/mod.rs +++ b/libindy/src/services/crypto/mod.rs @@ -18,7 +18,7 @@ use utils::crypto::verkey_builder::build_full_verkey; use utils::crypto::ed25519_sign; use utils::crypto::ed25519_box; use utils::crypto::chacha20poly1305_ietf; -use utils::crypto::chacha20poly1305_ietf::{gen_key, gen_nonce_and_encrypt}; +use utils::crypto::chacha20poly1305_ietf::{gen_key, gen_nonce_and_encrypt_detached, decrypt_detached}; use std::collections::HashMap; use std::str; @@ -451,126 +451,21 @@ impl CryptoService { Ok(res) } -// /* Authcrypt helper function section */ -// pub fn anon_encrypt_recipient( -// &self, -// recp_vk: &str, -// sym_key: chacha20poly1305_ietf::Key -// ) -> Result { -// //encrypt cek -// let cek = self.crypto_box_seal(recp_vk, &sym_key[..]).map_err(|err| { -// CryptoError::UnknownCryptoError(format!("Failed to encrypt anon recipient {}", err)) -// })?; -// -// //generate struct -// let anon_recipient = AnonRecipient { -// to: recp_vk.to_string(), -// cek: base64::encode(cek.as_slice()), -// }; -// -// Ok(anon_recipient) -// } -// -// pub fn anon_decrypt_recipient( -// &self, -// my_key: &Key, -// recipient: &Recipient -// ) -> Result { -// let cek_as_vec = base64::decode(&anon_recipient.cek).map_err(|err| { -// CryptoError::CommonError( -// CommonError::InvalidStructure(format!("Failed to decode cek for anon_decrypt_recipient {}", err)) -// ) -// })?; -// let cek_as_bytes = cek_as_vec.as_ref(); -// -// let decrypted_cek = self -// .crypto_box_seal_open(my_key, cek_as_bytes) -// .map_err(|err| CryptoError::UnknownCryptoError(format!("Failed to decrypt cek {}", err)))?; -// -// //convert to secretbox key -// let sym_key = chacha20poly1305_ietf::Key::from_slice(&decrypted_cek[..]).map_err(|err| { -// CryptoError::UnknownCryptoError(format!("Failed to decrypt sym_key {}", err)) -// })?; -// -// //return key -// Ok(sym_key) -// } - -// /* Authcrypt helper function section */ -// pub fn auth_encrypt_recipient( -// &self, -// my_key: &Key, -// recp_vk: &str, -// sym_key: &chacha20poly1305_ietf -// ) -> Result { -// //encrypt sym_key for recipient -// let (e_cek, cek_nonce) = self.crypto_box(my_key, recp_vk, &sym_key[..])?; -// -// //serialize enc_header -// let sender_vk_bytes = base58::decode(&my_key.verkey).map_err(|err| { -// CryptoError::CommonError(err) -// })?; -// -// //encrypt enc_from -// let enc_from = self.crypto_box_seal(recp_vk, sender_vk_bytes.as_slice())?; -// -// //create struct -// let auth_recipient = AuthRecipient { -// enc_from: base64::encode(enc_from.as_slice()), -// e_cek: base64::encode(e_cek.as_slice()), -// cek_nonce: base64::encode(cek_nonce.as_slice()), -// to: recp_vk.to_string(), -// }; -// -// //return AuthRecipient struct -// Ok(auth_recipient) -// } -// -// pub fn auth_decrypt_recipient( -// &self, -// my_key: &Key, -// auth_recipient: Recipient -// ) -> Result<(chacha20poly1305_ietf::Key, String), CryptoError> { -// //decode enc_from -// let enc_from_bytes = base64::decode(&auth_recipient.enc_from) -// .map_err(|err| CryptoError::CommonError(err))?; -// -// //decrypt enc_from -// let sender_vk_as_vec = self.crypto_box_seal_open(my_key, enc_from_bytes.as_ref())?; -// -// //encode sender_vk to base58 to use to decrypt cek -// let sender_vk = base58::encode(sender_vk_as_vec.as_ref()); -// -// //decode e_cek -// let e_cek_as_vec = base64::decode(&auth_recipient.e_cek) -// .map_err(|err| CryptoError::CommonError(err))?; -// let e_cek: &[u8] = e_cek_as_vec.as_ref(); -// -// //type convert cek_nonce -// let cek_nonce_as_vec = base64::decode(&auth_recipient.cek_nonce).map_err(|err| { -// CryptoError::CommonError(err) -// })?; -// let cek_nonce: &[u8] = cek_nonce_as_vec.as_ref(); -// -// //decrypt cek -// let decrypted_cek = self.crypto_box_open(my_key, &sender_vk, e_cek, cek_nonce)?; -// -// //convert to secretbox key -// let sym_key = xsalsa20::Key::from_slice(&decrypted_cek[..]).map_err( |err| { -// CommonError::InvalidStructure(format!("Failed to unpack sym_key {}", err)) -// })?; -// -// //TODO Verify key is in DID Doc -// -// //return key to decrypt ciphertext and the key used to decrypt with -// Ok((sym_key, sender_vk)) -// } - - pub fn encrypt_plaintext(&self, plaintext: &str) -> (chacha20poly1305_ietf::Key, chacha20poly1305_ietf::Nonce, Vec) { - let sym_key = gen_key(); - let (message, iv) = gen_nonce_and_encrypt(plaintext.as_bytes(), &sym_key); + pub fn encrypt_plaintext(&self, + plaintext: &str, + aad: &[u8], + sym_key: &chacha20poly1305_ietf::Key) + -> (String, String, String) { ; + + //encrypt message with aad + let (ciphertext, iv, tag) = gen_nonce_and_encrypt_detached(plaintext.as_bytes(), aad, &sym_key); + + //base64 url encode data + let iv_encoded = base64::encode(&iv[..]); + let ciphertext_encoded = base64::encode(ciphertext.as_slice()); + let tag_encoded = base64::encode(&tag[..]); - (sym_key, iv, message) + (ciphertext_encoded, iv_encoded, tag_encoded) } /* ciphertext helper functions*/ @@ -578,9 +473,19 @@ impl CryptoService { &self, ciphertext: &str, iv: &str, + tag: &str, + aad: &str, sym_key: &chacha20poly1305_ietf::Key, ) -> Result { + //convert ciphertext to bytes + let ciphertext_as_vec = base64::decode(ciphertext).map_err(|err| { + CryptoError::CommonError( + CommonError::InvalidStructure(format!("Failed to decode ciphertext {}", err)) + ) + })?; + let ciphertext_as_bytes = ciphertext_as_vec.as_ref(); + //convert IV from &str to &Nonce let iv_as_vec = base64::decode(iv).map_err(|err| CryptoError::CommonError( @@ -593,17 +498,33 @@ impl CryptoService { ) })?; - //convert ciphertext to bytes - let ciphertext_as_vec = base64::decode(ciphertext).map_err(|err| { + //convert tag from &str to &Tag + let tag_as_vec = base64::decode(tag).map_err(|err| CryptoError::CommonError( - CommonError::InvalidStructure(format!("Failed to decode ciphertext {}", err)) + CommonError::InvalidStructure(format!("Failed to decode tag {}", err))) + )?; + let tag_as_slice = tag_as_vec.as_slice(); + let tag = chacha20poly1305_ietf::Tag::from_slice(tag_as_slice).map_err(|err| { + CryptoError::CommonError( + CommonError::InvalidStructure(format!("Failed to convert tag to Tag type {}", err)) ) })?; - let ciphertext_as_bytes = ciphertext_as_vec.as_ref(); + + //convert aad to byte array type + let aad_as_vec = base64::decode(aad) + .map_err(|err|CryptoError::CommonError( + CommonError::InvalidStructure(format!("Failed to decode aad {}", err)) + ))?; + let aad_as_bytes= aad_as_vec.as_slice(); //decrypt message let plaintext_bytes = - chacha20poly1305_ietf::decrypt(ciphertext_as_bytes, sym_key, &nonce).map_err(|err| { + chacha20poly1305_ietf::decrypt_detached(ciphertext_as_bytes, + sym_key, + &nonce, + &tag, + Some(aad_as_bytes)) + .map_err(|err| { CryptoError::UnknownCryptoError(format!("Failed to decrypt ciphertext {}", err)) })?; @@ -621,6 +542,7 @@ impl CryptoService { mod tests { use super::*; use domain::crypto::did::MyDidInfo; + use utils::crypto::randombytes::randombytes; #[test] fn create_my_did_with_works_for_empty_info() { @@ -895,148 +817,119 @@ mod tests { #[test] pub fn test_encrypt_plaintext_and_decrypt_ciphertext_works() { let service: CryptoService = CryptoService::new(); - let message : &str = "Message to encrypt"; + let plaintext : &str = "Message to encrypt"; + let aad = randombytes(100); + let sym_key = gen_key(); - let (sym_key, iv, expected_ciphertext) = service - .encrypt_plaintext(message); + let (expected_ciphertext, iv_encoded, tag) = service + .encrypt_plaintext(plaintext, aad.as_slice(), &sym_key); - //convert values to base64 encoded strings - let plaintext_str = base64::encode(expected_ciphertext.as_slice()); - let iv_encoded = base64::encode(&iv[..]); let expected_plaintext = service - .decrypt_ciphertext(&plaintext_str, &iv_encoded, &sym_key).unwrap(); - assert_eq!(expected_plaintext, message); + .decrypt_ciphertext(&expected_ciphertext, &iv_encoded, &tag, &base64::encode(aad.as_slice()), &sym_key).unwrap(); + assert_eq!(expected_plaintext, plaintext); } #[test] pub fn test_encrypt_plaintext_decrypt_ciphertext_empty_string_works() { let service: CryptoService = CryptoService::new(); - let message : &str = ""; + let plaintext : &str = "Hello World"; + let aad = randombytes(100); + let sym_key = gen_key(); - let (sym_key, iv, expected_ciphertext) = service - .encrypt_plaintext(message); + let (expected_ciphertext, iv_encoded, tag) = service + .encrypt_plaintext(plaintext, aad.as_slice(), &sym_key); - //convert values to base64 encoded strings - let plaintext_str = base64::encode(expected_ciphertext.as_slice()); - let iv_encoded = base64::encode(&iv[..]); let expected_plaintext = service - .decrypt_ciphertext(&plaintext_str, &iv_encoded, &sym_key).unwrap(); - assert_eq!(expected_plaintext, message); + .decrypt_ciphertext(&expected_ciphertext, &iv_encoded, &tag, &base64::encode(aad.as_slice()), &sym_key).unwrap(); + assert_eq!(expected_plaintext, plaintext); } #[test] pub fn test_encrypt_plaintext_decrypt_ciphertext_bad_iv_fails() { let service: CryptoService = CryptoService::new(); - let message : &str = "hello world"; + let plaintext : &str = ""; + let aad = randombytes(100); + let sym_key = gen_key(); - let (sym_key, iv, expected_ciphertext) = service - .encrypt_plaintext(message); + let (expected_ciphertext, _, tag) = service + .encrypt_plaintext(plaintext, aad.as_slice(), &sym_key); //convert values to base64 encoded strings - let plaintext_str = base64::encode(expected_ciphertext.as_slice()); let bad_iv_input = "invalid_iv"; let expected_error = service - .decrypt_ciphertext(&plaintext_str, bad_iv_input, &sym_key); + .decrypt_ciphertext(&expected_ciphertext, bad_iv_input, &tag, &base64::encode(aad.as_slice()), &sym_key); assert!(expected_error.is_err()); } #[test] pub fn test_encrypt_plaintext_decrypt_ciphertext_bad_ciphertext_fails() { let service: CryptoService = CryptoService::new(); - let message : &str = "hello world"; + let plaintext : &str = "Hello World"; + let aad = randombytes(100); + let sym_key = gen_key(); - let (sym_key, iv, expected_ciphertext) = service - .encrypt_plaintext(message); + let (_, iv_encoded, tag) = service + .encrypt_plaintext(plaintext, aad.as_slice(), &sym_key); - //convert values to base64 encoded strings let bad_ciphertext= base64::encode("bad_ciphertext".as_bytes()); - let iv_encoded = base64::encode(&iv[..]); let expected_error = service - .decrypt_ciphertext(&bad_ciphertext, &iv_encoded, &sym_key); + .decrypt_ciphertext(&bad_ciphertext, &iv_encoded, &tag, &base64::encode(aad.as_slice()), &sym_key); assert!(expected_error.is_err()); } #[test] pub fn test_encrypt_plaintext_and_decrypt_ciphertext_wrong_sym_key_fails() { let service: CryptoService = CryptoService::new(); - let message : &str = "Message to encrypt"; + let plaintext : &str = "Hello World"; + let aad = randombytes(100); + let sym_key = chacha20poly1305_ietf::gen_key(); - let (sym_key, iv, expected_ciphertext) = service - .encrypt_plaintext(message); + let (expected_ciphertext, iv_encoded, tag) = service + .encrypt_plaintext(plaintext, aad.as_slice(), &sym_key); - //convert values to base64 encoded stringsT - let plaintext_str = base64::encode(expected_ciphertext.as_slice()); - let iv_encoded = base64::encode(&iv[..]); - let bad_sym_key = chacha20poly1305_ietf::gen_key(); + let bad_sym_key= gen_key(); let expected_error = service - .decrypt_ciphertext(&plaintext_str, &iv_encoded, &bad_sym_key); + .decrypt_ciphertext(&expected_ciphertext, &iv_encoded, &tag, &base64::encode(aad.as_slice()), &bad_sym_key); assert!(expected_error.is_err()); } + #[test] + pub fn test_encrypt_plaintext_and_decrypt_ciphertext_bad_tag_fails() { + let service: CryptoService = CryptoService::new(); + let plaintext : &str = "Hello World"; + let aad = randombytes(100); + let sym_key = gen_key(); + let (expected_ciphertext, iv_encoded, tag) = service + .encrypt_plaintext(plaintext, aad.as_slice(), &sym_key); + let bad_tag = "bad_tag".to_string(); -// #[test] -// pub fn test_auth_encrypt_recipient_works() { -// //setup route_service -// let service: CryptoService = CryptoService::new(); -// -// //setup DIDs and Keys -// let did_info = MyDidInfo { did: None, cid: None, seed: None, crypto_type: None }; -// let (my_did, my_key) = service.create_my_did(&did_info).unwrap(); -// let (their_did, their_key) = service.create_my_did(&did_info.clone()).unwrap(); -// -// let my_key_for_encrypt = my_key.clone(); -// let their_did_for_encrypt = Did::new(their_did.did, their_did.verkey); -// -// let my_key_for_decrypt = their_key.clone(); -// let their_did_for_decrypt = Did::new(my_did.did, my_did.verkey); -// -// //sym_key -// let sym_key = chacha20poly1305_ietf::gen_key(); -// -// //pack then unpack message -// let auth_recipient = service -// .auth_encrypt_recipient(&my_key_for_encrypt, &their_did_for_encrypt.verkey, &sym_key); -// -// //verify no errors are thrown -// assert!(auth_recipient.is_ok()); -// } - -// pub fn test_auth_encrypt_and_auth_decrypt_recipient_works() { -// //setup route_service -// let service: CryptoService = CryptoService::new(); -// -// //setup DIDs and Keys -// let did_info = MyDidInfo { did: None, cid: None, seed: None, crypto_type: None }; -// let (my_did, my_key) = service.create_my_did(&did_info).unwrap(); -// let (their_did, their_key) = service.create_my_did(&did_info.clone()).unwrap(); -// -// let my_key_for_encrypt = my_key.clone(); -// let their_did_for_encrypt = Did::new(their_did.did, their_did.verkey); -// -// let my_key_for_decrypt = their_key.clone(); -// let their_did_for_decrypt = Did::new(my_did.did, my_did.verkey); -// -// //sym_key -// let sym_key = xsalsa20::create_key(); -// -// //pack then unpack message -// let auth_recipient = service -// .auth_encrypt_recipient(&my_key_for_encrypt, &their_did_for_encrypt.verkey, &sym_key); -// -// let (expected_sym_key, sender_vk) = service -// .auth_decrypt_recipient(&recv_key, auth_recipient, cs.clone()) -// .unwrap(); -// -// //verify same plaintext goes in and comes out -// assert_eq!(expected_sym_key, sym_key); -// assert_eq!(expected_send_key.verkey, sender_vk); -// } + let expected_error = service + .decrypt_ciphertext(&expected_ciphertext, &iv_encoded, &bad_tag, &base64::encode(aad.as_slice()), &sym_key); + assert!(expected_error.is_err()); + } + + #[test] + pub fn test_encrypt_plaintext_and_decrypt_ciphertext_bad_aad_fails() { + let service: CryptoService = CryptoService::new(); + let plaintext : &str = "Hello World"; + let aad = randombytes(100); + let sym_key = gen_key(); + + let (expected_ciphertext, iv_encoded, tag) = service + .encrypt_plaintext(plaintext, aad.as_slice(), &sym_key); + + let bad_aad = randombytes(100); + + let expected_error = service + .decrypt_ciphertext(&expected_ciphertext, &iv_encoded, &tag, &base64::encode(bad_aad.as_slice()), &sym_key); + assert!(expected_error.is_err()); + } } \ No newline at end of file From 5a268fba4198dd11ad3de742c240981d3a42980e Mon Sep 17 00:00:00 2001 From: Kyle Den Hartog Date: Thu, 20 Dec 2018 17:19:45 -0700 Subject: [PATCH 03/17] needs c headers and tests - but compiles code upto API layer Signed-off-by: Kyle Den Hartog --- libindy/src/api/agent.rs | 206 ------------------ libindy/src/api/crypto.rs | 170 ++++++++++++++- libindy/src/api/mod.rs | 2 +- libindy/src/commands/agent.rs | 320 ---------------------------- libindy/src/commands/crypto.rs | 331 ++++++++++++++++++++++++++++- libindy/src/commands/mod.rs | 10 - libindy/src/domain/agent/mod.rs | 86 -------- libindy/src/domain/crypto/mod.rs | 1 + libindy/src/domain/crypto/pack.rs | 28 +++ libindy/src/errors/agent.rs | 58 ----- libindy/src/errors/indy.rs | 12 -- libindy/src/errors/mod.rs | 1 - libindy/src/services/agent/mod.rs | 314 --------------------------- libindy/src/services/crypto/mod.rs | 89 ++++---- libindy/src/services/mod.rs | 1 - 15 files changed, 571 insertions(+), 1058 deletions(-) delete mode 100644 libindy/src/api/agent.rs delete mode 100644 libindy/src/commands/agent.rs create mode 100644 libindy/src/domain/crypto/pack.rs delete mode 100644 libindy/src/errors/agent.rs delete mode 100644 libindy/src/services/agent/mod.rs diff --git a/libindy/src/api/agent.rs b/libindy/src/api/agent.rs deleted file mode 100644 index 874be55642..0000000000 --- a/libindy/src/api/agent.rs +++ /dev/null @@ -1,206 +0,0 @@ -//extern crate libc; -// -//use api::ErrorCode; -//use commands::agent::AgentCommand; -//use commands::{Command, CommandExecutor}; -//use errors::ToErrorCode; -//use utils::ctypes; -// -//use self::libc::c_char; -// -///// Verify a signature with a verkey. -///// -///// Note to use DID keys with this function you can call indy_key_for_did to get key id (verkey) -///// for specific DID. -///// -///// #Params -///// command_handle: command handle to map callback to user context. -///// wallet_handle: wallet identifier containing the key of the sender's private key -///// message: the message which is going to be packed up -///// receiver_keys: a string in the format of a json list which will contain the list of receiver's keys -///// the message is being encrypted for. For example: -///// "[, ]" -///// cb: Callback that takes command result as parameter. -///// -///// #Returns -///// a JWE in the format defined below: -///// { -///// "protected": "b64URLencoded({ -///// "enc": "xsalsa20poly1305", -///// "typ": "JWM/1.0", -///// "aad_hash_alg": "BLAKE2b", -///// "cek_enc": "authcrypt" -///// })" -///// "recipients": [ -///// { -///// "encrypted_key": , -///// "header": { -///// "sender": , -///// "kid": "did:sov:1234512345#key-id", -///// "key": "b64URLencode(ver_key)" -///// } -///// }, -///// ], -///// "aad": , -///// "iv": , -///// "ciphertext": , -///// "tag": -///// } -///// -///// #Errors -///// Common* -///// Wallet* -///// Ledger* -///// Crypto* -// -// -//#[no_mangle] -//pub fn indy_auth_pack_message( -// command_handle: i32, -// wallet_handle: i32, -// message: *const c_char, -// receiver_keys: *const c_char, -// sender: *const c_char, -// cb: Option, -//) -> ErrorCode { -// trace!("indy_auth_pack_message: >>> wallet_handle: {:?}, message: {:?}, receiver_keys: {:?}, sender: {:?}", -// wallet_handle, message, receiver_keys, sender); -// -// check_useful_c_str!(message, ErrorCode::CommonInvalidParam3); -// check_useful_c_str!(receiver_keys, ErrorCode::CommonInvalidParam4); -// check_useful_c_str!(sender, ErrorCode::CommonInvalidParam5); -// check_useful_c_callback!(cb, ErrorCode::CommonInvalidParam6); -// -// trace!("indy_auth_pack_message: entities >>> wallet_handle: {:?}, message: {:?}, receiver_keys: {:?}, sender: {:?}", -// wallet_handle, message, receiver_keys, sender); -// -// let result = CommandExecutor::instance().send(Command::Agent(AgentCommand::AuthPackMessage( -// message, -// receiver_keys, -// sender, -// wallet_handle, -// Box::new(move |result| { -// let (err, jwe) = result_to_err_code_1!(result, String::new()); -// trace!( -// "indy_auth_pack_message: cb command_handle: {:?}, err: {:?}, jwe: {:?}", -// command_handle, -// err, -// jwe -// ); -// let jwe = ctypes::string_to_cstring(jwe); -// cb(command_handle, err, jwe.as_ptr()) -// }), -// ))); -// -// let res = result_to_err_code!(result); -// -// trace!("indy_auth_pack_message: <<< res: {:?}", res); -// -// res -//} -// -//#[no_mangle] -//pub fn indy_anon_pack_message( -// command_handle: i32, -// message: *const c_char, -// receiver_keys: *const c_char, -// cb: Option, -//) -> ErrorCode { -// trace!( -// "indy_anon_pack_message: >>> message: {:?}, receiver_keys: {:?}", -// message, -// receiver_keys -// ); -// -// check_useful_c_str!(message, ErrorCode::CommonInvalidParam3); -// check_useful_c_str!(receiver_keys, ErrorCode::CommonInvalidParam4); -// check_useful_c_callback!(cb, ErrorCode::CommonInvalidParam5); -// -// trace!( -// "indy_anon_pack_message: entities >>> message: {:?}, receiver_keys: {:?}", -// message, -// receiver_keys -// ); -// -// let result = CommandExecutor::instance().send(Command::Agent(AgentCommand::AnonPackMessage( -// message, -// receiver_keys, -// Box::new(move |result| { -// let (err, jwe) = result_to_err_code_1!(result, String::new()); -// trace!( -// "indy_anon_pack_message: cb command_handle: {:?}, err: {:?}, jwe: {:?}", -// command_handle, -// err, -// jwe -// ); -// let verkey = ctypes::string_to_cstring(jwe); -// cb(command_handle, err, verkey.as_ptr()) -// }), -// ))); -// -// let res = result_to_err_code!(result); -// -// trace!("indy_anon_pack_message: <<< res: {:?}", res); -// -// res -//} -// -////update function to return key used -//#[no_mangle] -//pub fn indy_unpack_message( -// command_handle: i32, -// wallet_handle: i32, -// jwe: *const c_char, -// sender: *const c_char, -// cb: Option< -// extern "C" fn( -// xcommand_handle: i32, -// err: ErrorCode, -// plaintext: *const c_char, -// sender_vk: *const c_char, -// ), -// >, -//) -> ErrorCode { -// trace!( -// "indy_unpack_message: >>> wallet_handle: {:?}, jwe: {:?}, sender: {:?}", -// wallet_handle, -// jwe, -// sender -// ); -// -// check_useful_c_str!(jwe, ErrorCode::CommonInvalidParam3); -// check_useful_c_str!(sender, ErrorCode::CommonInvalidParam4); -// check_useful_c_callback!(cb, ErrorCode::CommonInvalidParam5); -// -// trace!( -// "indy_unpack_message: entities >>> wallet_handle: {:?}, jwe: {:?}, sender: {:?}", -// wallet_handle, -// jwe, -// sender -// ); -// -// let result = CommandExecutor::instance().send(Command::Agent(AgentCommand::UnpackMessage( -// jwe, -// sender, -// wallet_handle, -// Box::new(move |result| { -// let (err, plaintext, sender_vk) = -// result_to_err_code_2!(result, String::new(), String::new()); -// trace!( -// "indy_unpack_message: cb command_handle: {:?}, err: {:?}, plaintext: {:?}", -// command_handle, -// err, -// plaintext -// ); -// let plaintext = ctypes::string_to_cstring(plaintext); -// let sender_vk = ctypes::string_to_cstring(sender_vk); -// cb(command_handle, err, plaintext.as_ptr(), sender_vk.as_ptr()) -// }), -// ))); -// -// let res = result_to_err_code!(result); -// -// trace!("indy_unpack_message: <<< res: {:?}", res); -// -// res -//} diff --git a/libindy/src/api/crypto.rs b/libindy/src/api/crypto.rs index 4dd78b4288..fce1da2c62 100644 --- a/libindy/src/api/crypto.rs +++ b/libindy/src/api/crypto.rs @@ -557,4 +557,172 @@ pub extern fn indy_crypto_anon_decrypt(command_handle: IndyHandle, trace!("indy_crypto_anon_decrypt: <<< res: {:?}", res); res -} \ No newline at end of file +} + +/// Packs a message +/// +/// Note to use DID keys with this function you can call indy_key_for_did to get key id (verkey) +/// for specific DID. +/// +/// #Params +/// command_handle: command handle to map callback to user context. +/// message: the message which is going to be packed up +/// receivers: a string in the format of a json list which will contain the list of receiver's keys +/// the message is being encrypted for. +/// Example: +/// "[, ]" +/// sender: a string in the form +/// cb: Callback that takes command result as parameter. +/// +/// #Returns +/// a JWE in using authcrypt alg is defined below: +/// { +/// "protected": "b64URLencode({ +/// "enc": "xsalsa20poly1305", +/// "typ": "JWM/1.0", +/// "alg": "authcrypt", +/// "recipients": [ +/// { +/// "encrypted_key": , +/// "header": { +/// "sender": , +/// "kid": "b64URLencode(ver_key)" +/// } +/// }, +/// ], +/// })" +/// "iv": , +/// "ciphertext": , +/// "tag": +/// } +/// +/// Alternative example in using anoncrypt alg is defined below: +/// { +/// "protected": "b64URLencode({ +/// "enc": "xsalsa20poly1305", +/// "typ": "JWM/1.0", +/// "alg": "anoncrypt", +/// "recipients": [ +/// { +/// "encrypted_key": , +/// "header": { +/// "kid": "b64URLencode(ver_key)" +/// } +/// }, +/// ], +/// })" +/// "iv": , +/// "ciphertext": , +/// "tag": +/// } +/// +/// +/// #Errors +/// Common* +/// Wallet* +/// Ledger* +/// Crypto* +/// Agent* + + +#[no_mangle] +pub fn indy_pack_message( + command_handle: i32, + wallet_handle: i32, + msg_data: *const u8, + msg_len: u32, + receiver_keys: *const c_char, + sender: *const c_char, + cb: Option, +) -> ErrorCode { + trace!("indy_pack_message: >>> msg_data: {:?}, msg_len: {:?}, receiver_keys: {:?}, sender: {:?}", + msg_data, msg_len, receiver_keys, sender); + + check_useful_c_byte_array!(msg_data, msg_len, ErrorCode::CommonInvalidParam2, ErrorCode::CommonInvalidParam3); + check_useful_c_str!(receiver_keys, ErrorCode::CommonInvalidParam4); + check_useful_opt_c_str!(sender, ErrorCode::CommonInvalidParam5); + check_useful_c_callback!(cb, ErrorCode::CommonInvalidParam6); + + trace!("indy_pack_message: entities >>> msg_data: {:?}, msg_len: {:?}, receiver_keys: {:?}, sender: {:?}", + msg_data, msg_len, receiver_keys, sender); + + let result = CommandExecutor::instance().send(Command::Crypto(CryptoCommand::PackMessage( + msg_data, + receiver_keys, + sender, + wallet_handle, + Box::new(move |result| { + let (err, jwe) = result_to_err_code_1!(result, String::new()); + trace!( + "indy_auth_pack_message: cb command_handle: {:?}, err: {:?}, jwe: {:?}", + command_handle, + err, + jwe + ); + let jwe = ctypes::string_to_cstring(jwe); + cb(command_handle, err, jwe.as_ptr()) + }), + ))); + + let res = result_to_err_code!(result); + + trace!("indy_auth_pack_message: <<< res: {:?}", res); + + res +} + +//update function to return key used +#[no_mangle] +pub fn indy_unpack_message( + command_handle: i32, + wallet_handle: i32, + jwe: *const c_char, + cb: Option< + extern "C" fn( + xcommand_handle: i32, + err: ErrorCode, + plaintext: *const c_char, + sender_vk: *const c_char, + ), + >, +) -> ErrorCode { + trace!( + "indy_unpack_message: >>> wallet_handle: {:?}, jwe: {:?}", + wallet_handle, + jwe + ); + + check_useful_c_str!(jwe, ErrorCode::CommonInvalidParam3); + check_useful_c_callback!(cb, ErrorCode::CommonInvalidParam4); + + trace!( + "indy_unpack_message: entities >>> wallet_handle: {:?}, jwe: {:?}", + wallet_handle, + jwe + ); + + let result = CommandExecutor::instance().send(Command::Crypto(CryptoCommand::UnpackMessage( + jwe, + wallet_handle, + Box::new(move |result| { + let (err, plaintext, sender_vk) = + result_to_err_code_2!(result, String::new(), String::new()); + trace!( + "indy_unpack_message: cb command_handle: {:?}, err: {:?}, plaintext: {:?}, sender_vk: {:?}", + command_handle, + err, + plaintext, + sender_vk + ); + let plaintext = ctypes::string_to_cstring(plaintext); + let sender_vk = ctypes::string_to_cstring(sender_vk); + cb(command_handle, err, plaintext.as_ptr(), sender_vk.as_ptr()) + }), + ))); + + let res = result_to_err_code!(result); + + trace!("indy_unpack_message: <<< res: {:?}", res); + + res +} diff --git a/libindy/src/api/mod.rs b/libindy/src/api/mod.rs index 226278c525..178c141602 100644 --- a/libindy/src/api/mod.rs +++ b/libindy/src/api/mod.rs @@ -12,7 +12,7 @@ pub mod blob_storage; pub mod non_secrets; pub mod payments; pub mod logger; -pub mod agent; +// pub mod agent; use self::libc::c_char; diff --git a/libindy/src/commands/agent.rs b/libindy/src/commands/agent.rs deleted file mode 100644 index 9db05c7127..0000000000 --- a/libindy/src/commands/agent.rs +++ /dev/null @@ -1,320 +0,0 @@ -use errors::agent::AgentError; -use serde_json; -use domain::agent::AuthAMES; -use domain::agent::AnonRecipient; -use domain::agent::AnonAMES; -use domain::crypto::key::Key; -use errors::common::CommonError; -use errors::indy::IndyError; -use services::crypto::CryptoService; -use services::agent::AgentService; -use services::wallet::WalletService; -use services::wallet::RecordOptions; -use std::rc::Rc; -use std::result; -use utils::crypto::base64; -use domain::agent::FromAddress; -use domain::agent::ToAddress; -use domain::agent::Recipient; -use utils::crypto::base58; -use domain::agent::Header; - -type Result = result::Result; - -pub enum AgentCommand { - AuthPackMessage( - String, // plaintext message - String, //list of receiving keys - String, //my verkey - i32, //wallet_handle - Box) + Send>, - ), - AnonPackMessage( - String, // plaintext message - String, // list of receiving keys - Box) + Send>, - ), - UnpackMessage( - String, // JWE - String, // my verkey - i32, // wallet handle - Box) + Send>, - ), -} - -pub struct AgentCommandExecutor { - wallet_service: Rc, - crypto_service: Rc, - agent_service: Rc, -} - -impl AgentCommandExecutor { - pub fn new( - wallet_service: Rc, - crypto_service: Rc, - agent_service: Rc, - ) -> AgentCommandExecutor { - AgentCommandExecutor { - wallet_service, - crypto_service, - agent_service, - } - } - - pub fn execute(&self, command: AgentCommand) { - match command { - AgentCommand::AuthPackMessage(message, receiver_keys_json, sender_verkey, wallet_handle, cb) => { - info!("PackMessage command received"); - cb(self.auth_pack_msg(&message, &receiver_keys_json, &sender_verkey, wallet_handle)); - } - AgentCommand::AnonPackMessage(message, receiver_keys_json, cb) => { - info!("PackMessage command received"); - cb(self.anon_pack_msg(&message, &receiver_keys_json)); - } - AgentCommand::UnpackMessage(jwe, sender_verkey, wallet_handle, cb) => { - info!("UnpackMessage command received"); - cb(self.unpack_msg(&jwe, &sender_verkey, wallet_handle)); - } - }; - } - -// //TODO change errors -// pub fn auth_pack_msg( -// &self, -// message: &str, -// receiver_keys_json: &str, -// sender_verkey: &str, -// wallet_handle: i32, -// ) -> Result { -// //convert type from json array to Vec -// let recv_keys: Vec<&str> = serde_json::from_str(receiver_keys_json).map_err(|err| { -// IndyError::CommonError(CommonError::InvalidParam4(format!("Failed to serialize recv_keys {:?}", err))) -// })?; -// -// //encrypt ciphertext -// let (sym_key, iv, ciphertext) = self.crypto_service.encrypt_ciphertext(message); -// -// //convert sender_vk to Key -// let my_key: Key = self.wallet_service.get_indy_object(wallet_handle, sender_verkey, &RecordOptions::id_value())?; -// -// //encrypt ceks -// let mut auth_recipients = vec![]; -// -// for their_vk in recv_keys { -// auth_recipients.push( -// self.crypto_service -// .auth_encrypt_recipient(&my_key, their_vk, &sym_key) -// .map_err(|err| { -// IndyError::CommonError(CommonError::InvalidStructure(format!("Invalid anon_recipient structure: {}", err))) -// })?, -// ); -// } -// -// //serialize AuthAMES -// let jwe_json = AuthAMES { -// recipients: auth_recipients, -// ver: "AuthAMES/1.0/".to_string(), -// enc: "xsalsa20poly1305".to_string(), -// ciphertext: base64::encode(ciphertext.as_slice()), -// iv: base64::encode(&iv[..]), -// }; -// serde_json::to_string(&jwe_json) -// .map_err(|err| IndyError::CommonError(CommonError::InvalidStructure(format!("Failed to serialize JWE {}", err)))) -// } -// -// pub fn anon_pack_msg(&self, message: &str, receiver_keys_json: &str) -> Result { -// //convert type from json array to Vec<&str> -// let recv_keys: Vec<&str> = serde_json::from_str(receiver_keys_json).map_err(|err| { -// IndyError::CommonError(CommonError::InvalidParam4(format!("Failed to serialize recv_keys {:?}", err))) -// })?; -// -// //encrypt ciphertext -// let (sym_key, iv, ciphertext) = self.crypto_service.encrypt_ciphertext(message); -// -// //encrypt ceks -// let mut anon_recipients: Vec = vec![]; -// -// for their_vk in recv_keys { -// anon_recipients.push( -// self.crypto_service -// .anon_encrypt_recipient(their_vk, sym_key.clone()) -// .map_err(|err| { -// IndyError::AgentError(AgentError::CommonError(CommonError::InvalidStructure(format!("Invalid anon_recipient structure: {}", err)))) -// })?, -// ); -// } -// -// //serialize AnonAMES -// let anon_ames_struct = AnonAMES { -// recipients: anon_recipients, -// ver: "AnonAMES/1.0/".to_string(), -// enc: "xsalsa20poly1305".to_string(), -// ciphertext: base64::encode(ciphertext.as_slice()), -// iv: base64::encode(&iv[..]), -// }; -// serde_json::to_string(&anon_ames_struct) -// .map_err(|err| IndyError::AgentError(AgentError::PackError(format!("Failed to serialize JWE {}", err)))) -// } -~~~` - pub fn pack_msg( - &self, - message: &[u8], - receivers: &str, - sender: Option<&str> - ) -> Result { - - // encrypt ciphertext - let (sym_key, iv, ciphertext) = self.crypto_service.encrypt_ciphertext(message); - - //list of ceks used to construct JWE later - let mut encrypted_recipients_struct: Vec = vec![]; - - match sender { - Some(s) => { - //parse senders and receivers to structs - let from_address : Result = serde_json::from_str(s) - .map_err(|err| { - IndyError::AgentError(AgentError::PackError(format!("Failed to deserialize sender {}", err))) - }); - let to_address : Result = serde_json::from_str(receivers) - .map_err(|err| { - IndyError::AgentError(AgentError::PackError(format!("Failed to deserialize receivers {}", err))) - }); - - //encrypt sym_key for recipient - for their_vk in receivers_list { - let (e_cek, cek_nonce) = self.crypto_service - .crypto_box(my_key, their_vk, &sym_key[..])?; - - //serialize enc_header - let sender_vk_bytes = base58::decode(&my_key.verkey) - .map_err(|err| { - IndyError::CommonError(err) - })?; - - //encrypt enc_from - let enc_sender = self.crypto_service. - crypto_box_seal(recp_vk, sender_vk_bytes.as_slice())?; - - //create recipient struct and push to encrypted list - encrypted_recipients_struct.push( - Recipient { - encrypted_key: base58::encode(e_cek.as_bytes()), - header: Header { - sender: Some(base64::encode(enc_sender.as_bytes())), - kid: base64::encode(their_vk.as_bytes()) - } - }); - - } // end for-loop - }, - None => { - //anoncrypt - - } - } - - //serialize JWE struct - - //return JWE_json - serde_json::to_string(&jwe_struct) - .map_err(|err| IndyError::AgentError(AgentError::PackError(format!("Failed to serialize JWE {}", err)))) - } - - pub fn unpack_msg( - &self, - jwe_json: &str, - sender: &str, - wallet_handle: i32, - ) -> Result<(String, String)> { - - if jwe_json.contains("AuthAMES/1.0/") { //handles unpacking auth_crypt JWE - //deserialize json string to struct - let jwe_json: AuthAMES = serde_json::from_str(jwe_json).map_err(|err| { - IndyError::AgentError(AgentError::UnpackError(format!("Failed to deserialize auth ames {}", err))) - })?; - - //get recipient struct that matches sender_verkey parameter - let recipient_struct = - self.agent_service.get_auth_recipient_header(sender, jwe_json.recipients)?; - - //get key to use for decryption - let my_key: &Key = &self.wallet_service.get_indy_object(wallet_handle, sender, &RecordOptions::id_value())?; - - //decrypt recipient header - let (ephem_sym_key, sender_vk) = self.crypto_service.auth_decrypt_recipient(my_key, recipient_struct)?; - - // decode - let message = self.crypto_service.decrypt_ciphertext( - &jwe_json.ciphertext, - &jwe_json.iv, - &ephem_sym_key, - )?; - - //TODO convert this to a json_string instead of Tuple - Ok((message, sender_vk)) - - } else if jwe_json.contains("AnonAMES/1.0/") { //handles unpacking anon_crypt JWE - //deserialize json string to struct - let jwe_json: AnonAMES = serde_json::from_str(jwe_json).map_err(|err| { - IndyError::AgentError(AgentError::UnpackError(format!("Failed to deserialize auth ames {}", err))) - })?; - - //get recipient struct that matches sender_verkey parameter - let recipient_struct = - self.agent_service.get_anon_recipient_header(sender, jwe_json.recipients)?; - - //get key to use for decryption - let my_key: &Key = &self.wallet_service - .get_indy_object(wallet_handle, sender, &RecordOptions::id_value()) - .map_err(|err| IndyError::AgentError(AgentError::UnpackError(format!("Can't find my_key: {:?}", err))))?; - - //decrypt recipient header - let ephem_sym_key = self.crypto_service.anon_decrypt_recipient(my_key, recipient_struct)?; - - //decrypt message - let message = self.crypto_service.decrypt_ciphertext( - &jwe_json.ciphertext, - &jwe_json.iv, - &ephem_sym_key, - )?; - - //TODO convert this to a json_string instead of Tuple - Ok((message, "".to_string())) - - } else { - Err(IndyError::AgentError(AgentError::UnpackError(format!("Failed to unpack - unidentified ver provided")))) - } - } -} - -#[cfg(test)] -mod tests { - use domain::agent::{FromAddress, ToAddress}; - use serde_json; - use serde_json::Error; - - #[test] - pub fn test_serde_json_works() { - let sender = r#"{ wallet_key: ["pubkey12345", wallet_handle: 1] }"#; - let receivers = r#"["pubkey_1", "pubkey2", "pubkey3"]"#; - - let from_address : Result = serde_json::from_str(sender); - let to_address : Result = serde_json::from_str(receivers); - - assert!(from_address.is_ok()); - assert!(to_address.is_ok()); - } - - #[test] - pub fn test_serde_json_to_string_works() { - let sender = r#"{ wallet_key: { ["pubkey12345", wallet_handle: 1] } }"#; - let receivers = r#"["pubkey_1", "pubkey2", "pubkey3"]"#; - - let from_address : Result = serde_json::from_str(sender); - let to_address : Result = serde_json::from_str(receivers); - - assert!(from_address.is_ok()); - assert!(to_address.is_ok()); - } -} \ No newline at end of file diff --git a/libindy/src/commands/crypto.rs b/libindy/src/commands/crypto.rs index 6428f5ef6f..a56b8f4218 100644 --- a/libindy/src/commands/crypto.rs +++ b/libindy/src/commands/crypto.rs @@ -4,13 +4,19 @@ extern crate serde_json; use std::collections::HashMap; use errors::indy::IndyError; +use errors::crypto::CryptoError; +use errors::common::CommonError; +use errors::wallet::WalletError; use domain::crypto::key::{KeyInfo, Key, KeyMetadata}; +use domain::crypto::pack::*; use services::wallet::{WalletService, RecordOptions}; use services::crypto::CryptoService; use std::rc::Rc; use std::str; use std::result; +use utils::crypto::chacha20poly1305_ietf; +use utils::crypto::base64; type Result = result::Result; @@ -57,7 +63,19 @@ pub enum CryptoCommand { i32, // wallet handle String, // my vk Vec, // msg - Box>) + Send>) + Box>) + Send>), + PackMessage( + Vec, // plaintext message + String, // list of receiver's keys + Option, // senders object + i32, //wallet handle + Box) + Send>, + ), + UnpackMessage( + String, // JWE + i32, // wallet handle + Box) + Send>, + ), } pub struct CryptoCommandExecutor { @@ -113,6 +131,14 @@ impl CryptoCommandExecutor { info!("AnonymousDecrypt command received"); cb(self.anonymous_decrypt(wallet_handle, &my_vk, &encrypted_msg)); } + CryptoCommand::PackMessage(message, receivers, sender, wallet_handle, cb) => { + info!("PackMessage command received"); + cb(self.pack_msg(message, &receivers, sender, wallet_handle)); + } + CryptoCommand::UnpackMessage(jwe, wallet_handle, cb) => { + info!("UnpackMessage command received"); + cb(self.unpack_msg(&jwe, wallet_handle)); + } }; } @@ -255,4 +281,307 @@ impl CryptoCommandExecutor { Ok(res) } + + //TODO: Refactor pack to be more modular to version changes or crypto_scheme changes + //this match statement is super messy, but the easiest way to comply with current architecture + pub fn pack_msg( + &self, + message: Vec, + receivers: &str, + sender_vk: Option, + wallet_handle: i32 + ) -> Result { + + //generate symmetrical key + let sym_key = chacha20poly1305_ietf::gen_key(); + + //list of ceks used to construct JWE later + let mut encrypted_recipients_struct: Vec = vec![]; + + match sender_vk { + + Some(verkey) => { + //TODO find more readable way to perform authcrypt funtionality something like private command function + + //get my_key from my wallet + let my_key = self.wallet_service + .get_indy_object(wallet_handle, &verkey, &RecordOptions::id_value())?; + + //parse receivers to structs + let receiver_list : Vec = serde_json::from_str(receivers) + .map_err(|err| + IndyError::CryptoError( + CryptoError::CommonError( + CommonError::InvalidStructure( + format!("Failed to deserialize receiver list of keys {}", err) + ) + ) + ) + )?; + + //encrypt sym_key for recipient + for their_vk in receiver_list { + let enc_sym_key = self.crypto_service + .authenticated_encrypt(&my_key, &their_vk, &sym_key[..])?; + + //create recipient struct and push to encrypted list + encrypted_recipients_struct.push( + Recipient { + encrypted_key: base64::encode(enc_sym_key.as_slice()), + header: Header { + kid: base64::encode(&their_vk.as_bytes()) + } + }); + } // end for-loop + + //structure protected and base64URL encode it + let protected_struct = Protected { + enc: "xchacha20poly1305".to_string(), + typ: "JWM/1.0".to_string(), + alg: "Authcrypt".to_string(), + recipients: encrypted_recipients_struct, + }; + let protected_encoded = serde_json::to_string(&protected_struct) + .map_err(|err| + IndyError::CryptoError( + CryptoError::CommonError( + CommonError::InvalidStructure( + format!("Failed to serialize protected field {}", err)))) + )?; + + let base64_protected = base64::encode(protected_encoded.as_bytes()); + + // encrypt ciphertext and integrity protect "protected" field + let (iv, ciphertext, tag) = self.crypto_service + .encrypt_plaintext(message, &base64_protected, &sym_key); + + //construct JWE struct + let jwe_struct = JWE { + protected : base64_protected, + iv, + ciphertext, + tag + }; + + //convert JWE struct to a string and return + serde_json::to_string(&jwe_struct) + .map_err(|err| + IndyError::CryptoError( + CryptoError::CommonError( + CommonError::InvalidStructure( + format!("Failed to serialize JWE {}", err) + ) + ) + ) + ) + }, + + None => { + //TODO find more readable way to perform anoncrypt funtionality something like private command function + + //parse receivers to structs + let receiver_list : Vec = serde_json::from_str(receivers) + .map_err(|err| + IndyError::CryptoError( + CryptoError::CommonError( + CommonError::InvalidStructure( + format!("Failed to deserialize receiver list of keys {}", err) + ) + ) + ) + )?; + + //encrypt sym_key for recipient + for their_vk in receiver_list { + + //encrypt sender verkey + let enc_sym_key = self.crypto_service + .crypto_box_seal(&their_vk, &sym_key[..])?; + + //create recipient struct and push to encrypted list + encrypted_recipients_struct.push( + Recipient { + encrypted_key: base64::encode(enc_sym_key.as_slice()), + header: Header { + kid: base64::encode(&their_vk.as_bytes()) + } + }); + } // end for-loop + + //structure protected and base64URL encode it + let protected_struct = Protected { + enc: "xchacha20poly1305".to_string(), + typ: "JWM/1.0".to_string(), + alg: "Anoncrypt".to_string(), + recipients: encrypted_recipients_struct, + }; + let protected_encoded = base64::encode(serde_json::to_string(&protected_struct) + .map_err(|err| + IndyError::CryptoError( + CryptoError::CommonError( + CommonError::InvalidStructure( + format!("Failed to serialize JWE {}", err) + ) + ) + ) + )?.as_bytes()); + + // encrypt ciphertext + let (iv, ciphertext, tag) = self.crypto_service + .encrypt_plaintext(message, &protected_encoded, &sym_key); + + //serialize JWE struct + let jwe_struct = JWE { + protected : protected_encoded, + iv, + ciphertext, + tag + }; + + //return JWE_json + return serde_json::to_string(&jwe_struct) + .map_err(|err| + IndyError::CryptoError( + CryptoError::CommonError( + CommonError::InvalidStructure( + format!("Failed to serialize JWE {}", err) + ) + ) + ) + ) + } + } + } + + pub fn unpack_msg( + &self, + jwe_json: &str, + wallet_handle: i32 + ) -> Result<(String, String)> { + + //serialize JWE to struct + let jwe_struct : JWE = serde_json::from_str(jwe_json) + .map_err(|err| + IndyError::CryptoError( + CryptoError::CommonError( + CommonError::InvalidStructure( + format!("Failed to deserialize auth ames {}", err) + ) + ) + ) + )?; + + //decode protected data + let protected_decoded_vec = base64::decode(&jwe_struct.protected)?; + let protected_decoded_str = String::from_utf8(protected_decoded_vec) + .map_err(|err| + IndyError::CryptoError( + CryptoError::CommonError( + CommonError::InvalidStructure( + format!("Failed to utf8 encode data {}", err) + ) + ) + ) + )?; + + //convert protected_data_str to struct + let protected_struct : Protected = serde_json::from_str(&protected_decoded_str) + .map_err(|err| + IndyError::CryptoError( + CryptoError::CommonError( + CommonError::InvalidStructure( + format!("Failed to deserialize protected data {}", err) + ) + ) + ) + )?; + + //search through recipients_list and check if one of the kid matches a verkey in the wallet + for recipient in protected_struct.recipients { + let key_in_wallet_result = self.wallet_service + .get_indy_object::(wallet_handle, &recipient.header.kid, &RecordOptions::id_value()); + + if key_in_wallet_result.is_ok() { + //TODO change to move this to a separate function and return recipient rather than + // putting logic inside the for loop for loops have no way to return values in rust + + //decode encrypted_key + let encrypted_key_vec = base64::decode(&recipient.encrypted_key)?; + + //get sym_key and sender data + let (sender, sym_key) = match protected_struct.alg.as_ref() { + "Authcrypt" => { + + //get my key based on kid + let my_key = self.wallet_service + .get_indy_object(wallet_handle, &recipient.header.kid, &RecordOptions::id_value())?; + + //decrypt sym_key + let (sender_vk , sym_key_as_vec) = self.crypto_service + .authenticated_decrypt(&my_key, encrypted_key_vec.as_slice())?; + + //convert to chacha Key struct + let sym_key : chacha20poly1305_ietf::Key = chacha20poly1305_ietf::Key::from_slice(&sym_key_as_vec[..]) + .map_err(|err| + IndyError::CryptoError( + CryptoError::CommonError( + CommonError::InvalidStructure( + format!("Failed to decrypt sym_key {}", err) + ) + ) + ) + )?; + + Ok((sender_vk, sym_key)) + }, + + "Anoncrypt" => { + //get my private key + let my_key = self.wallet_service + .get_indy_object(wallet_handle, &recipient.header.kid, &RecordOptions::id_value())?; + + //decrypt sym_key + let sym_key_as_vec = self.crypto_service + .crypto_box_seal_open(&my_key, encrypted_key_vec.as_slice())?; + + //convert to chacha Key struct + let sym_key : chacha20poly1305_ietf::Key = chacha20poly1305_ietf::Key::from_slice(&sym_key_as_vec[..]) + .map_err(|err| + IndyError::CryptoError( + CryptoError::CommonError( + CommonError::InvalidStructure( + format!("Failed to decrypt sym_key {}", err) + ) + ) + ) + )?; + + Ok((String::from(""), sym_key )) + }, + + _ => Err( + IndyError::CryptoError( + CryptoError::CommonError( + CommonError::InvalidStructure( + format!("Failed to deserialize sym_key encryption alg") + ) + ) + ) + ) + }?; + + let message = self.crypto_service + .decrypt_ciphertext(&jwe_struct.ciphertext, + &jwe_struct.protected, + &jwe_struct.iv, + &jwe_struct.tag, + &sym_key)?; + + return Ok((message, sender)) + } // close if statement if a kid matches a verkey found in wallet + } // close for loop searching through recipients on kid + + //If it gets to this point no verkey was found in wallet that matches a kid + return Err(IndyError::WalletError(WalletError::ItemNotFound)) + } } diff --git a/libindy/src/commands/mod.rs b/libindy/src/commands/mod.rs index 060f906a2e..571ae3629a 100644 --- a/libindy/src/commands/mod.rs +++ b/libindy/src/commands/mod.rs @@ -8,7 +8,6 @@ pub mod wallet; pub mod pairwise; pub mod non_secrets; pub mod payments; -pub mod agent; extern crate indy_crypto; extern crate threadpool; @@ -25,7 +24,6 @@ use commands::wallet::{WalletCommand, WalletCommandExecutor}; use commands::pairwise::{PairwiseCommand, PairwiseCommandExecutor}; use commands::non_secrets::{NonSecretsCommand, NonSecretsCommandExecutor}; use commands::payments::{PaymentsCommand, PaymentsCommandExecutor}; -// use commands::agent::{AgentCommand, AgentCommandExecutor}; use errors::common::CommonError; @@ -36,7 +34,6 @@ use services::pool::PoolService; use services::wallet::WalletService; use services::crypto::CryptoService; use services::ledger::LedgerService; -use services::agent::AgentService; use domain::IndyConfig; @@ -59,7 +56,6 @@ pub enum Command { Pairwise(PairwiseCommand), NonSecrets(NonSecretsCommand), Payments(PaymentsCommand), - //Agent(AgentCommand) } lazy_static! { @@ -100,7 +96,6 @@ impl CommandExecutor { let payments_service = Rc::new(PaymentsService::new()); let pool_service = Rc::new(PoolService::new()); let wallet_service = Rc::new(WalletService::new()); - //let agent_service = Rc::new(AgentService::new()); let anoncreds_command_executor = AnoncredsCommandExecutor::new(anoncreds_service.clone(), blob_storage_service.clone(), pool_service.clone(), wallet_service.clone(), crypto_service.clone()); let crypto_command_executor = CryptoCommandExecutor::new(wallet_service.clone(), crypto_service.clone()); @@ -112,7 +107,6 @@ impl CommandExecutor { let blob_storage_command_executor = BlobStorageCommandExecutor::new(blob_storage_service.clone()); let non_secret_command_executor = NonSecretsCommandExecutor::new(wallet_service.clone()); let payments_command_executor = PaymentsCommandExecutor::new(payments_service.clone(), wallet_service.clone(), crypto_service.clone()); - //let agent_command_executor = AgentCommandExecutor::new(wallet_service.clone(), crypto_service.clone(), agent_service.clone()); loop { match receiver.recv() { @@ -156,10 +150,6 @@ impl CommandExecutor { info!("PaymentsCommand command received"); payments_command_executor.execute(cmd); } -// Ok(Command::Agent(cmd)) => { -// info!("AgentCommand command received"); -// agent_command_executor.execute(cmd); -// } Ok(Command::Exit) => { info!("Exit command received"); break diff --git a/libindy/src/domain/agent/mod.rs b/libindy/src/domain/agent/mod.rs index 2bcc63a91e..e69de29bb2 100644 --- a/libindy/src/domain/agent/mod.rs +++ b/libindy/src/domain/agent/mod.rs @@ -1,86 +0,0 @@ -#[derive(Serialize, Deserialize, Debug, Clone, Eq, PartialEq)] -pub struct AuthRecipient { - pub enc_from : String, - pub e_cek: String, - pub cek_nonce: String, - pub to : String -} - -#[derive(Serialize, Deserialize, Debug, Clone, Eq, PartialEq)] -pub struct AnonRecipient { - pub cek: String, - pub to: String -} - -#[derive(Serialize, Deserialize, Debug, Clone, Eq, PartialEq)] -pub struct AuthAMES { - pub recipients: Vec, - pub ver: String, - pub enc: String, - pub ciphertext: String, - pub iv: String -} - -#[derive(Serialize, Deserialize, Debug, Clone, Eq, PartialEq)] -pub struct AnonAMES { - pub recipients: Vec, - pub ver: String, - pub enc: String, - pub ciphertext: String, - pub iv: String -} - -#[derive(Serialize, Deserialize, Debug, Clone, Eq, PartialEq)] -pub enum AMES { - Auth(AuthAMES), - Anon(AnonAMES) -} - -#[derive(Serialize, Deserialize, Debug, Clone, Eq, PartialEq)] -pub struct JWE { - pub protected: Protected, - pub recipients: Vec, - pub aad: String, - pub iv: String, - pub ciphertext: String, - pub tag: String - -} - -#[derive(Serialize, Deserialize, Debug, Clone, Eq, PartialEq)] -pub struct Recipient { - pub encrypted_key: String, - pub header: Header -} - - -#[derive(Serialize, Deserialize, Debug, Clone, Eq, PartialEq)] -pub struct Header { - pub sender: Option, - pub kid: String -} - -#[derive(Serialize, Deserialize, Debug, Clone, Eq, PartialEq)] -pub struct Protected { - pub enc: String, - pub typ: String, - pub aad_hash_alg: String, - pub cek_enc: String -} - -#[derive(Serialize, Deserialize, Debug, Clone, Eq, PartialEq)] -pub struct FromAddress { - wallet_key: WalletKey -} - -#[derive(Serialize, Deserialize, Debug, Clone, Eq, PartialEq)] -#[serde(untagged)] -pub enum WalletKey { - WalletKeyId(i32, String) //wallet_handle, base58 encoded public key -} - -#[derive(Serialize, Deserialize, Debug, Clone, Eq, PartialEq)] -#[serde(untagged)] -pub enum ToAddress { - public_key(Vec) -} \ No newline at end of file diff --git a/libindy/src/domain/crypto/mod.rs b/libindy/src/domain/crypto/mod.rs index 9d63af2ef9..13bb318baa 100644 --- a/libindy/src/domain/crypto/mod.rs +++ b/libindy/src/domain/crypto/mod.rs @@ -1,3 +1,4 @@ pub mod key; pub mod did; pub mod combo_box; +pub mod pack; diff --git a/libindy/src/domain/crypto/pack.rs b/libindy/src/domain/crypto/pack.rs new file mode 100644 index 0000000000..9560d4d608 --- /dev/null +++ b/libindy/src/domain/crypto/pack.rs @@ -0,0 +1,28 @@ +#[derive(Serialize, Deserialize, Debug, Clone, Eq, PartialEq)] +pub struct JWE { + pub protected: String, + pub iv: String, + pub ciphertext: String, + pub tag: String + +} + +#[derive(Serialize, Deserialize, Debug, Clone, Eq, PartialEq)] +pub struct Recipient { + pub encrypted_key: String, + pub header: Header +} + + +#[derive(Serialize, Deserialize, Debug, Clone, Eq, PartialEq)] +pub struct Header { + pub kid: String +} + +#[derive(Serialize, Deserialize, Debug, Clone, Eq, PartialEq)] +pub struct Protected { + pub enc: String, + pub typ: String, + pub alg: String, + pub recipients: Vec, +} \ No newline at end of file diff --git a/libindy/src/errors/agent.rs b/libindy/src/errors/agent.rs deleted file mode 100644 index 7eb437e51d..0000000000 --- a/libindy/src/errors/agent.rs +++ /dev/null @@ -1,58 +0,0 @@ -use api::ErrorCode; -use errors::ToErrorCode; - -use std::error; -use std::fmt; - -use errors::common::CommonError; - -#[derive(Debug)] -pub enum AgentError { - UnpackError(String), - PackError(String), - CommonError(CommonError) -} - -impl fmt::Display for AgentError { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - match *self { - AgentError::UnpackError(ref description) => write!(f, "Failed while unpacking message: {}", description), - AgentError::PackError(ref description) => write!(f, "Failed while packing message: {}", description), - AgentError::CommonError(ref err) => err.fmt(f) - } - } -} - -impl error::Error for AgentError { - fn description(&self) -> &str { - match *self { - AgentError::UnpackError(ref description) => description, - AgentError::PackError(ref description) => description, - AgentError::CommonError(ref err) => err.description() - } - } - - fn cause(&self) -> Option<&error::Error> { - match *self { - AgentError::UnpackError(_) => None, - AgentError::PackError(_) => None, - AgentError::CommonError(ref err) => Some(err) - } - } -} - -impl ToErrorCode for AgentError { - fn to_error_code(&self) -> ErrorCode { - match *self { - AgentError::UnpackError(_) => ErrorCode::RouteUnpackError, - AgentError::PackError(_) => ErrorCode::RoutePackError, - AgentError::CommonError(ref err) => err.to_error_code() - } - } -} - -impl From for AgentError { - fn from(err: CommonError) -> AgentError { - AgentError::CommonError(err) - } -} \ No newline at end of file diff --git a/libindy/src/errors/indy.rs b/libindy/src/errors/indy.rs index 881e7f69fa..85802bdae6 100644 --- a/libindy/src/errors/indy.rs +++ b/libindy/src/errors/indy.rs @@ -6,7 +6,6 @@ use errors::crypto::CryptoError; use errors::wallet::WalletError; use errors::did::DidError; use errors::payments::PaymentsError; -use errors::agent::AgentError; use api::ErrorCode; use errors::ToErrorCode; @@ -24,7 +23,6 @@ pub enum IndyError { WalletError(WalletError), DidError(DidError), PaymentsError(PaymentsError), - AgentError(AgentError) } impl fmt::Display for IndyError { @@ -38,7 +36,6 @@ impl fmt::Display for IndyError { IndyError::WalletError(ref err) => err.fmt(f), IndyError::DidError(ref err) => err.fmt(f), IndyError::PaymentsError(ref err) => err.fmt(f), - IndyError::AgentError(ref err) => err.fmt(f), } } } @@ -54,7 +51,6 @@ impl error::Error for IndyError { IndyError::WalletError(ref err) => err.description(), IndyError::DidError(ref err) => err.description(), IndyError::PaymentsError(ref err) => err.description(), - IndyError::AgentError(ref err) => err.description(), } } @@ -68,7 +64,6 @@ impl error::Error for IndyError { IndyError::WalletError(ref err) => Some(err), IndyError::DidError(ref err) => Some(err), IndyError::PaymentsError(ref err) => Some(err), - IndyError::AgentError(ref err) => Some(err), } } } @@ -85,7 +80,6 @@ impl ToErrorCode for IndyError { IndyError::WalletError(ref err) => err.to_error_code(), IndyError::DidError(ref err) => err.to_error_code(), IndyError::PaymentsError(ref err) => err.to_error_code(), - IndyError::AgentError(ref err) => err.to_error_code(), } } } @@ -137,9 +131,3 @@ impl From for IndyError { IndyError::PaymentsError(err) } } - -impl From for IndyError { - fn from(err : AgentError) -> IndyError { - IndyError::AgentError(err) - } -} \ No newline at end of file diff --git a/libindy/src/errors/mod.rs b/libindy/src/errors/mod.rs index 7a36013408..ce4fe1fd0b 100644 --- a/libindy/src/errors/mod.rs +++ b/libindy/src/errors/mod.rs @@ -7,7 +7,6 @@ pub mod indy; pub mod wallet; pub mod did; pub mod payments; -pub mod agent; use api::ErrorCode; diff --git a/libindy/src/services/agent/mod.rs b/libindy/src/services/agent/mod.rs deleted file mode 100644 index d8542fd54a..0000000000 --- a/libindy/src/services/agent/mod.rs +++ /dev/null @@ -1,314 +0,0 @@ -use core::result; -use domain::crypto::key::Key; -use domain::agent::*; -use errors::agent::AgentError; -use serde_json; -use services::crypto::CryptoService; -use services::wallet::{RecordOptions, WalletService}; -use std::rc::Rc; -use utils::crypto::base58; -use utils::crypto::base64; -use utils::crypto::xsalsa20; -use utils::crypto::xsalsa20::{create_key, gen_nonce}; - -type Result = result::Result; - -pub struct AgentService {} - -impl AgentService { - pub fn new() -> AgentService { - AgentService {} - } - - pub fn get_recipient_header( - &self, - recipient_verkey: &str, - recipients_list: Vec, - ) -> Result { - for recipient in recipients_list { - if &recipient.header.kid == recipient_verkey { - return Ok(recipient); - } - } - - Err(AgentError::UnpackError(format!( - "Failed to find a matching header" - ))) - } -} - -#[cfg(test)] -pub mod tests { - use super::*; - use domain::agent::{Recipient, Header}; - - #[test] - pub fn test_get_recipient_header_works(){ - let service = AgentService::new(); - - let verkey = "key_of_recipient1".to_string(); - - let recp1 : Recipient = Recipient { - encrypted_key: "unrelevant data".to_string(), - header: Header { - sender : "unrelevant for this test".to_string(), - kid : "key_of_recipient1".to_string(), - } - }; - - let recp2 : Recipient = Recipient { - encrypted_key: "unrelevant data".to_string(), - header: Header { - sender : "unrelevant for this test".to_string(), - kid : "key_of_recipient2".to_string(), - } - }; - - let test_recp_list : Vec = vec![recp1, recp2]; - - let returned_recipient = service.get_recipient_header(&verkey.clone(), test_recp_list).unwrap(); - - assert_eq!(returned_recipient.header.kid, verkey); - } - - #[test] - pub fn test_get_recipient_header_unpack_error(){ - let service = AgentService::new(); - - let verkey = "key_of_recipient1".to_string(); - - let recp1 : Recipient = Recipient { - encrypted_key: "unrelevant data".to_string(), - header: Header { - sender : "unrelevant for this test".to_string(), - kid : "key_does_not_match1".to_string(), - } - }; - - let recp2 : Recipient = Recipient { - encrypted_key: "unrelevant data".to_string(), - header: Header { - sender : "unrelevant for this test".to_string(), - kid : "key_does_not_match2".to_string(), - } - }; - - let test_recp_list : Vec = vec![recp1, recp2]; - - let returned_recipient = service.get_recipient_header(&verkey.clone(), test_recp_list); - - assert!(returned_recipient.is_err()); - } -} - -//pub mod tests { -// -// // TODO Fix texts so only one wallet is used to speed up tests -// //unit tests -// -// -// /* component test useful to identify if unpack is breaking or if pack is breaking. If unpack is -// * breaking both this test and the tests below will fail. If only pack is breaking, only this test -// * will fail. -// */ -// -// // Integration tests -// -// pub fn test_single_anon_pack_message_and_unpack_msg_success() { -// _cleanup(); -// //setup generic data to test -// let expected_message = "Hello World"; -// -// //setup route_service -// let rs: Rc = Rc::new(AgentService::new()); -// let cs: Rc = Rc::new(CryptoService::new()); -// let ws: Rc = Rc::new(WalletService::new()); -// -// //setup wallets -// let (recv_wallet_handle, _, recv_key) = _setup_recv_wallet1(ws.clone(), cs.clone()); -// -// //setup recv_keys list -// let recv_verkey: &str = recv_key.verkey.as_ref(); -// let recv_keys: Vec<&str> = vec![recv_verkey]; -// -// //pack then unpack message -// let packed_msg = rs -// .anon_pack_msg(expected_message, recv_keys, cs.clone()) -// .unwrap(); -// -// let (message, _) = rs -// .unpack_msg( -// &packed_msg, -// &recv_key.verkey, -// recv_wallet_handle, -// ws.clone(), -// cs.clone(), -// ) -// .unwrap(); -// -// //verify same plaintext goes in and comes out -// assert_eq!(expected_message.to_string(), message); -// } -// -// -// pub fn test_single_auth_pack_msg_and_unpack_msg_success() { -// _cleanup(); -// //setup generic data to test -// let expected_message = "Hello World"; -// -// //setup route_service -// let rs: Rc = Rc::new(AgentService::new()); -// let cs: Rc = Rc::new(CryptoService::new()); -// let ws: Rc = Rc::new(WalletService::new()); -// -// //setup wallets -// let (recv_wallet_handle, _, recv_key) = _setup_recv_wallet1(ws.clone(), cs.clone()); -// let (send_wallet_handle, _, send_key) = _setup_send_wallet(ws.clone(), cs.clone()); -// -// //setup recv_keys list -// let recv_verkey: &str = recv_key.verkey.as_ref(); -// let recv_keys: Vec<&str> = vec![recv_verkey]; -// -// //pack then unpack message -// let packed_msg = rs -// .auth_pack_msg( -// expected_message, -// recv_keys, -// &send_key.verkey, -// send_wallet_handle, -// ws.clone(), -// cs.clone(), -// ) -// .unwrap(); -// -// let (message, sender_vk) = rs -// .unpack_msg( -// &packed_msg, -// &recv_key.verkey, -// recv_wallet_handle, -// ws.clone(), -// cs.clone(), -// ) -// .unwrap(); -// -// //verify same plaintext goes in and comes out -// assert_eq!(expected_message.to_string(), message); -// assert_eq!(sender_vk, send_key.verkey); -// } -// -// -// pub fn test_pack_and_unpack_msg_success_multi_anoncrypt() { -// _cleanup(); -// //setup generic data to test -// let plaintext = "Hello World"; -// -// //setup route_service -// let rs: Rc = Rc::new(AgentService::new()); -// let cs: Rc = Rc::new(CryptoService::new()); -// let ws: Rc = Rc::new(WalletService::new()); -// -// //setup recv_keys to use with pack_msg -// let (_, recv_key1_before_wallet_setup) = _recv_did1(cs.clone()); -// let (_, recv_key2_before_wallet_setup) = _recv_did2(cs.clone()); -// let recv_keys = vec![ -// recv_key1_before_wallet_setup.verkey.as_ref(), -// recv_key2_before_wallet_setup.verkey.as_ref(), -// ]; -// -// //setup send wallet then pack message -// let (send_wallet_handle, _, _) = _setup_send_wallet(ws.clone(), cs.clone()); -// let packed_msg = rs.anon_pack_msg(plaintext, recv_keys, cs.clone()).unwrap(); -// let _result1 = ws.close_wallet(send_wallet_handle); -// -// //setup recv_wallet1 and unpack message then verify plaintext -// let (recv_wallet_handle1, _, recv_key1) = _setup_recv_wallet1(ws.clone(), cs.clone()); -// let (unpacked_msg1, _) = rs -// .unpack_msg( -// &packed_msg, -// &recv_key1.verkey, -// recv_wallet_handle1, -// ws.clone(), -// cs.clone(), -// ) -// .unwrap(); -// -// //test first recipient -// assert_eq!(plaintext.to_string(), unpacked_msg1); -// let _result2 = ws.close_wallet(recv_wallet_handle1); -// -// //setup recv_wallet2 and unpack message then verify plaintext -// let (recv_wallet_handle2, _, recv_key2) = _setup_recv_wallet2(ws.clone(), cs.clone()); -// let (unpacked_msg2, _) = rs -// .unpack_msg( -// &packed_msg, -// &recv_key2.verkey, -// recv_wallet_handle2, -// ws.clone(), -// cs.clone(), -// ) -// .unwrap(); -// -// //test second recipient -// assert_eq!(plaintext, &unpacked_msg2); -// let _result2 = ws.close_wallet(recv_wallet_handle2); -// } -// -// -// pub fn test_pack_and_unpack_msg_success_multi_authcrypt() { -// _cleanup(); -// //setup generic data to test -// let plaintext = "Hello World"; -// -// //setup route_service -// let rs: Rc = Rc::new(AgentService::new()); -// let cs: Rc = Rc::new(CryptoService::new()); -// let ws: Rc = Rc::new(WalletService::new()); -// -// //setup recv_keys to use with pack_msg -// let (_, recv_key1_before_wallet_setup) = _recv_did1(cs.clone()); -// let (_, recv_key2_before_wallet_setup) = _recv_did2(cs.clone()); -// let recv_keys = vec![ -// recv_key1_before_wallet_setup.verkey.as_ref(), -// recv_key2_before_wallet_setup.verkey.as_ref(), -// ]; -// -// //setup send wallet then pack message -// let (send_wallet_handle, _, expected_send_key) = _setup_send_wallet(ws.clone(), cs.clone()); -// let packed_msg = rs.auth_pack_msg(plaintext, recv_keys, &expected_send_key.verkey, send_wallet_handle, ws.clone(), cs.clone()).unwrap(); -// let _result1 = ws.close_wallet(send_wallet_handle); -// -// //setup recv_wallet1 and unpack message then verify plaintext -// let (recv_wallet_handle1, _, recv_key1) = _setup_recv_wallet1(ws.clone(), cs.clone()); -// let (unpacked_msg1, send_vk_1) = rs -// .unpack_msg( -// &packed_msg, -// &recv_key1.verkey, -// recv_wallet_handle1, -// ws.clone(), -// cs.clone(), -// ) -// .unwrap(); -// -// //test first recipient -// assert_eq!(plaintext.to_string(), unpacked_msg1); -// assert_eq!(&expected_send_key.verkey, &send_vk_1); -// let _result2 = ws.close_wallet(recv_wallet_handle1); -// -// //setup recv_wallet2 and unpack message then verify plaintext -// let (recv_wallet_handle2, _, recv_key2) = _setup_recv_wallet2(ws.clone(), cs.clone()); -// let (unpacked_msg2, send_vk_2) = rs -// .unpack_msg( -// &packed_msg, -// &recv_key2.verkey, -// recv_wallet_handle2, -// ws.clone(), -// cs.clone(), -// ) -// .unwrap(); -// -// //test second recipient -// assert_eq!(plaintext, &unpacked_msg2); -// assert_eq!(&expected_send_key.verkey, &send_vk_2); -// let _result2 = ws.close_wallet(recv_wallet_handle2); -// } -//} \ No newline at end of file diff --git a/libindy/src/services/crypto/mod.rs b/libindy/src/services/crypto/mod.rs index 331229a9db..06775cbbe4 100644 --- a/libindy/src/services/crypto/mod.rs +++ b/libindy/src/services/crypto/mod.rs @@ -7,18 +7,16 @@ use self::hex::FromHex; use errors::common::CommonError; use errors::crypto::CryptoError; -use errors::agent::AgentError; use domain::crypto::key::{Key, KeyInfo}; use domain::crypto::did::{Did, MyDidInfo, TheirDidInfo, TheirDid}; use domain::crypto::combo_box::ComboBox; -use domain::agent::Recipient; use utils::crypto::base58; use utils::crypto::base64; use utils::crypto::verkey_builder::build_full_verkey; use utils::crypto::ed25519_sign; use utils::crypto::ed25519_box; use utils::crypto::chacha20poly1305_ietf; -use utils::crypto::chacha20poly1305_ietf::{gen_key, gen_nonce_and_encrypt_detached, decrypt_detached}; +use utils::crypto::chacha20poly1305_ietf::{ gen_nonce_and_encrypt_detached}; use std::collections::HashMap; use std::str; @@ -452,13 +450,14 @@ impl CryptoService { } pub fn encrypt_plaintext(&self, - plaintext: &str, - aad: &[u8], + plaintext: Vec, + aad: &str, sym_key: &chacha20poly1305_ietf::Key) - -> (String, String, String) { ; + -> (String, String, String) { //encrypt message with aad - let (ciphertext, iv, tag) = gen_nonce_and_encrypt_detached(plaintext.as_bytes(), aad, &sym_key); + let (ciphertext, iv, tag) = gen_nonce_and_encrypt_detached( + plaintext.as_slice(), aad.as_bytes(), &sym_key); //base64 url encode data let iv_encoded = base64::encode(&iv[..]); @@ -472,9 +471,9 @@ impl CryptoService { pub fn decrypt_ciphertext( &self, ciphertext: &str, + aad: &str, iv: &str, tag: &str, - aad: &str, sym_key: &chacha20poly1305_ietf::Key, ) -> Result { @@ -510,20 +509,13 @@ impl CryptoService { ) })?; - //convert aad to byte array type - let aad_as_vec = base64::decode(aad) - .map_err(|err|CryptoError::CommonError( - CommonError::InvalidStructure(format!("Failed to decode aad {}", err)) - ))?; - let aad_as_bytes= aad_as_vec.as_slice(); - //decrypt message let plaintext_bytes = chacha20poly1305_ietf::decrypt_detached(ciphertext_as_bytes, sym_key, &nonce, &tag, - Some(aad_as_bytes)) + Some(aad.as_bytes())) .map_err(|err| { CryptoError::UnknownCryptoError(format!("Failed to decrypt ciphertext {}", err)) })?; @@ -543,6 +535,7 @@ mod tests { use super::*; use domain::crypto::did::MyDidInfo; use utils::crypto::randombytes::randombytes; + use utils::crypto::chacha20poly1305_ietf::gen_key; #[test] fn create_my_did_with_works_for_empty_info() { @@ -817,119 +810,121 @@ mod tests { #[test] pub fn test_encrypt_plaintext_and_decrypt_ciphertext_works() { let service: CryptoService = CryptoService::new(); - let plaintext : &str = "Message to encrypt"; - let aad = randombytes(100); + let plaintext = "Hello World".as_bytes().to_vec(); + let aad = "Random authenticated additional data"; let sym_key = gen_key(); let (expected_ciphertext, iv_encoded, tag) = service - .encrypt_plaintext(plaintext, aad.as_slice(), &sym_key); + .encrypt_plaintext(plaintext.clone(), aad, &sym_key); let expected_plaintext = service - .decrypt_ciphertext(&expected_ciphertext, &iv_encoded, &tag, &base64::encode(aad.as_slice()), &sym_key).unwrap(); - assert_eq!(expected_plaintext, plaintext); + .decrypt_ciphertext(&expected_ciphertext, aad, &iv_encoded, &tag, &sym_key).unwrap(); + + assert_eq!(expected_plaintext.as_bytes().to_vec(), plaintext); } #[test] pub fn test_encrypt_plaintext_decrypt_ciphertext_empty_string_works() { let service: CryptoService = CryptoService::new(); - let plaintext : &str = "Hello World"; - let aad = randombytes(100); + let plaintext = "".as_bytes().to_vec(); + let aad = "Random authenticated additional data"; let sym_key = gen_key(); let (expected_ciphertext, iv_encoded, tag) = service - .encrypt_plaintext(plaintext, aad.as_slice(), &sym_key); + .encrypt_plaintext(plaintext.clone(), aad, &sym_key); let expected_plaintext = service - .decrypt_ciphertext(&expected_ciphertext, &iv_encoded, &tag, &base64::encode(aad.as_slice()), &sym_key).unwrap(); - assert_eq!(expected_plaintext, plaintext); + .decrypt_ciphertext(&expected_ciphertext, aad, &iv_encoded, &tag, &sym_key).unwrap(); + + assert_eq!(expected_plaintext.as_bytes().to_vec(), plaintext); } #[test] pub fn test_encrypt_plaintext_decrypt_ciphertext_bad_iv_fails() { let service: CryptoService = CryptoService::new(); - let plaintext : &str = ""; - let aad = randombytes(100); + let plaintext = "Hello World".as_bytes().to_vec(); + let aad = "Random authenticated additional data"; let sym_key = gen_key(); let (expected_ciphertext, _, tag) = service - .encrypt_plaintext(plaintext, aad.as_slice(), &sym_key); + .encrypt_plaintext(plaintext, aad, &sym_key); //convert values to base64 encoded strings let bad_iv_input = "invalid_iv"; let expected_error = service - .decrypt_ciphertext(&expected_ciphertext, bad_iv_input, &tag, &base64::encode(aad.as_slice()), &sym_key); + .decrypt_ciphertext(&expected_ciphertext, bad_iv_input, &tag, aad, &sym_key); assert!(expected_error.is_err()); } #[test] pub fn test_encrypt_plaintext_decrypt_ciphertext_bad_ciphertext_fails() { let service: CryptoService = CryptoService::new(); - let plaintext : &str = "Hello World"; - let aad = randombytes(100); + let plaintext = "Hello World".as_bytes().to_vec(); + let aad = "Random authenticated additional data"; let sym_key = gen_key(); let (_, iv_encoded, tag) = service - .encrypt_plaintext(plaintext, aad.as_slice(), &sym_key); + .encrypt_plaintext(plaintext, aad, &sym_key); let bad_ciphertext= base64::encode("bad_ciphertext".as_bytes()); let expected_error = service - .decrypt_ciphertext(&bad_ciphertext, &iv_encoded, &tag, &base64::encode(aad.as_slice()), &sym_key); + .decrypt_ciphertext(&bad_ciphertext, &iv_encoded, &tag, aad, &sym_key); assert!(expected_error.is_err()); } #[test] pub fn test_encrypt_plaintext_and_decrypt_ciphertext_wrong_sym_key_fails() { let service: CryptoService = CryptoService::new(); - let plaintext : &str = "Hello World"; - let aad = randombytes(100); + let plaintext = "Hello World".as_bytes().to_vec(); + let aad = "Random authenticated additional data"; let sym_key = chacha20poly1305_ietf::gen_key(); let (expected_ciphertext, iv_encoded, tag) = service - .encrypt_plaintext(plaintext, aad.as_slice(), &sym_key); + .encrypt_plaintext(plaintext, aad, &sym_key); let bad_sym_key= gen_key(); let expected_error = service - .decrypt_ciphertext(&expected_ciphertext, &iv_encoded, &tag, &base64::encode(aad.as_slice()), &bad_sym_key); + .decrypt_ciphertext(&expected_ciphertext, &iv_encoded, &tag, aad, &bad_sym_key); assert!(expected_error.is_err()); } #[test] pub fn test_encrypt_plaintext_and_decrypt_ciphertext_bad_tag_fails() { let service: CryptoService = CryptoService::new(); - let plaintext : &str = "Hello World"; - let aad = randombytes(100); + let plaintext = "Hello World".as_bytes().to_vec(); + let aad = "Random authenticated additional data"; let sym_key = gen_key(); let (expected_ciphertext, iv_encoded, tag) = service - .encrypt_plaintext(plaintext, aad.as_slice(), &sym_key); + .encrypt_plaintext(plaintext, aad, &sym_key); let bad_tag = "bad_tag".to_string(); let expected_error = service - .decrypt_ciphertext(&expected_ciphertext, &iv_encoded, &bad_tag, &base64::encode(aad.as_slice()), &sym_key); + .decrypt_ciphertext(&expected_ciphertext, &iv_encoded, &bad_tag, aad, &sym_key); assert!(expected_error.is_err()); } #[test] pub fn test_encrypt_plaintext_and_decrypt_ciphertext_bad_aad_fails() { let service: CryptoService = CryptoService::new(); - let plaintext : &str = "Hello World"; - let aad = randombytes(100); + let plaintext = "Hello World".as_bytes().to_vec(); + let aad = "Random authenticated additional data"; let sym_key = gen_key(); let (expected_ciphertext, iv_encoded, tag) = service - .encrypt_plaintext(plaintext, aad.as_slice(), &sym_key); + .encrypt_plaintext(plaintext, aad, &sym_key); - let bad_aad = randombytes(100); + let bad_aad = "bad aad"; let expected_error = service - .decrypt_ciphertext(&expected_ciphertext, &iv_encoded, &tag, &base64::encode(bad_aad.as_slice()), &sym_key); + .decrypt_ciphertext(&expected_ciphertext, &iv_encoded, &tag, bad_aad, &sym_key); assert!(expected_error.is_err()); } } \ No newline at end of file diff --git a/libindy/src/services/mod.rs b/libindy/src/services/mod.rs index 8359d4642d..ea1ae0535f 100644 --- a/libindy/src/services/mod.rs +++ b/libindy/src/services/mod.rs @@ -5,4 +5,3 @@ pub mod ledger; pub mod payments; pub mod pool; pub mod wallet; -pub mod agent; From c86bf046c51cd9ff6ad478a21f02880a3fd5f16b Mon Sep 17 00:00:00 2001 From: Kyle Den Hartog Date: Fri, 21 Dec 2018 18:05:12 -0700 Subject: [PATCH 04/17] build properly - pack_message test throws segfault Signed-off-by: Kyle Den Hartog --- libindy/include/indy_crypto.h | 117 +++++++++++++++++++++++++++ libindy/src/api/crypto.rs | 116 +++++++++++++++----------- libindy/src/commands/crypto.rs | 102 ++++++++++++----------- libindy/src/commands/route.rs | 16 ++-- libindy/src/domain/crypto/pack.rs | 7 ++ libindy/src/services/crypto/mod.rs | 57 +++++++------ libindy/tests/crypto.rs | 23 ++++++ libindy/tests/utils/crypto.rs | 8 ++ wrappers/rust/indy-sys/src/crypto.rs | 16 ++++ wrappers/rust/src/crypto.rs | 66 +++++++++++++++ 10 files changed, 397 insertions(+), 131 deletions(-) diff --git a/libindy/include/indy_crypto.h b/libindy/include/indy_crypto.h index 8ef0a525b8..5b6d51794b 100644 --- a/libindy/include/indy_crypto.h +++ b/libindy/include/indy_crypto.h @@ -317,6 +317,123 @@ extern "C" { indy_u32_t decrypted_msg_len) ); + + /// Packs a message + /// + /// Note to use DID keys with this function you can call indy_key_for_did to get key id (verkey) + /// for specific DID. + /// + /// #Params + /// command_handle: command handle to map callback to user context. + /// message: a pointer to the first byte of the message to be packed + /// message_len: the length of the message + /// receivers: a string in the format of a json list which will contain the list of receiver's keys + /// the message is being encrypted for. + /// Example: + /// "[, ]" + /// sender: the sender's verkey as a string When "" is used in this parameter, anoncrypt is used + /// cb: Callback that takes command result as parameter. + /// + /// #Returns + /// a JWE using authcrypt alg is defined below: + /// { + /// "protected": "b64URLencoded({ + /// "enc": "xsalsa20poly1305", + /// "typ": "JWM/1.0", + /// "alg": "authcrypt", + /// "recipients": [ + /// { + /// "encrypted_key": anoncrypt(encrypted_cek|sender_vk|nonce) + /// "header": { + /// "kid": "b64URLencode(ver_key)" + /// } + /// }, + /// ], + /// })" + /// "iv": , + /// "ciphertext": , + /// "tag": + /// } + /// + /// Alternative example in using anoncrypt alg is defined below: + /// { + /// "protected": "b64URLencode({ + /// "enc": "xsalsa20poly1305", + /// "typ": "JWM/1.0", + /// "alg": "anoncrypt", + /// "recipients": [ + /// { + /// "encrypted_key": , + /// "header": { + /// "kid": "b64URLencode(ver_key)" + /// } + /// }, + /// ], + /// })" + /// "iv": , + /// "ciphertext": , + /// "tag": + /// } + /// + /// + /// #Errors + /// Common* + /// Wallet* + /// Ledger* + /// Crypto* + extern indy_error_t indy_crypto_pack_message(indy_handle_t command_handle, + indy_handle_t wallet_handle, + const indy_u8_t* message, + indy_u32_t message_len, + const char * receiver_keys, + const char * sender, + + void (*cb)(indy_handle_t command_handle_, + indy_error_t err, + const indy_u8_t* jwe_msg_raw, + indy_u32_t jwe_msg_len) + ); + + + /// Unpacks a message packed using indy_pack_message which follows the wire message format HIPE + /// + /// + /// #Params + /// command_handle: command handle to map callback to user context. + /// jwe_data: a pointer to the first byte of the JWE to be unpacked + /// jwe_len: the length of the JWE message in bytes + /// cb: Callback that takes command result as parameter. + /// + /// #Returns + /// if authcrypt was used to pack the message returns this json structure: + /// { + /// message: , + /// sender_verkey: + /// } + /// + /// OR + /// + /// if anoncrypt was used to pack the message returns this json structure: + /// { + /// message: , + /// } + /// + /// + /// #Errors + /// Common* + /// Wallet* + /// Ledger* + /// Crypto* + extern indy_error_t indy_crypto_unpack_message(indy_handle_t command_handle, + indy_handle_t wallet_handle, + const indy_u8_t* jwe_msg, + indy_u32_t jwe_len, + + void (*cb)(indy_handle_t command_handle_, + indy_error_t err, + const indy_u8_t* res_json_raw, + indy_u32_t res_json_len) + ); #ifdef __cplusplus } #endif diff --git a/libindy/src/api/crypto.rs b/libindy/src/api/crypto.rs index fce1da2c62..7ca8caa94d 100644 --- a/libindy/src/api/crypto.rs +++ b/libindy/src/api/crypto.rs @@ -566,26 +566,26 @@ pub extern fn indy_crypto_anon_decrypt(command_handle: IndyHandle, /// /// #Params /// command_handle: command handle to map callback to user context. -/// message: the message which is going to be packed up +/// message: a pointer to the first byte of the message to be packed +/// message_len: the length of the message /// receivers: a string in the format of a json list which will contain the list of receiver's keys /// the message is being encrypted for. /// Example: /// "[, ]" -/// sender: a string in the form +/// sender: the sender's verkey as a string When "" is used in this parameter, anoncrypt is used /// cb: Callback that takes command result as parameter. /// /// #Returns -/// a JWE in using authcrypt alg is defined below: +/// a JWE using authcrypt alg is defined below: /// { -/// "protected": "b64URLencode({ +/// "protected": "b64URLencoded({ /// "enc": "xsalsa20poly1305", /// "typ": "JWM/1.0", /// "alg": "authcrypt", /// "recipients": [ /// { -/// "encrypted_key": , +/// "encrypted_key": anoncrypt(encrypted_cek|sender_vk|nonce) /// "header": { -/// "sender": , /// "kid": "b64URLencode(ver_key)" /// } /// }, @@ -604,7 +604,7 @@ pub extern fn indy_crypto_anon_decrypt(command_handle: IndyHandle, /// "alg": "anoncrypt", /// "recipients": [ /// { -/// "encrypted_key": , +/// "encrypted_key": , /// "header": { /// "kid": "b64URLencode(ver_key)" /// } @@ -622,45 +622,37 @@ pub extern fn indy_crypto_anon_decrypt(command_handle: IndyHandle, /// Wallet* /// Ledger* /// Crypto* -/// Agent* - - #[no_mangle] pub fn indy_pack_message( command_handle: i32, wallet_handle: i32, - msg_data: *const u8, - msg_len: u32, + message: *const u8, + message_len: u32, receiver_keys: *const c_char, sender: *const c_char, - cb: Option, + cb: Option, ) -> ErrorCode { - trace!("indy_pack_message: >>> msg_data: {:?}, msg_len: {:?}, receiver_keys: {:?}, sender: {:?}", - msg_data, msg_len, receiver_keys, sender); + trace!("indy_pack_message: >>> wallet_handle: {:?}, message: {:?}, message_len {:?},\ + receiver_keys: {:?}, sender: {:?}", wallet_handle, message, message_len, receiver_keys, sender); - check_useful_c_byte_array!(msg_data, msg_len, ErrorCode::CommonInvalidParam2, ErrorCode::CommonInvalidParam3); + check_useful_c_byte_array!(message, message_len, ErrorCode::CommonInvalidParam2, ErrorCode::CommonInvalidParam3); check_useful_c_str!(receiver_keys, ErrorCode::CommonInvalidParam4); check_useful_opt_c_str!(sender, ErrorCode::CommonInvalidParam5); check_useful_c_callback!(cb, ErrorCode::CommonInvalidParam6); - trace!("indy_pack_message: entities >>> msg_data: {:?}, msg_len: {:?}, receiver_keys: {:?}, sender: {:?}", - msg_data, msg_len, receiver_keys, sender); + trace!("indy_pack_message: entities >>> wallet_handle: {:?}, message: {:?}, message_len {:?},\ + receiver_keys: {:?}, sender: {:?}", wallet_handle, message, message_len, receiver_keys, sender); let result = CommandExecutor::instance().send(Command::Crypto(CryptoCommand::PackMessage( - msg_data, + message, receiver_keys, sender, wallet_handle, Box::new(move |result| { - let (err, jwe) = result_to_err_code_1!(result, String::new()); - trace!( - "indy_auth_pack_message: cb command_handle: {:?}, err: {:?}, jwe: {:?}", - command_handle, - err, - jwe - ); - let jwe = ctypes::string_to_cstring(jwe); - cb(command_handle, err, jwe.as_ptr()) + let (err, jwe) = result_to_err_code_1!(result, Vec::new()); + trace!("indy_auth_pack_message: jwe: {:?}", jwe); + let (jwe_data, jwe_len) = ctypes::vec_to_pointer(&jwe); + cb(command_handle, err, jwe_data, jwe_len) }), ))); @@ -671,52 +663,78 @@ pub fn indy_pack_message( res } -//update function to return key used + +/// Unpacks a message packed using indy_pack_message which follows the wire message format +/// +/// +/// #Params +/// command_handle: command handle to map callback to user context. +/// jwe_data: a pointer to the first byte of the JWE to be unpacked +/// jwe_len: the length of the JWE message in bytes +/// cb: Callback that takes command result as parameter. +/// +/// #Returns +/// if authcrypt was used to pack the message returns this json structure: +/// { +/// message: , +/// sender_verkey: +/// } +/// +/// OR +/// +/// if anoncrypt was used to pack the message returns this json structure: +/// { +/// message: , +/// } +/// +/// +/// #Errors +/// Common* +/// Wallet* +/// Ledger* +/// Crypto* #[no_mangle] pub fn indy_unpack_message( command_handle: i32, wallet_handle: i32, - jwe: *const c_char, + jwe_data: *const u8, + jwe_len: u32, cb: Option< extern "C" fn( xcommand_handle: i32, err: ErrorCode, - plaintext: *const c_char, - sender_vk: *const c_char, + res_json_data : *const u8, + res_json_len : u32 ), >, ) -> ErrorCode { trace!( - "indy_unpack_message: >>> wallet_handle: {:?}, jwe: {:?}", + "indy_unpack_message: >>> wallet_handle: {:?}, jwe_data: {:?}, jwe_len {:?}", wallet_handle, - jwe + jwe_data, + jwe_len ); - check_useful_c_str!(jwe, ErrorCode::CommonInvalidParam3); + check_useful_c_byte_array!(jwe_data, jwe_len, ErrorCode::CommonInvalidParam2, ErrorCode::CommonInvalidParam3); check_useful_c_callback!(cb, ErrorCode::CommonInvalidParam4); trace!( - "indy_unpack_message: entities >>> wallet_handle: {:?}, jwe: {:?}", + "indy_unpack_message: entities >>> wallet_handle: {:?}, jwe_data: {:?}, jwe_len {:?}", wallet_handle, - jwe + jwe_data, + jwe_len ); let result = CommandExecutor::instance().send(Command::Crypto(CryptoCommand::UnpackMessage( - jwe, + jwe_data, wallet_handle, Box::new(move |result| { - let (err, plaintext, sender_vk) = - result_to_err_code_2!(result, String::new(), String::new()); - trace!( - "indy_unpack_message: cb command_handle: {:?}, err: {:?}, plaintext: {:?}, sender_vk: {:?}", - command_handle, - err, - plaintext, - sender_vk + let (err, res_json) = result_to_err_code_1!(result, Vec::new()); + trace!("indy_unpack_message: cb command_handle: {:?}, err: {:?}, res_json: {:?}", + command_handle, err, res_json ); - let plaintext = ctypes::string_to_cstring(plaintext); - let sender_vk = ctypes::string_to_cstring(sender_vk); - cb(command_handle, err, plaintext.as_ptr(), sender_vk.as_ptr()) + let (res_json_data, res_json_len) = ctypes::vec_to_pointer(&res_json); + cb(command_handle, err, res_json_data, res_json_len) }), ))); diff --git a/libindy/src/commands/crypto.rs b/libindy/src/commands/crypto.rs index a56b8f4218..b5d83eef14 100644 --- a/libindy/src/commands/crypto.rs +++ b/libindy/src/commands/crypto.rs @@ -67,14 +67,14 @@ pub enum CryptoCommand { PackMessage( Vec, // plaintext message String, // list of receiver's keys - Option, // senders object + Option, // senders verkey i32, //wallet handle - Box) + Send>, + Box>) + Send>, ), UnpackMessage( - String, // JWE + Vec, // JWE i32, // wallet handle - Box) + Send>, + Box>) + Send>, ), } @@ -131,13 +131,13 @@ impl CryptoCommandExecutor { info!("AnonymousDecrypt command received"); cb(self.anonymous_decrypt(wallet_handle, &my_vk, &encrypted_msg)); } - CryptoCommand::PackMessage(message, receivers, sender, wallet_handle, cb) => { + CryptoCommand::PackMessage(message, receivers, sender_vk, wallet_handle, cb) => { info!("PackMessage command received"); - cb(self.pack_msg(message, &receivers, sender, wallet_handle)); + cb(self.pack_msg(message, &receivers, sender_vk, wallet_handle)); } - CryptoCommand::UnpackMessage(jwe, wallet_handle, cb) => { + CryptoCommand::UnpackMessage(jwe_json, wallet_handle, cb) => { info!("UnpackMessage command received"); - cb(self.unpack_msg(&jwe, wallet_handle)); + cb(self.unpack_msg(jwe_json, wallet_handle)); } }; } @@ -290,10 +290,10 @@ impl CryptoCommandExecutor { receivers: &str, sender_vk: Option, wallet_handle: i32 - ) -> Result { + ) -> Result> { //generate symmetrical key - let sym_key = chacha20poly1305_ietf::gen_key(); + let cek = chacha20poly1305_ietf::gen_key(); //list of ceks used to construct JWE later let mut encrypted_recipients_struct: Vec = vec![]; @@ -319,15 +319,15 @@ impl CryptoCommandExecutor { ) )?; - //encrypt sym_key for recipient + //encrypt cek for recipient for their_vk in receiver_list { - let enc_sym_key = self.crypto_service - .authenticated_encrypt(&my_key, &their_vk, &sym_key[..])?; + let enc_cek = self.crypto_service + .authenticated_encrypt(&my_key, &their_vk, &cek[..])?; //create recipient struct and push to encrypted list encrypted_recipients_struct.push( Recipient { - encrypted_key: base64::encode(enc_sym_key.as_slice()), + encrypted_key: base64::encode(enc_cek.as_slice()), header: Header { kid: base64::encode(&their_vk.as_bytes()) } @@ -353,7 +353,7 @@ impl CryptoCommandExecutor { // encrypt ciphertext and integrity protect "protected" field let (iv, ciphertext, tag) = self.crypto_service - .encrypt_plaintext(message, &base64_protected, &sym_key); + .encrypt_plaintext(message, &base64_protected, &cek); //construct JWE struct let jwe_struct = JWE { @@ -364,7 +364,7 @@ impl CryptoCommandExecutor { }; //convert JWE struct to a string and return - serde_json::to_string(&jwe_struct) + serde_json::to_vec(&jwe_struct) .map_err(|err| IndyError::CryptoError( CryptoError::CommonError( @@ -391,17 +391,17 @@ impl CryptoCommandExecutor { ) )?; - //encrypt sym_key for recipient + //encrypt cek for recipient for their_vk in receiver_list { //encrypt sender verkey - let enc_sym_key = self.crypto_service - .crypto_box_seal(&their_vk, &sym_key[..])?; + let enc_cek = self.crypto_service + .crypto_box_seal(&their_vk, &cek[..])?; //create recipient struct and push to encrypted list encrypted_recipients_struct.push( Recipient { - encrypted_key: base64::encode(enc_sym_key.as_slice()), + encrypted_key: base64::encode(enc_cek.as_slice()), header: Header { kid: base64::encode(&their_vk.as_bytes()) } @@ -428,7 +428,7 @@ impl CryptoCommandExecutor { // encrypt ciphertext let (iv, ciphertext, tag) = self.crypto_service - .encrypt_plaintext(message, &protected_encoded, &sym_key); + .encrypt_plaintext(message, &protected_encoded, &cek); //serialize JWE struct let jwe_struct = JWE { @@ -439,7 +439,7 @@ impl CryptoCommandExecutor { }; //return JWE_json - return serde_json::to_string(&jwe_struct) + serde_json::to_vec(&jwe_struct) .map_err(|err| IndyError::CryptoError( CryptoError::CommonError( @@ -455,12 +455,12 @@ impl CryptoCommandExecutor { pub fn unpack_msg( &self, - jwe_json: &str, + jwe_json: Vec, wallet_handle: i32 - ) -> Result<(String, String)> { + ) -> Result> { //serialize JWE to struct - let jwe_struct : JWE = serde_json::from_str(jwe_json) + let jwe_struct : JWE = serde_json::from_slice(jwe_json.as_slice()) .map_err(|err| IndyError::CryptoError( CryptoError::CommonError( @@ -503,85 +503,97 @@ impl CryptoCommandExecutor { if key_in_wallet_result.is_ok() { //TODO change to move this to a separate function and return recipient rather than - // putting logic inside the for loop for loops have no way to return values in rust + // putting logic inside the for loop. For loops have no way to return values in rust. //decode encrypted_key let encrypted_key_vec = base64::decode(&recipient.encrypted_key)?; - //get sym_key and sender data - let (sender, sym_key) = match protected_struct.alg.as_ref() { + //get cek and sender data + let (sender_verkey, cek) = match protected_struct.alg.as_ref() { "Authcrypt" => { //get my key based on kid let my_key = self.wallet_service .get_indy_object(wallet_handle, &recipient.header.kid, &RecordOptions::id_value())?; - //decrypt sym_key - let (sender_vk , sym_key_as_vec) = self.crypto_service + //decrypt cek + let (sender_vk , cek_as_vec) = self.crypto_service .authenticated_decrypt(&my_key, encrypted_key_vec.as_slice())?; //convert to chacha Key struct - let sym_key : chacha20poly1305_ietf::Key = chacha20poly1305_ietf::Key::from_slice(&sym_key_as_vec[..]) + let cek : chacha20poly1305_ietf::Key = chacha20poly1305_ietf::Key::from_slice(&cek_as_vec[..]) .map_err(|err| IndyError::CryptoError( CryptoError::CommonError( CommonError::InvalidStructure( - format!("Failed to decrypt sym_key {}", err) + format!("Failed to decrypt cek {}", err) ) ) ) )?; - Ok((sender_vk, sym_key)) - }, + Ok((Some(sender_vk), cek)) + }, //close authcrypt option "Anoncrypt" => { //get my private key let my_key = self.wallet_service .get_indy_object(wallet_handle, &recipient.header.kid, &RecordOptions::id_value())?; - //decrypt sym_key - let sym_key_as_vec = self.crypto_service + //decrypt cek + let cek_as_vec = self.crypto_service .crypto_box_seal_open(&my_key, encrypted_key_vec.as_slice())?; //convert to chacha Key struct - let sym_key : chacha20poly1305_ietf::Key = chacha20poly1305_ietf::Key::from_slice(&sym_key_as_vec[..]) + let cek : chacha20poly1305_ietf::Key = chacha20poly1305_ietf::Key::from_slice(&cek_as_vec[..]) .map_err(|err| IndyError::CryptoError( CryptoError::CommonError( CommonError::InvalidStructure( - format!("Failed to decrypt sym_key {}", err) + format!("Failed to decrypt cek {}", err) ) ) ) )?; - Ok((String::from(""), sym_key )) - }, + Ok((None, cek )) + }, //close Anoncrypt option _ => Err( IndyError::CryptoError( CryptoError::CommonError( CommonError::InvalidStructure( - format!("Failed to deserialize sym_key encryption alg") + format!("Failed to deserialize cek encryption alg") ) ) ) ) - }?; + }?; //close cek and sender_data match statement let message = self.crypto_service .decrypt_ciphertext(&jwe_struct.ciphertext, &jwe_struct.protected, &jwe_struct.iv, &jwe_struct.tag, - &sym_key)?; + &cek)?; - return Ok((message, sender)) + let res = UnpackMessage { + message, + sender_verkey + }; + + return serde_json::to_vec(&res) + .map_err(|err| + IndyError::CommonError( + CommonError::InvalidStructure( + format!("Failed to serialize message {}", err) + ) + ) + ) } // close if statement if a kid matches a verkey found in wallet } // close for loop searching through recipients on kid - //If it gets to this point no verkey was found in wallet that matches a kid + // If it gets to this point no verkey was found in wallet that matches a kid so return Error return Err(IndyError::WalletError(WalletError::ItemNotFound)) } } diff --git a/libindy/src/commands/route.rs b/libindy/src/commands/route.rs index c31a462b01..cdabd1079a 100644 --- a/libindy/src/commands/route.rs +++ b/libindy/src/commands/route.rs @@ -78,7 +78,7 @@ impl RouteCommandExecutor { })?; //encrypt ciphertext - let (sym_key, iv, ciphertext) = self.crypto_service.encrypt_ciphertext(message); + let (cek, iv, ciphertext) = self.crypto_service.encrypt_ciphertext(message); //convert sender_vk to Key let my_key = &ws @@ -90,7 +90,7 @@ impl RouteCommandExecutor { for their_vk in recv_keys { auth_recipients.push( - self.auth_encrypt_recipient(my_key, their_vk, &sym_key, cs.clone()) + self.auth_encrypt_recipient(my_key, their_vk, &cek, cs.clone()) .map_err(|err| { RouteError::PackError(format!("Failed to push auth recipient {}", err)) })?, @@ -116,13 +116,13 @@ impl RouteCommandExecutor { })?; //encrypt ciphertext - let (sym_key, iv, ciphertext) = self.encrypt_ciphertext(message); + let (cek, iv, ciphertext) = self.encrypt_ciphertext(message); //encrypt ceks let mut anon_recipients: Vec = vec![]; for their_vk in recv_keys { let anon_recipient = - self.anon_encrypt_recipient(their_vk, sym_key.clone(), cs.clone())?; + self.anon_encrypt_recipient(their_vk, cek.clone(), cs.clone())?; anon_recipients.push(anon_recipient); } @@ -162,14 +162,14 @@ impl RouteCommandExecutor { .map_err(|err| RouteError::UnpackError(format!("Can't find my_key: {:?}", err)))?; //decrypt recipient header - let (ephem_sym_key, sender_vk) = + let (ephem_cek, sender_vk) = self.crypto_service.auth_decrypt_recipient(my_key, recipient_struct)?; // decode let message = self.crypto_service.decrypt_ciphertext( &auth_ames_struct.ciphertext, &auth_ames_struct.iv, - &ephem_sym_key, + &ephem_cek, )?; Ok((message, sender_vk)) @@ -191,13 +191,13 @@ impl RouteCommandExecutor { .map_err(|err| RouteError::UnpackError(format!("Can't find my_key: {:?}", err)))?; //decrypt recipient header - let ephem_sym_key = self.crypto_service.anon_decrypt_recipient(my_key, recipient_struct)?; + let ephem_cek = self.crypto_service.anon_decrypt_recipient(my_key, recipient_struct)?; //decrypt message let message = self.crypto_service.decrypt_ciphertext( &auth_ames_struct.ciphertext, &auth_ames_struct.iv, - &ephem_sym_key, + &ephem_cek, )?; //return message and no key diff --git a/libindy/src/domain/crypto/pack.rs b/libindy/src/domain/crypto/pack.rs index 9560d4d608..e20bc666ed 100644 --- a/libindy/src/domain/crypto/pack.rs +++ b/libindy/src/domain/crypto/pack.rs @@ -25,4 +25,11 @@ pub struct Protected { pub typ: String, pub alg: String, pub recipients: Vec, +} + +#[derive(Serialize, Deserialize, Debug, Clone, Eq, PartialEq)] +pub struct UnpackMessage { + pub message: String, + #[serde(skip_serializing_if = "Option::is_none")] + pub sender_verkey: Option } \ No newline at end of file diff --git a/libindy/src/services/crypto/mod.rs b/libindy/src/services/crypto/mod.rs index 06775cbbe4..bada53cd5c 100644 --- a/libindy/src/services/crypto/mod.rs +++ b/libindy/src/services/crypto/mod.rs @@ -452,12 +452,12 @@ impl CryptoService { pub fn encrypt_plaintext(&self, plaintext: Vec, aad: &str, - sym_key: &chacha20poly1305_ietf::Key) + cek: &chacha20poly1305_ietf::Key) -> (String, String, String) { //encrypt message with aad let (ciphertext, iv, tag) = gen_nonce_and_encrypt_detached( - plaintext.as_slice(), aad.as_bytes(), &sym_key); + plaintext.as_slice(), aad.as_bytes(), &cek); //base64 url encode data let iv_encoded = base64::encode(&iv[..]); @@ -474,7 +474,7 @@ impl CryptoService { aad: &str, iv: &str, tag: &str, - sym_key: &chacha20poly1305_ietf::Key, + cek: &chacha20poly1305_ietf::Key, ) -> Result { //convert ciphertext to bytes @@ -512,7 +512,7 @@ impl CryptoService { //decrypt message let plaintext_bytes = chacha20poly1305_ietf::decrypt_detached(ciphertext_as_bytes, - sym_key, + cek, &nonce, &tag, Some(aad.as_bytes())) @@ -534,7 +534,6 @@ impl CryptoService { mod tests { use super::*; use domain::crypto::did::MyDidInfo; - use utils::crypto::randombytes::randombytes; use utils::crypto::chacha20poly1305_ietf::gen_key; #[test] @@ -812,14 +811,14 @@ mod tests { let service: CryptoService = CryptoService::new(); let plaintext = "Hello World".as_bytes().to_vec(); let aad = "Random authenticated additional data"; - let sym_key = gen_key(); + let cek = gen_key(); let (expected_ciphertext, iv_encoded, tag) = service - .encrypt_plaintext(plaintext.clone(), aad, &sym_key); + .encrypt_plaintext(plaintext.clone(), aad, &cek); let expected_plaintext = service - .decrypt_ciphertext(&expected_ciphertext, aad, &iv_encoded, &tag, &sym_key).unwrap(); + .decrypt_ciphertext(&expected_ciphertext, aad, &iv_encoded, &tag, &cek).unwrap(); assert_eq!(expected_plaintext.as_bytes().to_vec(), plaintext); } @@ -830,14 +829,14 @@ mod tests { let service: CryptoService = CryptoService::new(); let plaintext = "".as_bytes().to_vec(); let aad = "Random authenticated additional data"; - let sym_key = gen_key(); + let cek = gen_key(); let (expected_ciphertext, iv_encoded, tag) = service - .encrypt_plaintext(plaintext.clone(), aad, &sym_key); + .encrypt_plaintext(plaintext.clone(), aad, &cek); let expected_plaintext = service - .decrypt_ciphertext(&expected_ciphertext, aad, &iv_encoded, &tag, &sym_key).unwrap(); + .decrypt_ciphertext(&expected_ciphertext, aad, &iv_encoded, &tag, &cek).unwrap(); assert_eq!(expected_plaintext.as_bytes().to_vec(), plaintext); } @@ -847,16 +846,16 @@ mod tests { let service: CryptoService = CryptoService::new(); let plaintext = "Hello World".as_bytes().to_vec(); let aad = "Random authenticated additional data"; - let sym_key = gen_key(); + let cek = gen_key(); let (expected_ciphertext, _, tag) = service - .encrypt_plaintext(plaintext, aad, &sym_key); + .encrypt_plaintext(plaintext, aad, &cek); //convert values to base64 encoded strings let bad_iv_input = "invalid_iv"; let expected_error = service - .decrypt_ciphertext(&expected_ciphertext, bad_iv_input, &tag, aad, &sym_key); + .decrypt_ciphertext(&expected_ciphertext, bad_iv_input, &tag, aad, &cek); assert!(expected_error.is_err()); } @@ -865,32 +864,32 @@ mod tests { let service: CryptoService = CryptoService::new(); let plaintext = "Hello World".as_bytes().to_vec(); let aad = "Random authenticated additional data"; - let sym_key = gen_key(); + let cek = gen_key(); let (_, iv_encoded, tag) = service - .encrypt_plaintext(plaintext, aad, &sym_key); + .encrypt_plaintext(plaintext, aad, &cek); let bad_ciphertext= base64::encode("bad_ciphertext".as_bytes()); let expected_error = service - .decrypt_ciphertext(&bad_ciphertext, &iv_encoded, &tag, aad, &sym_key); + .decrypt_ciphertext(&bad_ciphertext, &iv_encoded, &tag, aad, &cek); assert!(expected_error.is_err()); } #[test] - pub fn test_encrypt_plaintext_and_decrypt_ciphertext_wrong_sym_key_fails() { + pub fn test_encrypt_plaintext_and_decrypt_ciphertext_wrong_cek_fails() { let service: CryptoService = CryptoService::new(); let plaintext = "Hello World".as_bytes().to_vec(); let aad = "Random authenticated additional data"; - let sym_key = chacha20poly1305_ietf::gen_key(); + let cek = chacha20poly1305_ietf::gen_key(); let (expected_ciphertext, iv_encoded, tag) = service - .encrypt_plaintext(plaintext, aad, &sym_key); + .encrypt_plaintext(plaintext, aad, &cek); - let bad_sym_key= gen_key(); + let bad_cek= gen_key(); let expected_error = service - .decrypt_ciphertext(&expected_ciphertext, &iv_encoded, &tag, aad, &bad_sym_key); + .decrypt_ciphertext(&expected_ciphertext, &iv_encoded, &tag, aad, &bad_cek); assert!(expected_error.is_err()); } @@ -899,15 +898,15 @@ mod tests { let service: CryptoService = CryptoService::new(); let plaintext = "Hello World".as_bytes().to_vec(); let aad = "Random authenticated additional data"; - let sym_key = gen_key(); + let cek = gen_key(); - let (expected_ciphertext, iv_encoded, tag) = service - .encrypt_plaintext(plaintext, aad, &sym_key); + let (expected_ciphertext, iv_encoded, _) = service + .encrypt_plaintext(plaintext, aad, &cek); let bad_tag = "bad_tag".to_string(); let expected_error = service - .decrypt_ciphertext(&expected_ciphertext, &iv_encoded, &bad_tag, aad, &sym_key); + .decrypt_ciphertext(&expected_ciphertext, &iv_encoded, &bad_tag, aad, &cek); assert!(expected_error.is_err()); } @@ -916,15 +915,15 @@ mod tests { let service: CryptoService = CryptoService::new(); let plaintext = "Hello World".as_bytes().to_vec(); let aad = "Random authenticated additional data"; - let sym_key = gen_key(); + let cek = gen_key(); let (expected_ciphertext, iv_encoded, tag) = service - .encrypt_plaintext(plaintext, aad, &sym_key); + .encrypt_plaintext(plaintext, aad, &cek); let bad_aad = "bad aad"; let expected_error = service - .decrypt_ciphertext(&expected_ciphertext, &iv_encoded, &tag, bad_aad, &sym_key); + .decrypt_ciphertext(&expected_ciphertext, &iv_encoded, &tag, bad_aad, &cek); assert!(expected_error.is_err()); } } \ No newline at end of file diff --git a/libindy/tests/crypto.rs b/libindy/tests/crypto.rs index 54c28ed4e6..0f29bc1dfb 100644 --- a/libindy/tests/crypto.rs +++ b/libindy/tests/crypto.rs @@ -473,6 +473,29 @@ mod high_cases { utils::tear_down_with_wallet(wallet_handle); } } + + mod pack_message { + use super::*; + + #[test] + fn indy_pack_message_works() { + + let (wallet_handle, verkey) = setup_with_key(); + + let rec_key_vec = vec![VERKEY_MY1, VERKEY_MY2, VERKEY_TRUSTEE]; + let receiver_keys = serde_json::to_string(&rec_key_vec).unwrap(); + + let message = "Hello World".as_bytes(); + crypto::pack_message(wallet_handle, message, &receiver_keys, &verkey).unwrap(); + + utils::tear_down_with_wallet(wallet_handle); + } + + } + + mod unpack_message { + + } } mod load { diff --git a/libindy/tests/utils/crypto.rs b/libindy/tests/utils/crypto.rs index ea4f0c0c81..85b3feeaeb 100644 --- a/libindy/tests/utils/crypto.rs +++ b/libindy/tests/utils/crypto.rs @@ -40,3 +40,11 @@ pub fn anon_crypt(their_vk: &str, msg: &[u8]) -> Result, ErrorCode> { pub fn anon_decrypt(wallet_handle: i32, my_vk: &str, encrypted_msg: &[u8]) -> Result, ErrorCode> { crypto::anon_decrypt(wallet_handle, my_vk, encrypted_msg).wait() } + +pub fn pack_message(wallet_handle: i32, message: &[u8], receiver_keys: &str, sender: &str) -> Result, ErrorCode> { + crypto::pack_message(wallet_handle, message, receiver_keys, sender).wait() +} + +pub fn unpack_message(wallet_handle: i32, jwe: &[u8]) -> Result, ErrorCode> { + crypto::unpack_message(wallet_handle, jwe).wait() +} \ No newline at end of file diff --git a/wrappers/rust/indy-sys/src/crypto.rs b/wrappers/rust/indy-sys/src/crypto.rs index 4510b28e80..78d185eb20 100644 --- a/wrappers/rust/indy-sys/src/crypto.rs +++ b/wrappers/rust/indy-sys/src/crypto.rs @@ -71,5 +71,21 @@ extern { encrypted_msg: BString, encrypted_len: u32, cb: Option) -> Error; + + #[no_mangle] + pub fn indy_pack_message(command_handle: Handle, + wallet_handle: Handle, + message: BString, + message_len: u32, + receiver_keys: CString, + sender: CString, + cb: Option) -> Error; + + #[no_mangle] + pub fn indy_unpack_message(command_handle: Handle, + wallet_handle: Handle, + jwe_msg: BString, + jwe_len: u32, + cb: Option) -> Error; } diff --git a/wrappers/rust/src/crypto.rs b/wrappers/rust/src/crypto.rs index ced6cfe42e..004deda653 100644 --- a/wrappers/rust/src/crypto.rs +++ b/wrappers/rust/src/crypto.rs @@ -274,3 +274,69 @@ fn _anon_decrypt(command_handle: IndyHandle, wallet_handle: IndyHandle, recipien }) } +/// Unpacks a message packed using indy_pack_message which follows the wire message format HIPE +/// +/// +/// +/// # Arguments +/// * `wallet_handle`: wallet handle (created by Wallet::open). +/// * `message`: a pointer to the first byte of the message to be encrypted +/// * `receiver_keys`: a JSON array as a string containing a list of the receivers verkey's +/// * `sender` : a string of the sender's verkey +/// # Returns +/// a json structure in the form of a JWE that contains the encrypted message and associated metadata +pub fn pack_message(wallet_handle: IndyHandle, message: &[u8], receiver_keys: &str, sender: &str) -> Box, Error=ErrorCode>> { + let (receiver, command_handle, cb) = ClosureHandler::cb_ec_slice(); + + println!("got to rust/src/crypto.rs"); + let err= _pack_message(command_handle, wallet_handle, message, receiver_keys, sender, cb); + + ResultHandler::slice(command_handle, err, receiver) +} + +fn _pack_message(command_handle: IndyHandle, wallet_handle: IndyHandle, message: &[u8], receiver_keys: &str, sender: &str, cb: Option) -> ErrorCode { + let receiver_keys = c_str!(receiver_keys); + let sender = c_str!(sender); + + let res = ErrorCode::from(unsafe { + crypto::indy_pack_message(command_handle, + wallet_handle, + message.as_ptr() as *const u8, + message.len() as u32, + receiver_keys.as_ptr(), + sender.as_ptr(), + cb) + }); + + println!("got to unsafe call"); + + res +} + +/// Unpacks a message packed using indy_pack_message which follows the wire message format HIPE +/// +/// +/// +/// # Arguments +/// * `wallet_handle`: wallet handle (created by Wallet::open). +/// * `jwe`: a pointer to the first byte of the JWE string +/// # Returns +/// a json structure that contains a decrypted message and a sender_verkey if packed with authcrypt +pub fn unpack_message(wallet_handle: IndyHandle, jwe: &[u8]) -> Box, Error=ErrorCode>> { + let (receiver, command_handle, cb) = ClosureHandler::cb_ec_slice(); + + let err= _unpack_message(command_handle, wallet_handle, jwe, cb); + + ResultHandler::slice(command_handle, err, receiver) +} + +fn _unpack_message(command_handle: IndyHandle, wallet_handle: IndyHandle, jwe: &[u8], cb: Option) -> ErrorCode { + ErrorCode::from(unsafe { + crypto::indy_unpack_message(command_handle, + wallet_handle, + jwe.as_ptr() as *const u8, + jwe.len() as u32, + cb) + }) +} + From fb9c481325163237e5bcf2f4e40b92fe30a39a9f Mon Sep 17 00:00:00 2001 From: Kyle Den Hartog Date: Fri, 21 Dec 2018 18:58:56 -0700 Subject: [PATCH 05/17] fixed seg fault issue first test passing Signed-off-by: Kyle Den Hartog --- libindy/include/indy_agent.h | 15 ------------ libindy/include/indy_crypto.h | 44 +++++++++++++++++------------------ libindy/src/api/crypto.rs | 4 ++-- libindy/src/api/mod.rs | 21 ----------------- libindy/tests/crypto.rs | 4 ++-- wrappers/rust/src/crypto.rs | 8 ++----- 6 files changed, 28 insertions(+), 68 deletions(-) delete mode 100644 libindy/include/indy_agent.h diff --git a/libindy/include/indy_agent.h b/libindy/include/indy_agent.h deleted file mode 100644 index eddc22a767..0000000000 --- a/libindy/include/indy_agent.h +++ /dev/null @@ -1,15 +0,0 @@ -#ifndef __indy__agent__included__ -#define __indy__agent__included__ - -#ifdef __cplusplus -extern "C" { -#endif - - extern indy_error_t indy_pack_message(indy_handle_t command_handle, - indy_handle_t wallet_handle, - const char *const , - - void (*cb)(indy_handle_t command_handle, - indy_error_t err, - const char *const vk) - ); \ No newline at end of file diff --git a/libindy/include/indy_crypto.h b/libindy/include/indy_crypto.h index 5b6d51794b..3c2d777ae6 100644 --- a/libindy/include/indy_crypto.h +++ b/libindy/include/indy_crypto.h @@ -381,18 +381,18 @@ extern "C" { /// Wallet* /// Ledger* /// Crypto* - extern indy_error_t indy_crypto_pack_message(indy_handle_t command_handle, - indy_handle_t wallet_handle, - const indy_u8_t* message, - indy_u32_t message_len, - const char * receiver_keys, - const char * sender, - - void (*cb)(indy_handle_t command_handle_, - indy_error_t err, - const indy_u8_t* jwe_msg_raw, - indy_u32_t jwe_msg_len) - ); + extern indy_error_t indy_pack_message(indy_handle_t command_handle, + indy_handle_t wallet_handle, + const indy_u8_t* message, + indy_u64_t message_len, + const char * receiver_keys, + const char * sender, + + void (*cb)(indy_handle_t command_handle_, + indy_error_t err, + const indy_u8_t* jwe_msg_raw, + indy_u64_t jwe_msg_len) + ); /// Unpacks a message packed using indy_pack_message which follows the wire message format HIPE @@ -424,16 +424,16 @@ extern "C" { /// Wallet* /// Ledger* /// Crypto* - extern indy_error_t indy_crypto_unpack_message(indy_handle_t command_handle, - indy_handle_t wallet_handle, - const indy_u8_t* jwe_msg, - indy_u32_t jwe_len, - - void (*cb)(indy_handle_t command_handle_, - indy_error_t err, - const indy_u8_t* res_json_raw, - indy_u32_t res_json_len) - ); + extern indy_error_t indy_unpack_message(indy_handle_t command_handle, + indy_handle_t wallet_handle, + const indy_u8_t* jwe_msg, + indy_u64_t jwe_len, + + void (*cb)(indy_handle_t command_handle_, + indy_error_t err, + const indy_u8_t* res_json_raw, + indy_u64_t res_json_len) + ); #ifdef __cplusplus } #endif diff --git a/libindy/src/api/crypto.rs b/libindy/src/api/crypto.rs index 7ca8caa94d..15c5df3b23 100644 --- a/libindy/src/api/crypto.rs +++ b/libindy/src/api/crypto.rs @@ -623,7 +623,7 @@ pub extern fn indy_crypto_anon_decrypt(command_handle: IndyHandle, /// Ledger* /// Crypto* #[no_mangle] -pub fn indy_pack_message( +pub extern fn indy_pack_message( command_handle: i32, wallet_handle: i32, message: *const u8, @@ -694,7 +694,7 @@ pub fn indy_pack_message( /// Ledger* /// Crypto* #[no_mangle] -pub fn indy_unpack_message( +pub extern fn indy_unpack_message( command_handle: i32, wallet_handle: i32, jwe_data: *const u8, diff --git a/libindy/src/api/mod.rs b/libindy/src/api/mod.rs index 178c141602..95ff342276 100644 --- a/libindy/src/api/mod.rs +++ b/libindy/src/api/mod.rs @@ -12,7 +12,6 @@ pub mod blob_storage; pub mod non_secrets; pub mod payments; pub mod logger; -// pub mod agent; use self::libc::c_char; @@ -240,26 +239,6 @@ pub enum ErrorCode // Extra funds on inputs PaymentExtraFundsError = 705, - // Failed to perform Encryption or Decryption while routing messages - RouteEncryptionError = 800, - - // Failed to Encode the data properly while routing messages - RouteEncodeError = 801, - - //Failed to Decode the data properly while routing messages - RouteDecodeError = 802, - - // Failed to unpack the JWM - RouteUnpackError = 803, - - // Failed to pack the JWM - RoutePackError = 804, - - // Key was included when it shouldn't have been - RouteMissingKeyError = 805, - - // Failed to serialize a JWM based on the parameters provided - RouteSerializationError = 806 } /// Set libindy runtime configuration. Can be optionally called to change current params. diff --git a/libindy/tests/crypto.rs b/libindy/tests/crypto.rs index 0f29bc1dfb..693c39db2a 100644 --- a/libindy/tests/crypto.rs +++ b/libindy/tests/crypto.rs @@ -486,8 +486,8 @@ mod high_cases { let receiver_keys = serde_json::to_string(&rec_key_vec).unwrap(); let message = "Hello World".as_bytes(); - crypto::pack_message(wallet_handle, message, &receiver_keys, &verkey).unwrap(); - + let res = crypto::pack_message(wallet_handle, message, &receiver_keys, &verkey); + assert!(res.is_ok()); utils::tear_down_with_wallet(wallet_handle); } diff --git a/wrappers/rust/src/crypto.rs b/wrappers/rust/src/crypto.rs index 004deda653..42f594ae75 100644 --- a/wrappers/rust/src/crypto.rs +++ b/wrappers/rust/src/crypto.rs @@ -288,7 +288,6 @@ fn _anon_decrypt(command_handle: IndyHandle, wallet_handle: IndyHandle, recipien pub fn pack_message(wallet_handle: IndyHandle, message: &[u8], receiver_keys: &str, sender: &str) -> Box, Error=ErrorCode>> { let (receiver, command_handle, cb) = ClosureHandler::cb_ec_slice(); - println!("got to rust/src/crypto.rs"); let err= _pack_message(command_handle, wallet_handle, message, receiver_keys, sender, cb); ResultHandler::slice(command_handle, err, receiver) @@ -298,7 +297,7 @@ fn _pack_message(command_handle: IndyHandle, wallet_handle: IndyHandle, message: let receiver_keys = c_str!(receiver_keys); let sender = c_str!(sender); - let res = ErrorCode::from(unsafe { + ErrorCode::from(unsafe { crypto::indy_pack_message(command_handle, wallet_handle, message.as_ptr() as *const u8, @@ -306,11 +305,8 @@ fn _pack_message(command_handle: IndyHandle, wallet_handle: IndyHandle, message: receiver_keys.as_ptr(), sender.as_ptr(), cb) - }); - - println!("got to unsafe call"); + }) - res } /// Unpacks a message packed using indy_pack_message which follows the wire message format HIPE From 458063957a1580c06336f8ba5f6684ee0dbe6bcc Mon Sep 17 00:00:00 2001 From: Kyle Den Hartog Date: Sat, 22 Dec 2018 13:38:43 -0700 Subject: [PATCH 06/17] added additional tests Signed-off-by: Kyle Den Hartog --- libindy/tests/crypto.rs | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/libindy/tests/crypto.rs b/libindy/tests/crypto.rs index 693c39db2a..05dfcd4027 100644 --- a/libindy/tests/crypto.rs +++ b/libindy/tests/crypto.rs @@ -491,6 +491,20 @@ mod high_cases { utils::tear_down_with_wallet(wallet_handle); } + #[test] + fn indy_pack_message_works_empty_message() { + let (wallet_handle, verkey) = setup_with_key(); + + let rec_key_vec = vec![VERKEY_MY1, VERKEY_MY2, VERKEY_TRUSTEE]; + let receiver_keys = serde_json::to_string(&rec_key_vec).unwrap(); + + let message = "".as_bytes(); + let res = crypto::pack_message(wallet_handle, message, &receiver_keys, &verkey); + assert_eq!(ErrorCode::CommonInvalidParam3, res.unwrap_err()); + + utils::tear_down_with_wallet(wallet_handle); + } + } mod unpack_message { From c3e6555ee78496eafca0c3dc3e01c796bb93ceeb Mon Sep 17 00:00:00 2001 From: Kyle Den Hartog Date: Mon, 24 Dec 2018 16:02:12 -0600 Subject: [PATCH 07/17] added high level tests for pack_message Signed-off-by: Kyle Den Hartog --- libindy/src/api/crypto.rs | 2 +- libindy/src/commands/crypto.rs | 668 ++++++++++++++++---------------- libindy/src/domain/agent/mod.rs | 0 libindy/src/domain/mod.rs | 1 - libindy/src/utils/ctypes.rs | 1 + libindy/tests/crypto.rs | 87 ++++- 6 files changed, 414 insertions(+), 345 deletions(-) delete mode 100644 libindy/src/domain/agent/mod.rs diff --git a/libindy/src/api/crypto.rs b/libindy/src/api/crypto.rs index 15c5df3b23..91e8d1f353 100644 --- a/libindy/src/api/crypto.rs +++ b/libindy/src/api/crypto.rs @@ -637,7 +637,7 @@ pub extern fn indy_pack_message( check_useful_c_byte_array!(message, message_len, ErrorCode::CommonInvalidParam2, ErrorCode::CommonInvalidParam3); check_useful_c_str!(receiver_keys, ErrorCode::CommonInvalidParam4); - check_useful_opt_c_str!(sender, ErrorCode::CommonInvalidParam5); + check_useful_c_str_empty_accepted!(sender, ErrorCode::CommonInvalidParam5); check_useful_c_callback!(cb, ErrorCode::CommonInvalidParam6); trace!("indy_pack_message: entities >>> wallet_handle: {:?}, message: {:?}, message_len {:?},\ diff --git a/libindy/src/commands/crypto.rs b/libindy/src/commands/crypto.rs index b5d83eef14..9ab86c91ec 100644 --- a/libindy/src/commands/crypto.rs +++ b/libindy/src/commands/crypto.rs @@ -3,77 +3,85 @@ extern crate serde_json; use std::collections::HashMap; -use errors::indy::IndyError; -use errors::crypto::CryptoError; +use domain::crypto::key::{Key, KeyInfo, KeyMetadata}; +use domain::crypto::pack::*; use errors::common::CommonError; +use errors::indy::IndyError; use errors::wallet::WalletError; -use domain::crypto::key::{KeyInfo, Key, KeyMetadata}; -use domain::crypto::pack::*; -use services::wallet::{WalletService, RecordOptions}; use services::crypto::CryptoService; +use services::wallet::{RecordOptions, WalletService}; use std::rc::Rc; -use std::str; use std::result; -use utils::crypto::chacha20poly1305_ietf; +use std::str; use utils::crypto::base64; +use utils::crypto::chacha20poly1305_ietf; type Result = result::Result; pub enum CryptoCommand { CreateKey( - i32, // wallet handle + i32, // wallet handle KeyInfo, // key info - Box) + Send>), + Box) + Send>, + ), SetKeyMetadata( - i32, // wallet handle + i32, // wallet handle String, // verkey String, // metadata - Box) + Send>), + Box) + Send>, + ), GetKeyMetadata( - i32, // wallet handle + i32, // wallet handle String, // verkey - Box) + Send>), + Box) + Send>, + ), CryptoSign( - i32, // wallet handle - String, // my vk + i32, // wallet handle + String, // my vk Vec, // msg - Box>) + Send>), + Box>) + Send>, + ), CryptoVerify( - String, // their vk + String, // their vk Vec, // msg Vec, // signature - Box) + Send>), + Box) + Send>, + ), AuthenticatedEncrypt( - i32, // wallet handle - String, // my vk - String, // their vk + i32, // wallet handle + String, // my vk + String, // their vk Vec, // msg - Box>) + Send>), + Box>) + Send>, + ), AuthenticatedDecrypt( - i32, // wallet handle - String, // my vk + i32, // wallet handle + String, // my vk Vec, // encrypted msg - Box)>) + Send>), + Box)>) + Send>, + ), AnonymousEncrypt( - String, // their vk + String, // their vk Vec, // msg - Box>) + Send>), + Box>) + Send>, + ), AnonymousDecrypt( - i32, // wallet handle - String, // my vk + i32, // wallet handle + String, // my vk Vec, // msg - Box>) + Send>), + Box>) + Send>, + ), PackMessage( Vec, // plaintext message - String, // list of receiver's keys - Option, // senders verkey - i32, //wallet handle + String, // list of receiver's keys + String, // senders verkey + i32, //wallet handle Box>) + Send>, ), UnpackMessage( Vec, // JWE - i32, // wallet handle + i32, // wallet handle Box>) + Send>, ), } @@ -84,8 +92,9 @@ pub struct CryptoCommandExecutor { } impl CryptoCommandExecutor { - pub fn new(wallet_service: Rc, - crypto_service: Rc, + pub fn new( + wallet_service: Rc, + crypto_service: Rc, ) -> CryptoCommandExecutor { CryptoCommandExecutor { wallet_service, @@ -133,7 +142,7 @@ impl CryptoCommandExecutor { } CryptoCommand::PackMessage(message, receivers, sender_vk, wallet_handle, cb) => { info!("PackMessage command received"); - cb(self.pack_msg(message, &receivers, sender_vk, wallet_handle)); + cb(self.pack_msg(message, &receivers, &sender_vk, wallet_handle)); } CryptoCommand::UnpackMessage(jwe_json, wallet_handle, cb) => { info!("UnpackMessage command received"); @@ -143,25 +152,34 @@ impl CryptoCommandExecutor { } fn create_key(&self, wallet_handle: i32, key_info: &KeyInfo) -> Result { - debug!("create_key >>> wallet_handle: {:?}, key_info: {:?}", wallet_handle, secret!(key_info)); + debug!( + "create_key >>> wallet_handle: {:?}, key_info: {:?}", + wallet_handle, + secret!(key_info) + ); let key = self.crypto_service.create_key(key_info)?; - self.wallet_service.add_indy_object(wallet_handle, &key.verkey, &key, &HashMap::new())?; + self.wallet_service + .add_indy_object(wallet_handle, &key.verkey, &key, &HashMap::new())?; let res = key.verkey; debug!("create_key <<< res: {:?}", res); Ok(res) } - fn crypto_sign(&self, - wallet_handle: i32, - my_vk: &str, - msg: &[u8]) -> Result> { - debug!("crypto_sign >>> wallet_handle: {:?}, sender_vk: {:?}, msg: {:?}", wallet_handle, my_vk, msg); + fn crypto_sign(&self, wallet_handle: i32, my_vk: &str, msg: &[u8]) -> Result> { + debug!( + "crypto_sign >>> wallet_handle: {:?}, sender_vk: {:?}, msg: {:?}", + wallet_handle, my_vk, msg + ); self.crypto_service.validate_key(my_vk)?; - let key: Key = self.wallet_service.get_indy_object(wallet_handle, &my_vk, &RecordOptions::id_value())?; + let key: Key = self.wallet_service.get_indy_object( + wallet_handle, + &my_vk, + &RecordOptions::id_value(), + )?; let res = self.crypto_service.sign(&key, msg)?; @@ -170,11 +188,11 @@ impl CryptoCommandExecutor { Ok(res) } - fn crypto_verify(&self, - their_vk: &str, - msg: &[u8], - signature: &[u8]) -> Result { - debug!("crypto_verify >>> their_vk: {:?}, msg: {:?}, signature: {:?}", their_vk, msg, signature); + fn crypto_verify(&self, their_vk: &str, msg: &[u8], signature: &[u8]) -> Result { + debug!( + "crypto_verify >>> their_vk: {:?}, msg: {:?}, signature: {:?}", + their_vk, msg, signature + ); self.crypto_service.validate_key(their_vk)?; @@ -185,34 +203,54 @@ impl CryptoCommandExecutor { Ok(res) } - fn authenticated_encrypt(&self, - wallet_handle: i32, - my_vk: &str, - their_vk: &str, - msg: &[u8]) -> Result> { - debug!("authenticated_encrypt >>> wallet_handle: {:?}, my_vk: {:?}, their_vk: {:?}, msg: {:?}", wallet_handle, my_vk, their_vk, msg); + fn authenticated_encrypt( + &self, + wallet_handle: i32, + my_vk: &str, + their_vk: &str, + msg: &[u8], + ) -> Result> { + debug!( + "authenticated_encrypt >>> wallet_handle: {:?}, my_vk: {:?}, their_vk: {:?}, msg: {:?}", + wallet_handle, my_vk, their_vk, msg + ); self.crypto_service.validate_key(my_vk)?; self.crypto_service.validate_key(their_vk)?; - let my_key: Key = self.wallet_service.get_indy_object(wallet_handle, my_vk, &RecordOptions::id_value())?; + let my_key: Key = self.wallet_service.get_indy_object( + wallet_handle, + my_vk, + &RecordOptions::id_value(), + )?; - let res = self.crypto_service.authenticated_encrypt(&my_key, their_vk, msg)?; + let res = self + .crypto_service + .authenticated_encrypt(&my_key, their_vk, msg)?; debug!("authenticated_encrypt <<< res: {:?}", res); Ok(res) } - fn authenticated_decrypt(&self, - wallet_handle: i32, - my_vk: &str, - msg: &[u8]) -> Result<(String, Vec)> { - debug!("authenticated_decrypt >>> wallet_handle: {:?}, my_vk: {:?}, msg: {:?}", wallet_handle, my_vk, msg); + fn authenticated_decrypt( + &self, + wallet_handle: i32, + my_vk: &str, + msg: &[u8], + ) -> Result<(String, Vec)> { + debug!( + "authenticated_decrypt >>> wallet_handle: {:?}, my_vk: {:?}, msg: {:?}", + wallet_handle, my_vk, msg + ); self.crypto_service.validate_key(my_vk)?; - let my_key: Key = self.wallet_service.get_indy_object(wallet_handle, my_vk, &RecordOptions::id_value())?; + let my_key: Key = self.wallet_service.get_indy_object( + wallet_handle, + my_vk, + &RecordOptions::id_value(), + )?; let res = self.crypto_service.authenticated_decrypt(&my_key, &msg)?; @@ -221,10 +259,11 @@ impl CryptoCommandExecutor { Ok(res) } - fn anonymous_encrypt(&self, - their_vk: &str, - msg: &[u8]) -> Result> { - debug!("anonymous_encrypt >>> their_vk: {:?}, msg: {:?}", their_vk, msg); + fn anonymous_encrypt(&self, their_vk: &str, msg: &[u8]) -> Result> { + debug!( + "anonymous_encrypt >>> their_vk: {:?}, msg: {:?}", + their_vk, msg + ); self.crypto_service.validate_key(their_vk)?; @@ -235,17 +274,28 @@ impl CryptoCommandExecutor { Ok(res) } - fn anonymous_decrypt(&self, - wallet_handle: i32, - my_vk: &str, - encrypted_msg: &[u8]) -> Result> { - debug!("anonymous_decrypt >>> wallet_handle: {:?}, my_vk: {:?}, encrypted_msg: {:?}", wallet_handle, my_vk, encrypted_msg); + fn anonymous_decrypt( + &self, + wallet_handle: i32, + my_vk: &str, + encrypted_msg: &[u8], + ) -> Result> { + debug!( + "anonymous_decrypt >>> wallet_handle: {:?}, my_vk: {:?}, encrypted_msg: {:?}", + wallet_handle, my_vk, encrypted_msg + ); self.crypto_service.validate_key(&my_vk)?; - let my_key: Key = self.wallet_service.get_indy_object(wallet_handle, &my_vk, &RecordOptions::id_value())?; + let my_key: Key = self.wallet_service.get_indy_object( + wallet_handle, + &my_vk, + &RecordOptions::id_value(), + )?; - let res = self.crypto_service.crypto_box_seal_open(&my_key, &encrypted_msg)?; + let res = self + .crypto_service + .crypto_box_seal_open(&my_key, &encrypted_msg)?; debug!("anonymous_decrypt <<< res: {:?}", res); @@ -253,27 +303,38 @@ impl CryptoCommandExecutor { } fn set_key_metadata(&self, wallet_handle: i32, verkey: &str, metadata: &str) -> Result<()> { - debug!("set_key_metadata >>> wallet_handle: {:?}, verkey: {:?}, metadata: {:?}", wallet_handle, verkey, metadata); + debug!( + "set_key_metadata >>> wallet_handle: {:?}, verkey: {:?}, metadata: {:?}", + wallet_handle, verkey, metadata + ); self.crypto_service.validate_key(verkey)?; - let metadata = KeyMetadata {value: metadata.to_string()}; + let metadata = KeyMetadata { + value: metadata.to_string(), + }; - self.wallet_service.upsert_indy_object(wallet_handle, &verkey, &metadata)?; + self.wallet_service + .upsert_indy_object(wallet_handle, &verkey, &metadata)?; debug!("set_key_metadata <<<"); Ok(()) } - fn get_key_metadata(&self, - wallet_handle: i32, - verkey: &str) -> Result { - debug!("get_key_metadata >>> wallet_handle: {:?}, verkey: {:?}", wallet_handle, verkey); + fn get_key_metadata(&self, wallet_handle: i32, verkey: &str) -> Result { + debug!( + "get_key_metadata >>> wallet_handle: {:?}, verkey: {:?}", + wallet_handle, verkey + ); self.crypto_service.validate_key(verkey)?; - let metadata = self.wallet_service.get_indy_object::(wallet_handle, &verkey, &RecordOptions::id_value())?; + let metadata = self.wallet_service.get_indy_object::( + wallet_handle, + &verkey, + &RecordOptions::id_value(), + )?; let res = metadata.value; @@ -282,224 +343,118 @@ impl CryptoCommandExecutor { Ok(res) } - //TODO: Refactor pack to be more modular to version changes or crypto_scheme changes + //TODO: Refactor pack to be more modular to version changes or crypto_scheme changes //this match statement is super messy, but the easiest way to comply with current architecture pub fn pack_msg( &self, message: Vec, receivers: &str, - sender_vk: Option, - wallet_handle: i32 + sender_vk: &str, + wallet_handle: i32, ) -> Result> { - - //generate symmetrical key + //generate cek let cek = chacha20poly1305_ietf::gen_key(); //list of ceks used to construct JWE later let mut encrypted_recipients_struct: Vec = vec![]; - match sender_vk { - - Some(verkey) => { - //TODO find more readable way to perform authcrypt funtionality something like private command function - - //get my_key from my wallet - let my_key = self.wallet_service - .get_indy_object(wallet_handle, &verkey, &RecordOptions::id_value())?; - - //parse receivers to structs - let receiver_list : Vec = serde_json::from_str(receivers) - .map_err(|err| - IndyError::CryptoError( - CryptoError::CommonError( - CommonError::InvalidStructure( - format!("Failed to deserialize receiver list of keys {}", err) - ) - ) - ) - )?; + //parse receivers to structs + let receiver_list: Vec = serde_json::from_str(receivers).map_err(|err| { + IndyError::CommonError(CommonError::InvalidStructure(format!( + "Failed to deserialize receiver list of keys {}", + err + ))) + })?; + + //break early and error out if no receivers keys are provided + if receiver_list.is_empty() { + return Err(IndyError::CommonError(CommonError::InvalidParam4(format!( + "No receiver keys found" + )))); + } + match sender_vk.is_empty() { + true => { + // anoncrypt //encrypt cek for recipient for their_vk in receiver_list { - let enc_cek = self.crypto_service - .authenticated_encrypt(&my_key, &their_vk, &cek[..])?; + //encrypt sender verkey + let enc_cek = self.crypto_service.crypto_box_seal(&their_vk, &cek[..])?; //create recipient struct and push to encrypted list - encrypted_recipients_struct.push( - Recipient { - encrypted_key: base64::encode(enc_cek.as_slice()), - header: Header { - kid: base64::encode(&their_vk.as_bytes()) - } - }); + encrypted_recipients_struct.push(Recipient { + encrypted_key: base64::encode(enc_cek.as_slice()), + header: Header { + kid: base64::encode(&their_vk.as_bytes()), + }, + }); } // end for-loop - //structure protected and base64URL encode it - let protected_struct = Protected { - enc: "xchacha20poly1305".to_string(), - typ: "JWM/1.0".to_string(), - alg: "Authcrypt".to_string(), - recipients: encrypted_recipients_struct, - }; - let protected_encoded = serde_json::to_string(&protected_struct) - .map_err(|err| - IndyError::CryptoError( - CryptoError::CommonError( - CommonError::InvalidStructure( - format!("Failed to serialize protected field {}", err)))) - )?; - - let base64_protected = base64::encode(protected_encoded.as_bytes()); - - // encrypt ciphertext and integrity protect "protected" field - let (iv, ciphertext, tag) = self.crypto_service - .encrypt_plaintext(message, &base64_protected, &cek); - - //construct JWE struct - let jwe_struct = JWE { - protected : base64_protected, - iv, - ciphertext, - tag - }; - - //convert JWE struct to a string and return - serde_json::to_vec(&jwe_struct) - .map_err(|err| - IndyError::CryptoError( - CryptoError::CommonError( - CommonError::InvalidStructure( - format!("Failed to serialize JWE {}", err) - ) - ) - ) - ) - }, - - None => { - //TODO find more readable way to perform anoncrypt funtionality something like private command function - - //parse receivers to structs - let receiver_list : Vec = serde_json::from_str(receivers) - .map_err(|err| - IndyError::CryptoError( - CryptoError::CommonError( - CommonError::InvalidStructure( - format!("Failed to deserialize receiver list of keys {}", err) - ) - ) - ) - )?; + self._format_pack_message(encrypted_recipients_struct, message, &cek) + } + false => { + //authcrypt + //get my_key from my wallet + let my_key = self.wallet_service.get_indy_object( + wallet_handle, + sender_vk, + &RecordOptions::id_value(), + )?; //encrypt cek for recipient for their_vk in receiver_list { - - //encrypt sender verkey - let enc_cek = self.crypto_service - .crypto_box_seal(&their_vk, &cek[..])?; + let enc_cek = + self.crypto_service + .authenticated_encrypt(&my_key, &their_vk, &cek[..])?; //create recipient struct and push to encrypted list - encrypted_recipients_struct.push( - Recipient { - encrypted_key: base64::encode(enc_cek.as_slice()), - header: Header { - kid: base64::encode(&their_vk.as_bytes()) - } - }); + encrypted_recipients_struct.push(Recipient { + encrypted_key: base64::encode(enc_cek.as_slice()), + header: Header { + kid: base64::encode(&their_vk.as_bytes()), + }, + }); } // end for-loop - //structure protected and base64URL encode it - let protected_struct = Protected { - enc: "xchacha20poly1305".to_string(), - typ: "JWM/1.0".to_string(), - alg: "Anoncrypt".to_string(), - recipients: encrypted_recipients_struct, - }; - let protected_encoded = base64::encode(serde_json::to_string(&protected_struct) - .map_err(|err| - IndyError::CryptoError( - CryptoError::CommonError( - CommonError::InvalidStructure( - format!("Failed to serialize JWE {}", err) - ) - ) - ) - )?.as_bytes()); - - // encrypt ciphertext - let (iv, ciphertext, tag) = self.crypto_service - .encrypt_plaintext(message, &protected_encoded, &cek); - - //serialize JWE struct - let jwe_struct = JWE { - protected : protected_encoded, - iv, - ciphertext, - tag - }; - - //return JWE_json - serde_json::to_vec(&jwe_struct) - .map_err(|err| - IndyError::CryptoError( - CryptoError::CommonError( - CommonError::InvalidStructure( - format!("Failed to serialize JWE {}", err) - ) - ) - ) - ) + self._format_pack_message(encrypted_recipients_struct, message, &cek) } } } - pub fn unpack_msg( - &self, - jwe_json: Vec, - wallet_handle: i32 - ) -> Result> { - + pub fn unpack_msg(&self, jwe_json: Vec, wallet_handle: i32) -> Result> { //serialize JWE to struct - let jwe_struct : JWE = serde_json::from_slice(jwe_json.as_slice()) - .map_err(|err| - IndyError::CryptoError( - CryptoError::CommonError( - CommonError::InvalidStructure( - format!("Failed to deserialize auth ames {}", err) - ) - ) - ) - )?; + let jwe_struct: JWE = serde_json::from_slice(jwe_json.as_slice()).map_err(|err| { + IndyError::CommonError(CommonError::InvalidStructure(format!( + "Failed to deserialize auth ames {}", + err + ))) + })?; //decode protected data let protected_decoded_vec = base64::decode(&jwe_struct.protected)?; - let protected_decoded_str = String::from_utf8(protected_decoded_vec) - .map_err(|err| - IndyError::CryptoError( - CryptoError::CommonError( - CommonError::InvalidStructure( - format!("Failed to utf8 encode data {}", err) - ) - ) - ) - )?; + let protected_decoded_str = String::from_utf8(protected_decoded_vec).map_err(|err| { + IndyError::CommonError(CommonError::InvalidStructure(format!( + "Failed to utf8 encode data {}", + err + ))) + })?; //convert protected_data_str to struct - let protected_struct : Protected = serde_json::from_str(&protected_decoded_str) - .map_err(|err| - IndyError::CryptoError( - CryptoError::CommonError( - CommonError::InvalidStructure( - format!("Failed to deserialize protected data {}", err) - ) - ) - ) - )?; + let protected_struct: Protected = + serde_json::from_str(&protected_decoded_str).map_err(|err| { + IndyError::CommonError(CommonError::InvalidStructure(format!( + "Failed to deserialize protected data {}", + err + ))) + })?; //search through recipients_list and check if one of the kid matches a verkey in the wallet for recipient in protected_struct.recipients { - let key_in_wallet_result = self.wallet_service - .get_indy_object::(wallet_handle, &recipient.header.kid, &RecordOptions::id_value()); + let key_in_wallet_result = self.wallet_service.get_indy_object::( + wallet_handle, + &recipient.header.kid, + &RecordOptions::id_value(), + ); if key_in_wallet_result.is_ok() { //TODO change to move this to a separate function and return recipient rather than @@ -509,91 +464,132 @@ impl CryptoCommandExecutor { let encrypted_key_vec = base64::decode(&recipient.encrypted_key)?; //get cek and sender data - let (sender_verkey, cek) = match protected_struct.alg.as_ref() { - "Authcrypt" => { - - //get my key based on kid - let my_key = self.wallet_service - .get_indy_object(wallet_handle, &recipient.header.kid, &RecordOptions::id_value())?; - - //decrypt cek - let (sender_vk , cek_as_vec) = self.crypto_service - .authenticated_decrypt(&my_key, encrypted_key_vec.as_slice())?; - - //convert to chacha Key struct - let cek : chacha20poly1305_ietf::Key = chacha20poly1305_ietf::Key::from_slice(&cek_as_vec[..]) - .map_err(|err| - IndyError::CryptoError( - CryptoError::CommonError( - CommonError::InvalidStructure( - format!("Failed to decrypt cek {}", err) - ) - ) - ) + let (sender_verkey, cek) = + match protected_struct.alg.as_ref() { + "Authcrypt" => { + //get my key based on kid + let my_key = self.wallet_service.get_indy_object( + wallet_handle, + &recipient.header.kid, + &RecordOptions::id_value(), )?; - Ok((Some(sender_vk), cek)) - }, //close authcrypt option - - "Anoncrypt" => { - //get my private key - let my_key = self.wallet_service - .get_indy_object(wallet_handle, &recipient.header.kid, &RecordOptions::id_value())?; - - //decrypt cek - let cek_as_vec = self.crypto_service - .crypto_box_seal_open(&my_key, encrypted_key_vec.as_slice())?; - - //convert to chacha Key struct - let cek : chacha20poly1305_ietf::Key = chacha20poly1305_ietf::Key::from_slice(&cek_as_vec[..]) - .map_err(|err| - IndyError::CryptoError( - CryptoError::CommonError( - CommonError::InvalidStructure( - format!("Failed to decrypt cek {}", err) - ) - ) - ) + //decrypt cek + let (sender_vk, cek_as_vec) = self + .crypto_service + .authenticated_decrypt(&my_key, encrypted_key_vec.as_slice())?; + + //convert to chacha Key struct + let cek: chacha20poly1305_ietf::Key = + chacha20poly1305_ietf::Key::from_slice(&cek_as_vec[..]).map_err( + |err| { + IndyError::CommonError(CommonError::InvalidStructure( + format!("Failed to decrypt cek {}", err), + )) + }, + )?; + + Ok((Some(sender_vk), cek)) + } //close authcrypt option + + "Anoncrypt" => { + //get my private key + let my_key = self.wallet_service.get_indy_object( + wallet_handle, + &recipient.header.kid, + &RecordOptions::id_value(), )?; - Ok((None, cek )) - }, //close Anoncrypt option - - _ => Err( - IndyError::CryptoError( - CryptoError::CommonError( - CommonError::InvalidStructure( - format!("Failed to deserialize cek encryption alg") - ) - ) - ) - ) - }?; //close cek and sender_data match statement - - let message = self.crypto_service - .decrypt_ciphertext(&jwe_struct.ciphertext, - &jwe_struct.protected, - &jwe_struct.iv, - &jwe_struct.tag, - &cek)?; + //decrypt cek + let cek_as_vec = self + .crypto_service + .crypto_box_seal_open(&my_key, encrypted_key_vec.as_slice())?; + + //convert to chacha Key struct + let cek: chacha20poly1305_ietf::Key = + chacha20poly1305_ietf::Key::from_slice(&cek_as_vec[..]).map_err( + |err| { + IndyError::CommonError(CommonError::InvalidStructure( + format!("Failed to decrypt cek {}", err), + )) + }, + )?; + + Ok((None, cek)) + } //close Anoncrypt option + + _ => Err(IndyError::CommonError(CommonError::InvalidStructure( + format!("Failed to deserialize cek encryption alg"), + ))), + }?; //close cek and sender_data match statement + + let message = self.crypto_service.decrypt_ciphertext( + &jwe_struct.ciphertext, + &jwe_struct.protected, + &jwe_struct.iv, + &jwe_struct.tag, + &cek, + )?; let res = UnpackMessage { message, - sender_verkey + sender_verkey, }; - return serde_json::to_vec(&res) - .map_err(|err| - IndyError::CommonError( - CommonError::InvalidStructure( - format!("Failed to serialize message {}", err) - ) - ) - ) + return serde_json::to_vec(&res).map_err(|err| { + IndyError::CommonError(CommonError::InvalidStructure(format!( + "Failed to serialize message {}", + err + ))) + }); } // close if statement if a kid matches a verkey found in wallet } // close for loop searching through recipients on kid // If it gets to this point no verkey was found in wallet that matches a kid so return Error - return Err(IndyError::WalletError(WalletError::ItemNotFound)) + return Err(IndyError::WalletError(WalletError::ItemNotFound)); + } + + fn _format_pack_message( + &self, + encrypted_recipients_struct: Vec, + message: Vec, + cek: &chacha20poly1305_ietf::Key, + ) -> Result> { + //structure protected and base64URL encode it + let protected_struct = Protected { + enc: "xchacha20poly1305".to_string(), + typ: "JWM/1.0".to_string(), + alg: "Authcrypt".to_string(), + recipients: encrypted_recipients_struct, + }; + let protected_encoded = serde_json::to_string(&protected_struct).map_err(|err| { + IndyError::CommonError(CommonError::InvalidStructure(format!( + "Failed to serialize protected field {}", + err + ))) + })?; + + let base64_protected = base64::encode(protected_encoded.as_bytes()); + + // encrypt ciphertext and integrity protect "protected" field + let (iv, ciphertext, tag) = + self.crypto_service + .encrypt_plaintext(message, &base64_protected, cek); + + //construct JWE struct + let jwe_struct = JWE { + protected: base64_protected, + iv, + ciphertext, + tag, + }; + + //convert JWE struct to a string and return + serde_json::to_vec(&jwe_struct).map_err(|err| { + IndyError::CommonError(CommonError::InvalidStructure(format!( + "Failed to serialize JWE {}", + err + ))) + }) } } diff --git a/libindy/src/domain/agent/mod.rs b/libindy/src/domain/agent/mod.rs deleted file mode 100644 index e69de29bb2..0000000000 diff --git a/libindy/src/domain/mod.rs b/libindy/src/domain/mod.rs index 013794691d..0ed46eb7e0 100644 --- a/libindy/src/domain/mod.rs +++ b/libindy/src/domain/mod.rs @@ -4,7 +4,6 @@ pub mod ledger; pub mod pairwise; pub mod pool; pub mod wallet; -pub mod agent; #[derive(Debug, Serialize, Deserialize)] pub struct IndyConfig { diff --git a/libindy/src/utils/ctypes.rs b/libindy/src/utils/ctypes.rs index 2f4197f0bf..01ddcbfd94 100644 --- a/libindy/src/utils/ctypes.rs +++ b/libindy/src/utils/ctypes.rs @@ -98,6 +98,7 @@ macro_rules! check_useful_c_str_empty_accepted { } macro_rules! check_useful_opt_c_str { + //TODO This no longer returns None options, only Strings are returned ($x:ident, $e:expr) => { let $x = match ctypes::c_str_to_string($x) { Ok(opt_val) => opt_val.map(String::from), diff --git a/libindy/tests/crypto.rs b/libindy/tests/crypto.rs index 05dfcd4027..04e93c8cd5 100644 --- a/libindy/tests/crypto.rs +++ b/libindy/tests/crypto.rs @@ -474,37 +474,110 @@ mod high_cases { } } - mod pack_message { + mod pack_message_authcrypt { use super::*; #[test] - fn indy_pack_message_works() { - + fn indy_pack_message_authcrypt_works() { let (wallet_handle, verkey) = setup_with_key(); + let rec_key_vec = vec![VERKEY_MY1, VERKEY_MY2, VERKEY_TRUSTEE]; + let receiver_keys = serde_json::to_string(&rec_key_vec).unwrap(); + let message = "Hello World".as_bytes(); + let res = crypto::pack_message(wallet_handle, message, &receiver_keys, &verkey); + assert!(res.is_ok()); + utils::tear_down_with_wallet(wallet_handle); + } + #[test] + fn indy_pack_message_authcrypt_fails_empty_message() { + let (wallet_handle, verkey) = setup_with_key(); let rec_key_vec = vec![VERKEY_MY1, VERKEY_MY2, VERKEY_TRUSTEE]; let receiver_keys = serde_json::to_string(&rec_key_vec).unwrap(); + let message = "".as_bytes(); + let res = crypto::pack_message(wallet_handle, message, &receiver_keys, &verkey); + assert_eq!(ErrorCode::CommonInvalidParam3, res.unwrap_err()); + utils::tear_down_with_wallet(wallet_handle); + } + #[test] + fn indy_pack_message_authcrypt_fails_no_receivers() { + let (wallet_handle, verkey) = setup_with_key(); + let receiver_keys = "[]"; let message = "Hello World".as_bytes(); let res = crypto::pack_message(wallet_handle, message, &receiver_keys, &verkey); - assert!(res.is_ok()); + assert_eq!(ErrorCode::CommonInvalidParam4, res.unwrap_err()); utils::tear_down_with_wallet(wallet_handle); } #[test] - fn indy_pack_message_works_empty_message() { - let (wallet_handle, verkey) = setup_with_key(); + fn indy_pack_message_authcrypt_fails_bad_wallet_handle() { + let (wallet_handle, verkey) = setup_with_key(); + let rec_key_vec = vec![VERKEY_MY1, VERKEY_MY2, VERKEY_TRUSTEE]; + let receiver_keys = serde_json::to_string(&rec_key_vec).unwrap(); + let message = "Hello World".as_bytes(); + let res = crypto::pack_message(wallet_handle + 1, message, &receiver_keys, &verkey); + assert_eq!(ErrorCode::WalletInvalidHandle, res.unwrap_err()); + utils::tear_down_with_wallet(wallet_handle); + } + #[test] + fn indy_pack_message_authcrypt_fails_invalid_verkey() { + let (wallet_handle, _) = setup_with_key(); let rec_key_vec = vec![VERKEY_MY1, VERKEY_MY2, VERKEY_TRUSTEE]; let receiver_keys = serde_json::to_string(&rec_key_vec).unwrap(); + let message = "Hello World".as_bytes(); + let res = crypto::pack_message(wallet_handle, message, &receiver_keys, INVALID_BASE58_VERKEY); + assert_eq!(ErrorCode::WalletItemNotFound, res.unwrap_err()); + utils::tear_down_with_wallet(wallet_handle); + } + + } + + mod pack_message_anoncrypt { + use super::*; + + #[test] + fn indy_pack_message_anon_works() { + let (wallet_handle, _ ) = setup_with_key(); + let rec_key_vec = vec![VERKEY_MY1, VERKEY_MY2, VERKEY_TRUSTEE]; + let receiver_keys = serde_json::to_string(&rec_key_vec).unwrap(); + let message = "Hello World".as_bytes(); + let res = crypto::pack_message(wallet_handle, message, &receiver_keys, ""); + assert!(res.is_ok()); + utils::tear_down_with_wallet(wallet_handle); + } + #[test] + fn indy_pack_message_anoncrypt_fails_empty_message() { + let (wallet_handle, _ ) = setup_with_key(); + let rec_key_vec = vec![VERKEY_MY1, VERKEY_MY2, VERKEY_TRUSTEE]; + let receiver_keys = serde_json::to_string(&rec_key_vec).unwrap(); let message = "".as_bytes(); - let res = crypto::pack_message(wallet_handle, message, &receiver_keys, &verkey); + let res = crypto::pack_message(wallet_handle, message, &receiver_keys, ""); assert_eq!(ErrorCode::CommonInvalidParam3, res.unwrap_err()); + utils::tear_down_with_wallet(wallet_handle); + } + #[test] + fn indy_pack_message_anoncrypt_fails_no_receivers() { + let (wallet_handle, _ ) = setup_with_key(); + let receiver_keys = "[]"; + let message = "Hello World".as_bytes(); + let res = crypto::pack_message(wallet_handle, message, &receiver_keys, ""); + assert_eq!(ErrorCode::CommonInvalidParam4, res.unwrap_err()); utils::tear_down_with_wallet(wallet_handle); } + #[test] + fn indy_pack_message_anoncrypt_passes_bad_wallet_handle() { + let rec_key_vec = vec![VERKEY_MY1, VERKEY_MY2, VERKEY_TRUSTEE]; + let receiver_keys = serde_json::to_string(&rec_key_vec).unwrap(); + let message = "Hello World".as_bytes(); + //The wallet_handle and sender aren't used in this case, so any wallet_handle whether inited or not will work + let res = crypto::pack_message(1, message, &receiver_keys, ""); + assert!(res.is_ok()); + } + } mod unpack_message { From 4b1af8d4c70ee04160fbac0790f34362fbc8d141 Mon Sep 17 00:00:00 2001 From: Andrey Kononykhin Date: Thu, 27 Dec 2018 15:36:27 +0300 Subject: [PATCH 08/17] uses powershell in batch commands Signed-off-by: Andrey Kononykhin --- Jenkinsfile.cd | 6 +++--- Jenkinsfile.ci | 6 +++--- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/Jenkinsfile.cd b/Jenkinsfile.cd index a890b4e8b1..f8527b350e 100644 --- a/Jenkinsfile.cd +++ b/Jenkinsfile.cd @@ -564,10 +564,10 @@ def windowsTesting() { stash includes: 'target/release/*.dll', name: 'LibnullpayWindowsBuildResult' } - bat "copy $WORKSPACE\\libnullpay\\target\\release\\nullpay.dll $WORKSPACE\\cli" + bat "PowerShell.exe \"Copy-Item $WORKSPACE/libnullpay/target/debug/nullpay.dll -Destination $WORKSPACE/cli\"" dir('cli') { - bat "sed -i -e \"s/10\\.0\\.0\\.2/${INDY_SDK_SERVER_IP}/g\" docker_pool_transactions_genesis" + bat "PowerShell.exe \"&{(Get-Content -Path docker_pool_transactions_genesis -Raw) -replace '10\\.0\\.0\\.2','$INDY_SDK_SERVER_IP'} | Set-Content -Path docker_pool_transactions_genesis\"" def featuresArgs = '' // def featuresArgs = '--features "nullpay_plugin"' // disabled because of IS-1109 @@ -594,7 +594,7 @@ def windowsTesting() { stash includes: 'target/release/indy-cli.exe,target/release/*.dll', name: 'IndyCliWindowsBuildResult' } - bat "copy $WORKSPACE\\libnullpay\\target\\release\\nullpay.dll $WORKSPACE\\vcx\\libvcx" + bat "PowerShell.exe \"Copy-Item $WORKSPACE/libnullpay/target/debug/nullpay.dll -Destination $WORKSPACE/vcx/libvcx\"" dir('vcx/libvcx') { echo "Windows Libvcx Test: Build" diff --git a/Jenkinsfile.ci b/Jenkinsfile.ci index e3035668d7..dd72dcd827 100644 --- a/Jenkinsfile.ci +++ b/Jenkinsfile.ci @@ -84,10 +84,10 @@ def windowsTesting() { } } - bat "copy $WORKSPACE\\libnullpay\\target\\debug\\nullpay.dll $WORKSPACE\\cli" + bat "PowerShell.exe \"Copy-Item $WORKSPACE/libnullpay/target/debug/nullpay.dll -Destination $WORKSPACE/cli\"" dir('cli') { - bat "sed -i -e \"s/10\\.0\\.0\\.2/${INDY_SDK_SERVER_IP}/g\" docker_pool_transactions_genesis" + bat "PowerShell.exe \"&{(Get-Content -Path docker_pool_transactions_genesis -Raw) -replace '10\\.0\\.0\\.2','$INDY_SDK_SERVER_IP'} | Set-Content -Path docker_pool_transactions_genesis\"" def featuresArgs = '--features "nullpay_plugin"' @@ -108,7 +108,7 @@ def windowsTesting() { } } - bat "copy $WORKSPACE\\libnullpay\\target\\debug\\nullpay.dll $WORKSPACE\\vcx\\libvcx" + bat "PowerShell.exe \"Copy-Item $WORKSPACE/libnullpay/target/debug/nullpay.dll -Destination $WORKSPACE/vcx/libvcx\"" dir('vcx/libvcx') { echo "Windows Libvcx Test: Build" From 64253da3e7cbc40899ec5c64559c305ea391ce80 Mon Sep 17 00:00:00 2001 From: Andrey Kononykhin Date: Thu, 27 Dec 2018 18:07:37 +0300 Subject: [PATCH 09/17] fixes source target name for nullpay binaries CD Signed-off-by: Andrey Kononykhin --- Jenkinsfile.cd | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Jenkinsfile.cd b/Jenkinsfile.cd index f8527b350e..c745b9ef64 100644 --- a/Jenkinsfile.cd +++ b/Jenkinsfile.cd @@ -564,7 +564,7 @@ def windowsTesting() { stash includes: 'target/release/*.dll', name: 'LibnullpayWindowsBuildResult' } - bat "PowerShell.exe \"Copy-Item $WORKSPACE/libnullpay/target/debug/nullpay.dll -Destination $WORKSPACE/cli\"" + bat "PowerShell.exe \"Copy-Item $WORKSPACE/libnullpay/target/release/nullpay.dll -Destination $WORKSPACE/cli\"" dir('cli') { bat "PowerShell.exe \"&{(Get-Content -Path docker_pool_transactions_genesis -Raw) -replace '10\\.0\\.0\\.2','$INDY_SDK_SERVER_IP'} | Set-Content -Path docker_pool_transactions_genesis\"" @@ -594,7 +594,7 @@ def windowsTesting() { stash includes: 'target/release/indy-cli.exe,target/release/*.dll', name: 'IndyCliWindowsBuildResult' } - bat "PowerShell.exe \"Copy-Item $WORKSPACE/libnullpay/target/debug/nullpay.dll -Destination $WORKSPACE/vcx/libvcx\"" + bat "PowerShell.exe \"Copy-Item $WORKSPACE/libnullpay/target/release/nullpay.dll -Destination $WORKSPACE/vcx/libvcx\"" dir('vcx/libvcx') { echo "Windows Libvcx Test: Build" From 80d652a3099f1fbb6b8602fc5c6af84401063725 Mon Sep 17 00:00:00 2001 From: Kyle Den Hartog Date: Thu, 27 Dec 2018 15:44:26 -0600 Subject: [PATCH 10/17] added additional tests Signed-off-by: Kyle Den Hartog --- libindy/src/commands/crypto.rs | 47 +++++++++------- libindy/tests/crypto.rs | 97 +++++++++++++++++++++++++++++++++- 2 files changed, 123 insertions(+), 21 deletions(-) diff --git a/libindy/src/commands/crypto.rs b/libindy/src/commands/crypto.rs index 9ab86c91ec..2834267aee 100644 --- a/libindy/src/commands/crypto.rs +++ b/libindy/src/commands/crypto.rs @@ -385,12 +385,12 @@ impl CryptoCommandExecutor { encrypted_recipients_struct.push(Recipient { encrypted_key: base64::encode(enc_cek.as_slice()), header: Header { - kid: base64::encode(&their_vk.as_bytes()), + kid: their_vk, }, }); } // end for-loop - self._format_pack_message(encrypted_recipients_struct, message, &cek) + self._format_pack_message(encrypted_recipients_struct, message, &cek, false) } false => { //authcrypt @@ -398,7 +398,7 @@ impl CryptoCommandExecutor { let my_key = self.wallet_service.get_indy_object( wallet_handle, sender_vk, - &RecordOptions::id_value(), + &RecordOptions::id_value() )?; //encrypt cek for recipient @@ -411,12 +411,12 @@ impl CryptoCommandExecutor { encrypted_recipients_struct.push(Recipient { encrypted_key: base64::encode(enc_cek.as_slice()), header: Header { - kid: base64::encode(&their_vk.as_bytes()), + kid:their_vk, }, }); } // end for-loop - self._format_pack_message(encrypted_recipients_struct, message, &cek) + self._format_pack_message(encrypted_recipients_struct, message, &cek, true) } } } @@ -430,6 +430,7 @@ impl CryptoCommandExecutor { ))) })?; + //decode protected data let protected_decoded_vec = base64::decode(&jwe_struct.protected)?; let protected_decoded_str = String::from_utf8(protected_decoded_vec).map_err(|err| { @@ -450,15 +451,17 @@ impl CryptoCommandExecutor { //search through recipients_list and check if one of the kid matches a verkey in the wallet for recipient in protected_struct.recipients { - let key_in_wallet_result = self.wallet_service.get_indy_object::( - wallet_handle, - &recipient.header.kid, - &RecordOptions::id_value(), - ); - if key_in_wallet_result.is_ok() { - //TODO change to move this to a separate function and return recipient rather than - // putting logic inside the for loop. For loops have no way to return values in rust. + let my_key_res = self.wallet_service.get_indy_object( + wallet_handle, + &recipient.header.kid, + &RecordOptions::id_value() + ).map_err(|err| IndyError::WalletError(err)); + + + if my_key_res.is_ok() { + //TODO change to move this to a separate function and return recipient rather than putting logic inside the for loop. + // For loops have no way to return values in rust //decode encrypted_key let encrypted_key_vec = base64::decode(&recipient.encrypted_key)?; @@ -467,12 +470,9 @@ impl CryptoCommandExecutor { let (sender_verkey, cek) = match protected_struct.alg.as_ref() { "Authcrypt" => { + //get my key based on kid - let my_key = self.wallet_service.get_indy_object( - wallet_handle, - &recipient.header.kid, - &RecordOptions::id_value(), - )?; + let my_key = my_key_res.unwrap(); //decrypt cek let (sender_vk, cek_as_vec) = self @@ -554,12 +554,19 @@ impl CryptoCommandExecutor { encrypted_recipients_struct: Vec, message: Vec, cek: &chacha20poly1305_ietf::Key, + alg_is_authcrypt: bool ) -> Result> { + + let alg_val= match alg_is_authcrypt { + true => String::from("Authcrypt"), + false => String::from("Anoncrypt") + }; + //structure protected and base64URL encode it let protected_struct = Protected { enc: "xchacha20poly1305".to_string(), typ: "JWM/1.0".to_string(), - alg: "Authcrypt".to_string(), + alg: alg_val, recipients: encrypted_recipients_struct, }; let protected_encoded = serde_json::to_string(&protected_struct).map_err(|err| { @@ -572,7 +579,7 @@ impl CryptoCommandExecutor { let base64_protected = base64::encode(protected_encoded.as_bytes()); // encrypt ciphertext and integrity protect "protected" field - let (iv, ciphertext, tag) = + let (ciphertext, iv, tag) = self.crypto_service .encrypt_plaintext(message, &base64_protected, cek); diff --git a/libindy/tests/crypto.rs b/libindy/tests/crypto.rs index 04e93c8cd5..355d24651c 100644 --- a/libindy/tests/crypto.rs +++ b/libindy/tests/crypto.rs @@ -580,7 +580,102 @@ mod high_cases { } - mod unpack_message { + mod unpack_message_authcrypt { + use super::*; + + #[derive(Serialize, Deserialize, Debug, Clone, Eq, PartialEq)] + pub struct UnpackMessage { + pub message: String, + pub sender_verkey: String + } + + #[test] + fn indy_unpack_message_authcrypt_works() { + //Test setup + let (wallet_handle_sender, sender_verkey) = setup_with_key(); + let (wallet_handle_receiver, receiver_verkey) = setup_with_key(); + let rec_key_vec = vec![VERKEY_TRUSTEE, &receiver_verkey]; + let receiver_keys = serde_json::to_string(&rec_key_vec).unwrap(); + let message = "Hello World".as_bytes(); + let pack_message = crypto::pack_message(wallet_handle_sender, message, &receiver_keys, &sender_verkey).unwrap(); + + //execute function + let res = crypto::unpack_message(wallet_handle_receiver, pack_message.as_slice()).unwrap(); + let res_serialized : UnpackMessage = serde_json::from_slice(res.as_slice()).unwrap(); + + //verify unpack ran correctly + assert_eq!(res_serialized.message, "Hello World".to_string()); + assert_eq!(res_serialized.sender_verkey, sender_verkey); + + //teardown + utils::tear_down_with_wallet(wallet_handle_sender); + utils::tear_down_with_wallet(wallet_handle_receiver); + } + + #[test] + fn indy_unpack_message_authcrypt_fails_no_matching_key() { + //Test Setup + let (wallet_handle_sender, sender_verkey) = setup_with_key(); + let (wallet_handle_receiver, _) = setup_with_key(); + let rec_key_vec = vec![VERKEY_TRUSTEE]; + let receiver_keys = serde_json::to_string(&rec_key_vec).unwrap(); + let message = "Hello World".as_bytes(); + let pack_message = crypto::pack_message(wallet_handle_sender, message, &receiver_keys, &sender_verkey).unwrap(); + + //execute function + let res = crypto::unpack_message(wallet_handle_receiver, pack_message.as_slice()); + + assert_eq!(ErrorCode::WalletItemNotFound, res.unwrap_err()); + + utils::tear_down_with_wallet(wallet_handle_sender); + utils::tear_down_with_wallet(wallet_handle_receiver); + } + + } + + mod unpack_message_anoncrypt { + use super::*; + + #[derive(Serialize, Deserialize, Debug, Clone, Eq, PartialEq)] + pub struct UnpackMessage { + pub message: String + } + + #[test] + fn indy_unpack_message_anoncrypt_works() { + let (wallet_handle_sender, _) = setup_with_key(); + let (wallet_handle_receiver, receiver_verkey) = setup_with_key(); + let rec_key_vec = vec![VERKEY_TRUSTEE, &receiver_verkey]; + let receiver_keys = serde_json::to_string(&rec_key_vec).unwrap(); + let message = "Hello World".as_bytes(); + let pack_message = crypto::pack_message(wallet_handle_sender, message, &receiver_keys, "").unwrap(); + let res = crypto::unpack_message(wallet_handle_receiver, pack_message.as_slice()).unwrap(); + let res_serialized : UnpackMessage = serde_json::from_slice(res.as_slice()).unwrap(); + + assert_eq!(res_serialized.message, "Hello World".to_string()); + + utils::tear_down_with_wallet(wallet_handle_sender); + utils::tear_down_with_wallet(wallet_handle_receiver); + } + + #[test] + fn indy_unpack_message_anoncrypt_fails_no_matching_key() { + //Test Setup + let (wallet_handle_sender, _) = setup_with_key(); + let (wallet_handle_receiver, _) = setup_with_key(); + let rec_key_vec = vec![VERKEY_TRUSTEE]; + let receiver_keys = serde_json::to_string(&rec_key_vec).unwrap(); + let message = "Hello World".as_bytes(); + let pack_message = crypto::pack_message(wallet_handle_sender, message, &receiver_keys, "").unwrap(); + + //execute function + let res = crypto::unpack_message(wallet_handle_receiver, pack_message.as_slice()); + + assert_eq!(ErrorCode::WalletItemNotFound, res.unwrap_err()); + + utils::tear_down_with_wallet(wallet_handle_sender); + utils::tear_down_with_wallet(wallet_handle_receiver); + } } } From c43e0f3e947a641f0fc4fba985cdab100f223151 Mon Sep 17 00:00:00 2001 From: Andrey Kononykhin Date: Fri, 28 Dec 2018 12:03:39 +0300 Subject: [PATCH 11/17] adds workaround for IS-1127 issue Signed-off-by: Andrey Kononykhin --- Jenkinsfile.cd | 6 ++++++ Jenkinsfile.ci | 6 ++++++ 2 files changed, 12 insertions(+) diff --git a/Jenkinsfile.cd b/Jenkinsfile.cd index c745b9ef64..b69452974a 100644 --- a/Jenkinsfile.cd +++ b/Jenkinsfile.cd @@ -598,6 +598,12 @@ def windowsTesting() { dir('vcx/libvcx') { echo "Windows Libvcx Test: Build" + + // FIXME remove that once https://jira.hyperledger.org/browse/IS-1127 is resolved + bat """ + PowerShell.exe "&{(Get-Content -Path src/settings.rs -Raw) -replace '/tmp/test_init.json','./test_init.json'} | Set-Content -Path src/settings.rs" + """ + withEnv([ "OPENSSL_DIR=$WORKSPACE\\libindy\\prebuilt", "RUST_BACKTRACE=1" diff --git a/Jenkinsfile.ci b/Jenkinsfile.ci index dd72dcd827..56011f1120 100644 --- a/Jenkinsfile.ci +++ b/Jenkinsfile.ci @@ -112,6 +112,12 @@ def windowsTesting() { dir('vcx/libvcx') { echo "Windows Libvcx Test: Build" + + // FIXME remove that once https://jira.hyperledger.org/browse/IS-1127 is resolved + bat """ + PowerShell.exe "&{(Get-Content -Path src/settings.rs -Raw) -replace '/tmp/test_init.json','./test_init.json'} | Set-Content -Path src/settings.rs" + """ + withEnv([ "OPENSSL_DIR=$WORKSPACE\\libindy\\prebuilt", "RUST_BACKTRACE=1" From 5b70d3a696975909e83ddd7ea19d5f7f8ebdd61e Mon Sep 17 00:00:00 2001 From: John Jordan Date: Sun, 30 Dec 2018 15:29:58 -0500 Subject: [PATCH 12/17] small typo in README Signed-off-by: John Jordan --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 59261077e9..620044fa87 100644 --- a/README.md +++ b/README.md @@ -32,7 +32,7 @@ rewritten but still contains some useful ideas. The major artifact of the SDK is a C-callable library that provides the basic building blocks for the creation of applications on the top of [Hyperledger Indy](https://www.hyperledger.org/projects/hyperledger-indy). -It is available for most popular desktop, mobile and server platfrorms. +It is available for most popular desktop, mobile and server platforms. ### Libindy wrappers From cc26fd684f4e767d109f4cf0fed05cedc772eec4 Mon Sep 17 00:00:00 2001 From: Dan Farnsworth Date: Mon, 7 Jan 2019 15:42:53 -0700 Subject: [PATCH 13/17] IS-1115 - Adjust python wrapper namespacing Signed-off-by: Dan Farnsworth --- wrappers/python/indy/__init__.py | 25 +++++++++++++++++++++++++ 1 file changed, 25 insertions(+) diff --git a/wrappers/python/indy/__init__.py b/wrappers/python/indy/__init__.py index d68cac3eca..657cb31903 100644 --- a/wrappers/python/indy/__init__.py +++ b/wrappers/python/indy/__init__.py @@ -1 +1,26 @@ from indy.error import IndyError +from indy import anoncreds +from indy import blob_storage +from indy import crypto +from indy import did +from indy import ledger +from indy import libindy +from indy import non_secrets +from indy import pairwise +from indy import payment +from indy import pool +from indy import wallet + +__all__ = [ + 'anoncreds', + 'blob_storage', + 'crypto', + 'did', + 'ledger', + 'libindy', + 'non_secrets', + 'pairwise', + 'payment', + 'pool', + 'wallet' +] From c88d8db11484e077bbef82c7a26742982e0c8fc9 Mon Sep 17 00:00:00 2001 From: "artem.ivanov" Date: Wed, 9 Jan 2019 11:09:44 +0300 Subject: [PATCH 14/17] Added restriction for the max acceptable number of schema attributes Signed-off-by: artem.ivanov --- cli/src/commands/ledger.rs | 2 +- libindy/include/indy_ledger.h | 2 +- libindy/src/api/anoncreds.rs | 2 +- libindy/src/api/ledger.rs | 2 +- libindy/src/commands/anoncreds/issuer.rs | 8 +++++- libindy/src/domain/anoncreds/schema.rs | 1 + libindy/src/services/ledger/mod.rs | 25 ++++++++++++++++++- libindy/tests/anoncreds.rs | 13 +++++++++- vcx/libvcx/src/api/schema.rs | 2 +- vcx/wrappers/node/src/api/schema.ts | 2 +- vcx/wrappers/python3/vcx/api/schema.py | 2 +- .../libindy-pod/Indy/Wrapper/IndyAnoncreds.h | 2 +- .../ios/libindy-pod/Indy/Wrapper/IndyLedger.h | 2 +- .../indy/sdk/anoncreds/Anoncreds.java | 2 +- .../hyperledger/indy/sdk/ledger/Ledger.java | 2 +- wrappers/nodejs/README.md | 4 +-- wrappers/python/indy/anoncreds.py | 2 +- wrappers/python/indy/ledger.py | 2 +- wrappers/rust/src/anoncreds.rs | 2 +- wrappers/rust/src/ledger.rs | 2 +- 20 files changed, 61 insertions(+), 20 deletions(-) diff --git a/cli/src/commands/ledger.rs b/cli/src/commands/ledger.rs index ee872d6f58..4ac7d27908 100644 --- a/cli/src/commands/ledger.rs +++ b/cli/src/commands/ledger.rs @@ -271,7 +271,7 @@ pub mod schema_command { command!(CommandMetadata::build("schema", "Send Schema transaction to the Ledger.") .add_required_param("name", "Schema name") .add_required_param("version", "Schema version") - .add_required_param("attr_names", "Schema attributes split by comma") + .add_required_param("attr_names", "Schema attributes split by comma (the number of attributes should be less or equal than 150)") .add_optional_param("fees_inputs","The list of source inputs") .add_optional_param("fees_outputs","The list of outputs in the following format: (recipient, amount)") .add_optional_param("extra","Optional information for fees payment operation") diff --git a/libindy/include/indy_ledger.h b/libindy/include/indy_ledger.h index 383ff35ed6..4500e3c435 100644 --- a/libindy/include/indy_ledger.h +++ b/libindy/include/indy_ledger.h @@ -313,7 +313,7 @@ extern "C" { /// data: Credential schema. /// { /// id: identifier of schema - /// attrNames: array of attribute name strings + /// attrNames: array of attribute name strings (the number of attributes should be less or equal than 150) /// name: Schema's name string /// version: Schema's version string, /// ver: Version of the Schema json diff --git a/libindy/src/api/anoncreds.rs b/libindy/src/api/anoncreds.rs index 2707f0b338..1e7a8e6e5f 100644 --- a/libindy/src/api/anoncreds.rs +++ b/libindy/src/api/anoncreds.rs @@ -42,7 +42,7 @@ use std::collections::HashMap; /// issuer_did: DID of schema issuer /// name: a name the schema /// version: a version of the schema -/// attrs: a list of schema attributes descriptions +/// attrs: a list of schema attributes descriptions (the number of attributes should be less or equal than 150) /// cb: Callback that takes command result as parameter /// /// #Returns diff --git a/libindy/src/api/ledger.rs b/libindy/src/api/ledger.rs index b9a53f81f6..2fdb3261ea 100644 --- a/libindy/src/api/ledger.rs +++ b/libindy/src/api/ledger.rs @@ -594,7 +594,7 @@ pub extern fn indy_build_get_attrib_request(command_handle: IndyHandle, /// data: Credential schema. /// { /// id: identifier of schema -/// attrNames: array of attribute name strings +/// attrNames: array of attribute name strings (the number of attributes should be less or equal than 150) /// name: Schema's name string /// version: Schema's version string, /// ver: Version of the Schema json diff --git a/libindy/src/commands/anoncreds/issuer.rs b/libindy/src/commands/anoncreds/issuer.rs index 4e91965b8f..7e9eac4d30 100644 --- a/libindy/src/commands/anoncreds/issuer.rs +++ b/libindy/src/commands/anoncreds/issuer.rs @@ -23,7 +23,7 @@ use self::indy_crypto::cl::{ }; use super::tails::{SDKTailsAccessor, store_tails_from_generator}; -use domain::anoncreds::schema::{Schema, SchemaV1, AttributeNames}; +use domain::anoncreds::schema::{Schema, SchemaV1, AttributeNames, MAX_ATTRIBUTES_COUNT}; use domain::anoncreds::credential_definition::{ CredentialDefinition, CredentialDefinitionV1, @@ -219,6 +219,12 @@ impl IssuerCommandExecutor { self.crypto_service.validate_did(issuer_did)?; + if attrs.len() > MAX_ATTRIBUTES_COUNT { + return Err(IndyError::CommonError( + CommonError::InvalidStructure( + format!("The number of Schema attributes {} cannot be greater than {}", attrs.len(), MAX_ATTRIBUTES_COUNT)))); + } + let schema_id = Schema::schema_id(issuer_did, name, version); let schema = Schema::SchemaV1(SchemaV1 { diff --git a/libindy/src/domain/anoncreds/schema.rs b/libindy/src/domain/anoncreds/schema.rs index a2588b248f..1f1f207cad 100644 --- a/libindy/src/domain/anoncreds/schema.rs +++ b/libindy/src/domain/anoncreds/schema.rs @@ -3,6 +3,7 @@ use super::DELIMITER; use std::collections::{HashMap, HashSet}; pub const SCHEMA_MARKER: &'static str = "2"; +pub const MAX_ATTRIBUTES_COUNT: usize = 150; #[derive(Debug, Serialize, Deserialize, Clone)] #[serde(rename_all = "camelCase")] diff --git a/libindy/src/services/ledger/mod.rs b/libindy/src/services/ledger/mod.rs index 31ef1a728e..daaebb4fa9 100644 --- a/libindy/src/services/ledger/mod.rs +++ b/libindy/src/services/ledger/mod.rs @@ -20,7 +20,7 @@ use domain::anoncreds::DELIMITER; use domain::anoncreds::revocation_registry_definition::{RevocationRegistryDefinition, RevocationRegistryDefinitionV1}; use domain::anoncreds::revocation_registry::RevocationRegistry; use domain::anoncreds::revocation_registry_delta::{RevocationRegistryDelta, RevocationRegistryDeltaV1}; -use domain::anoncreds::schema::{Schema, SchemaV1}; +use domain::anoncreds::schema::{Schema, SchemaV1, MAX_ATTRIBUTES_COUNT}; use domain::anoncreds::credential_definition::{CredentialDefinition, CredentialDefinitionV1}; use indy_crypto::cl::RevocationRegistryDelta as CryproRevocationRegistryDelta; @@ -149,6 +149,11 @@ impl LedgerService { pub fn build_schema_request(&self, identifier: &str, schema: SchemaV1) -> Result { info!("build_schema_request >>> identifier: {:?}, schema: {:?}", identifier, schema); + if schema.attr_names.len() > MAX_ATTRIBUTES_COUNT { + return Err(CommonError::InvalidStructure( + format!("The number of Schema attributes {} cannot be greater than {}", schema.attr_names.len(), MAX_ATTRIBUTES_COUNT))); + } + let schema_data = SchemaOperationData::new(schema.name, schema.version, schema.attr_names); let operation = SchemaOperation::new(schema_data); @@ -805,6 +810,24 @@ mod tests { check_request(&request, expected_result); } + #[test] + fn build_schema_request_works_for_attrs_count_more_than_acceptable() { + let ledger_service = LedgerService::new(); + + let attr_names: AttributeNames = (0..MAX_ATTRIBUTES_COUNT + 1).map(|i| i.to_string()).collect(); + + let data = SchemaV1 { + id: Schema::schema_id(IDENTIFIER, "name", "1.0"), + name: "name".to_string(), + version: "1.0".to_string(), + attr_names, + seq_no: None, + }; + + let res = ledger_service.build_schema_request(IDENTIFIER, data); + assert_match!(Err(CommonError::InvalidStructure(_)), res); + } + #[test] fn build_get_schema_request_works_for_invalid_id() { let ledger_service = LedgerService::new(); diff --git a/libindy/tests/anoncreds.rs b/libindy/tests/anoncreds.rs index 8fdb895736..5c85191770 100644 --- a/libindy/tests/anoncreds.rs +++ b/libindy/tests/anoncreds.rs @@ -34,7 +34,7 @@ use indy::ErrorCode; use utils::inmem_wallet::InmemWallet; use utils::constants::*; -use utils::domain::anoncreds::schema::Schema; +use utils::domain::anoncreds::schema::{Schema, AttributeNames, MAX_ATTRIBUTES_COUNT}; use utils::domain::anoncreds::credential_definition::CredentialDefinition; use utils::domain::anoncreds::revocation_registry_definition::RevocationRegistryDefinition; use utils::domain::anoncreds::credential::CredentialInfo; @@ -3146,6 +3146,17 @@ mod medium_cases { GVT_SCHEMA_ATTRIBUTES); assert_eq!(res.unwrap_err(), ErrorCode::CommonInvalidStructure); } + + #[test] + fn issuer_create_schema_works_for_attrs_count_more_than_acceptable() { + let attr_names: AttributeNames = (0..MAX_ATTRIBUTES_COUNT + 1).map(|i| i.to_string()).collect(); + + let res = anoncreds::issuer_create_schema(ISSUER_DID, + GVT_SCHEMA_NAME, + SCHEMA_VERSION, + &serde_json::to_string(&attr_names).unwrap()); + assert_eq!(res.unwrap_err(), ErrorCode::CommonInvalidStructure); + } } mod issuer_create_and_store_credential_def { diff --git a/vcx/libvcx/src/api/schema.rs b/vcx/libvcx/src/api/schema.rs index aa23bcbd55..dcba28a3e0 100644 --- a/vcx/libvcx/src/api/schema.rs +++ b/vcx/libvcx/src/api/schema.rs @@ -22,7 +22,7 @@ use utils::threadpool::spawn; /// /// version: version of schema /// -/// schema_data: list of attributes that will make up the schema +/// schema_data: list of attributes that will make up the schema (the number of attributes should be less or equal than 150) /// /// # Example schema_data -> "["attr1", "attr2", "attr3"]" /// diff --git a/vcx/wrappers/node/src/api/schema.ts b/vcx/wrappers/node/src/api/schema.ts index 944e895923..64e99e7381 100644 --- a/vcx/wrappers/node/src/api/schema.ts +++ b/vcx/wrappers/node/src/api/schema.ts @@ -18,7 +18,7 @@ export interface ISchemaCreateData { * @description * name: name of schema * version: - * attrNames: a list of named attribtes inteded to be added to the schema + * attrNames: a list of named attribtes inteded to be added to the schema (the number of attributes should be less or equal than 150) */ export interface ISchemaAttrs { name: string, diff --git a/vcx/wrappers/python3/vcx/api/schema.py b/vcx/wrappers/python3/vcx/api/schema.py index 56e0c1eabc..267ab011de 100644 --- a/vcx/wrappers/python3/vcx/api/schema.py +++ b/vcx/wrappers/python3/vcx/api/schema.py @@ -13,7 +13,7 @@ class Schema(VcxBase): Attributes: source_id: user generated unique identifier schema_id: the ledger ID of the schema - attrs: attribute/value pairs + attrs: attribute/value pairs (the number of attributes should be less or equal than 150) version: version of the schema """ diff --git a/wrappers/ios/libindy-pod/Indy/Wrapper/IndyAnoncreds.h b/wrappers/ios/libindy-pod/Indy/Wrapper/IndyAnoncreds.h index 56dc1aa4d5..30ca0574db 100644 --- a/wrappers/ios/libindy-pod/Indy/Wrapper/IndyAnoncreds.h +++ b/wrappers/ios/libindy-pod/Indy/Wrapper/IndyAnoncreds.h @@ -23,7 +23,7 @@ @param issuerDID DID of schema issuer @param name a name the schema @param version a version of the schema - @param attrs a list of schema attributes descriptions + @param attrs a list of schema attributes descriptions (the number of attributes should be less or equal than 150) @param completion Callback that takes command result as parameter. Returns: schemaId: identifier of created schema diff --git a/wrappers/ios/libindy-pod/Indy/Wrapper/IndyLedger.h b/wrappers/ios/libindy-pod/Indy/Wrapper/IndyLedger.h index aa56d2459d..85d2e1f61b 100644 --- a/wrappers/ios/libindy-pod/Indy/Wrapper/IndyLedger.h +++ b/wrappers/ios/libindy-pod/Indy/Wrapper/IndyLedger.h @@ -185,7 +185,7 @@ @param data Credential schema. { id: identifier of schema - attrNames: array of attribute name strings + attrNames: array of attribute name strings (the number of attributes should be less or equal than 150) name: Schema's name string version: Schema's version string, ver: Version of the Schema json diff --git a/wrappers/java/src/main/java/org/hyperledger/indy/sdk/anoncreds/Anoncreds.java b/wrappers/java/src/main/java/org/hyperledger/indy/sdk/anoncreds/Anoncreds.java index 1e2e51c71e..e17d995674 100644 --- a/wrappers/java/src/main/java/org/hyperledger/indy/sdk/anoncreds/Anoncreds.java +++ b/wrappers/java/src/main/java/org/hyperledger/indy/sdk/anoncreds/Anoncreds.java @@ -164,7 +164,7 @@ public void callback(int xcommand_handle, int err, Boolean valid) { * @param issuerDid The DID of the issuer. * @param name Human-readable name of schema. * @param version Version of schema. - * @param attrs: List of schema attributes descriptions + * @param attrs: List of schema attributes descriptions (the number of attributes should be less or equal than 150) * @return A future resolving to IssuerCreateSchemaResult object containing: * schemaId: identifier of created schema * schemaJson: schema as json diff --git a/wrappers/java/src/main/java/org/hyperledger/indy/sdk/ledger/Ledger.java b/wrappers/java/src/main/java/org/hyperledger/indy/sdk/ledger/Ledger.java index 86bdd613e3..ed8ca61056 100644 --- a/wrappers/java/src/main/java/org/hyperledger/indy/sdk/ledger/Ledger.java +++ b/wrappers/java/src/main/java/org/hyperledger/indy/sdk/ledger/Ledger.java @@ -511,7 +511,7 @@ public static CompletableFuture buildGetNymRequest( * @param data Credential schema. * { * id: identifier of schema - * attrNames: array of attribute name strings + * attrNames: array of attribute name strings (the number of attributes should be less or equal than 150) * name: Schema's name string * version: Schema's version string, * ver: Version of the Schema json diff --git a/wrappers/nodejs/README.md b/wrappers/nodejs/README.md index 2196809f54..c63f1e34cd 100644 --- a/wrappers/nodejs/README.md +++ b/wrappers/nodejs/README.md @@ -110,7 +110,7 @@ After that can call issuerCreateAndStoreCredentialDef to build corresponding Cre * `issuerDid`: String - DID of schema issuer * `name`: String - a name the schema * `version`: String - a version of the schema -* `attrNames`: Json +* `attrNames`: Json - a list of schema attributes descriptions (the number of attributes should be less or equal than 150) * __->__ [ `id`: String, `schema`: Json ] - schema\_id: identifier of created schema schema\_json: schema as json @@ -1328,7 +1328,7 @@ Builds a SCHEMA request. Request to add Credential's schema. { id: identifier of schema attrNames: array of attribute name strings - name: Schema's name string + name: Schema's name string (the number of attributes should be less or equal than 150) version: Schema's version string, ver: Version of the Schema json } diff --git a/wrappers/python/indy/anoncreds.py b/wrappers/python/indy/anoncreds.py index aa8cd7d6b4..bb461e8404 100644 --- a/wrappers/python/indy/anoncreds.py +++ b/wrappers/python/indy/anoncreds.py @@ -24,7 +24,7 @@ async def issuer_create_schema(issuer_did: str, :param issuer_did: DID of schema issuer :param name: a name the schema :param version: a version of the schema - :param attrs: a list of schema attributes descriptions + :param attrs: a list of schema attributes descriptions (the number of attributes should be less or equal than 150) :return: schema_id: identifier of created schema schema_json: schema as json diff --git a/wrappers/python/indy/ledger.py b/wrappers/python/indy/ledger.py index 78e4e4a65a..e558b70149 100644 --- a/wrappers/python/indy/ledger.py +++ b/wrappers/python/indy/ledger.py @@ -433,7 +433,7 @@ async def build_schema_request(submitter_did: str, :param data: Credential schema. { id: identifier of schema - attrNames: array of attribute name strings + attrNames: array of attribute name strings (the number of attributes should be less or equal than 150) name: Schema's name string version: Schema's version string, ver: Version of the Schema json diff --git a/wrappers/rust/src/anoncreds.rs b/wrappers/rust/src/anoncreds.rs index c29270f8ad..ff510dec78 100644 --- a/wrappers/rust/src/anoncreds.rs +++ b/wrappers/rust/src/anoncreds.rs @@ -31,7 +31,7 @@ use ffi::{ResponseStringStringCB, /// * `issuer_did`: DID of schema issuer /// * `name`: a name the schema /// * `version`: a version of the schema -/// * `attrs`: a list of schema attributes descriptions +/// * `attrs`: a list of schema attributes descriptions (the number of attributes should be less or equal than 150) /// /// # Returns /// * `schema_id`: identifier of created schema diff --git a/wrappers/rust/src/ledger.rs b/wrappers/rust/src/ledger.rs index fba2fe45d0..205ae3f648 100644 --- a/wrappers/rust/src/ledger.rs +++ b/wrappers/rust/src/ledger.rs @@ -348,7 +348,7 @@ fn _build_get_attrib_request(command_handle: IndyHandle, submitter_did: Option<& /// * `data` - Credential schema. /// { /// id: identifier of schema -/// attrNames: array of attribute name strings +/// attrNames: array of attribute name strings (the number of attributes should be less or equal than 150) /// name: Schema's name string /// version: Schema's version string, /// ver: Version of the Schema json From ceb2a55fe0a2ca21579ea2ce65bdb2ce2a358b45 Mon Sep 17 00:00:00 2001 From: "artem.ivanov" Date: Wed, 9 Jan 2019 16:15:31 +0300 Subject: [PATCH 15/17] Changed pytest version for VCX python wrapper testing Signed-off-by: artem.ivanov --- Jenkinsfile.cd | 5 ++--- Jenkinsfile.ci | 2 +- 2 files changed, 3 insertions(+), 4 deletions(-) diff --git a/Jenkinsfile.cd b/Jenkinsfile.cd index 6fcf994c8c..8f0ab258c9 100644 --- a/Jenkinsfile.cd +++ b/Jenkinsfile.cd @@ -481,14 +481,13 @@ def linuxVcxJavaTesting(env_name, network_name, testEnv, stashBuildResults) { def linuxVcxPythonTesting(env_name, network_name, testEnv, stashBuildResults) { unstash name: "VcxPythonLibvcxSO${env_name}" unstash name: "VcxPythonLibindyAndLibnullpaySO${env_name}" + dir('vcx/wrappers/python3') { - - testEnv.inside { echo "${env_name} Vcx Test: Test python wrapper" sh ''' - python3.5 -m pip install --user pytest==3.4.2 qrcode pytest-asyncio + python3.5 -m pip install --user pytest==3.6.4 pytest-asyncio LD_LIBRARY_PATH=./:${LD_LIBRARY_PATH} python3.5 -m pytest -s ''' } diff --git a/Jenkinsfile.ci b/Jenkinsfile.ci index 3cb3227a65..9b6af5b6e6 100644 --- a/Jenkinsfile.ci +++ b/Jenkinsfile.ci @@ -622,7 +622,7 @@ def linuxVcxPythonTesting(env_name, network_name, testEnv) { echo "${env_name} Vcx Test: Test python wrapper" sh ''' - python3.5 -m pip install --user pytest==3.4.2 qrcode pytest-asyncio + python3.5 -m pip install --user pytest==3.6.4 pytest-asyncio LD_LIBRARY_PATH=./:${LD_LIBRARY_PATH} python3.5 -m pytest -s ''' } From 866ecc6b8143cbeee999489da07eeebc18550e0b Mon Sep 17 00:00:00 2001 From: "artem.ivanov" Date: Wed, 9 Jan 2019 18:34:51 +0300 Subject: [PATCH 16/17] Changed max value from 150 on 125 Signed-off-by: artem.ivanov --- cli/src/commands/ledger.rs | 2 +- libindy/include/indy_ledger.h | 2 +- libindy/src/api/anoncreds.rs | 2 +- libindy/src/api/ledger.rs | 2 +- libindy/src/domain/anoncreds/schema.rs | 2 +- vcx/libvcx/src/api/schema.rs | 2 +- vcx/wrappers/node/src/api/schema.ts | 2 +- vcx/wrappers/python3/vcx/api/schema.py | 2 +- wrappers/ios/libindy-pod/Indy/Wrapper/IndyAnoncreds.h | 2 +- wrappers/ios/libindy-pod/Indy/Wrapper/IndyLedger.h | 2 +- .../java/org/hyperledger/indy/sdk/anoncreds/Anoncreds.java | 2 +- .../src/main/java/org/hyperledger/indy/sdk/ledger/Ledger.java | 2 +- wrappers/nodejs/README.md | 4 ++-- wrappers/python/indy/anoncreds.py | 2 +- wrappers/python/indy/ledger.py | 2 +- wrappers/rust/src/anoncreds.rs | 2 +- wrappers/rust/src/ledger.rs | 2 +- 17 files changed, 18 insertions(+), 18 deletions(-) diff --git a/cli/src/commands/ledger.rs b/cli/src/commands/ledger.rs index 4ac7d27908..fded7d2004 100644 --- a/cli/src/commands/ledger.rs +++ b/cli/src/commands/ledger.rs @@ -271,7 +271,7 @@ pub mod schema_command { command!(CommandMetadata::build("schema", "Send Schema transaction to the Ledger.") .add_required_param("name", "Schema name") .add_required_param("version", "Schema version") - .add_required_param("attr_names", "Schema attributes split by comma (the number of attributes should be less or equal than 150)") + .add_required_param("attr_names", "Schema attributes split by comma (the number of attributes should be less or equal than 125)") .add_optional_param("fees_inputs","The list of source inputs") .add_optional_param("fees_outputs","The list of outputs in the following format: (recipient, amount)") .add_optional_param("extra","Optional information for fees payment operation") diff --git a/libindy/include/indy_ledger.h b/libindy/include/indy_ledger.h index 4500e3c435..d2a511ab06 100644 --- a/libindy/include/indy_ledger.h +++ b/libindy/include/indy_ledger.h @@ -313,7 +313,7 @@ extern "C" { /// data: Credential schema. /// { /// id: identifier of schema - /// attrNames: array of attribute name strings (the number of attributes should be less or equal than 150) + /// attrNames: array of attribute name strings (the number of attributes should be less or equal than 125) /// name: Schema's name string /// version: Schema's version string, /// ver: Version of the Schema json diff --git a/libindy/src/api/anoncreds.rs b/libindy/src/api/anoncreds.rs index 1e7a8e6e5f..ee4ac96107 100644 --- a/libindy/src/api/anoncreds.rs +++ b/libindy/src/api/anoncreds.rs @@ -42,7 +42,7 @@ use std::collections::HashMap; /// issuer_did: DID of schema issuer /// name: a name the schema /// version: a version of the schema -/// attrs: a list of schema attributes descriptions (the number of attributes should be less or equal than 150) +/// attrs: a list of schema attributes descriptions (the number of attributes should be less or equal than 125) /// cb: Callback that takes command result as parameter /// /// #Returns diff --git a/libindy/src/api/ledger.rs b/libindy/src/api/ledger.rs index 2fdb3261ea..df1ef5b654 100644 --- a/libindy/src/api/ledger.rs +++ b/libindy/src/api/ledger.rs @@ -594,7 +594,7 @@ pub extern fn indy_build_get_attrib_request(command_handle: IndyHandle, /// data: Credential schema. /// { /// id: identifier of schema -/// attrNames: array of attribute name strings (the number of attributes should be less or equal than 150) +/// attrNames: array of attribute name strings (the number of attributes should be less or equal than 125) /// name: Schema's name string /// version: Schema's version string, /// ver: Version of the Schema json diff --git a/libindy/src/domain/anoncreds/schema.rs b/libindy/src/domain/anoncreds/schema.rs index 1f1f207cad..7b453338bc 100644 --- a/libindy/src/domain/anoncreds/schema.rs +++ b/libindy/src/domain/anoncreds/schema.rs @@ -3,7 +3,7 @@ use super::DELIMITER; use std::collections::{HashMap, HashSet}; pub const SCHEMA_MARKER: &'static str = "2"; -pub const MAX_ATTRIBUTES_COUNT: usize = 150; +pub const MAX_ATTRIBUTES_COUNT: usize = 125; #[derive(Debug, Serialize, Deserialize, Clone)] #[serde(rename_all = "camelCase")] diff --git a/vcx/libvcx/src/api/schema.rs b/vcx/libvcx/src/api/schema.rs index dcba28a3e0..af67f83c78 100644 --- a/vcx/libvcx/src/api/schema.rs +++ b/vcx/libvcx/src/api/schema.rs @@ -22,7 +22,7 @@ use utils::threadpool::spawn; /// /// version: version of schema /// -/// schema_data: list of attributes that will make up the schema (the number of attributes should be less or equal than 150) +/// schema_data: list of attributes that will make up the schema (the number of attributes should be less or equal than 125) /// /// # Example schema_data -> "["attr1", "attr2", "attr3"]" /// diff --git a/vcx/wrappers/node/src/api/schema.ts b/vcx/wrappers/node/src/api/schema.ts index 64e99e7381..93360c4e4c 100644 --- a/vcx/wrappers/node/src/api/schema.ts +++ b/vcx/wrappers/node/src/api/schema.ts @@ -18,7 +18,7 @@ export interface ISchemaCreateData { * @description * name: name of schema * version: - * attrNames: a list of named attribtes inteded to be added to the schema (the number of attributes should be less or equal than 150) + * attrNames: a list of named attribtes inteded to be added to the schema (the number of attributes should be less or equal than 125) */ export interface ISchemaAttrs { name: string, diff --git a/vcx/wrappers/python3/vcx/api/schema.py b/vcx/wrappers/python3/vcx/api/schema.py index 267ab011de..a27edb8720 100644 --- a/vcx/wrappers/python3/vcx/api/schema.py +++ b/vcx/wrappers/python3/vcx/api/schema.py @@ -13,7 +13,7 @@ class Schema(VcxBase): Attributes: source_id: user generated unique identifier schema_id: the ledger ID of the schema - attrs: attribute/value pairs (the number of attributes should be less or equal than 150) + attrs: attribute/value pairs (the number of attributes should be less or equal than 125) version: version of the schema """ diff --git a/wrappers/ios/libindy-pod/Indy/Wrapper/IndyAnoncreds.h b/wrappers/ios/libindy-pod/Indy/Wrapper/IndyAnoncreds.h index 30ca0574db..c1426cb1f1 100644 --- a/wrappers/ios/libindy-pod/Indy/Wrapper/IndyAnoncreds.h +++ b/wrappers/ios/libindy-pod/Indy/Wrapper/IndyAnoncreds.h @@ -23,7 +23,7 @@ @param issuerDID DID of schema issuer @param name a name the schema @param version a version of the schema - @param attrs a list of schema attributes descriptions (the number of attributes should be less or equal than 150) + @param attrs a list of schema attributes descriptions (the number of attributes should be less or equal than 125) @param completion Callback that takes command result as parameter. Returns: schemaId: identifier of created schema diff --git a/wrappers/ios/libindy-pod/Indy/Wrapper/IndyLedger.h b/wrappers/ios/libindy-pod/Indy/Wrapper/IndyLedger.h index 85d2e1f61b..0d7d395c01 100644 --- a/wrappers/ios/libindy-pod/Indy/Wrapper/IndyLedger.h +++ b/wrappers/ios/libindy-pod/Indy/Wrapper/IndyLedger.h @@ -185,7 +185,7 @@ @param data Credential schema. { id: identifier of schema - attrNames: array of attribute name strings (the number of attributes should be less or equal than 150) + attrNames: array of attribute name strings (the number of attributes should be less or equal than 125) name: Schema's name string version: Schema's version string, ver: Version of the Schema json diff --git a/wrappers/java/src/main/java/org/hyperledger/indy/sdk/anoncreds/Anoncreds.java b/wrappers/java/src/main/java/org/hyperledger/indy/sdk/anoncreds/Anoncreds.java index e17d995674..3aff9384f5 100644 --- a/wrappers/java/src/main/java/org/hyperledger/indy/sdk/anoncreds/Anoncreds.java +++ b/wrappers/java/src/main/java/org/hyperledger/indy/sdk/anoncreds/Anoncreds.java @@ -164,7 +164,7 @@ public void callback(int xcommand_handle, int err, Boolean valid) { * @param issuerDid The DID of the issuer. * @param name Human-readable name of schema. * @param version Version of schema. - * @param attrs: List of schema attributes descriptions (the number of attributes should be less or equal than 150) + * @param attrs: List of schema attributes descriptions (the number of attributes should be less or equal than 125) * @return A future resolving to IssuerCreateSchemaResult object containing: * schemaId: identifier of created schema * schemaJson: schema as json diff --git a/wrappers/java/src/main/java/org/hyperledger/indy/sdk/ledger/Ledger.java b/wrappers/java/src/main/java/org/hyperledger/indy/sdk/ledger/Ledger.java index ed8ca61056..dbc2e84d2e 100644 --- a/wrappers/java/src/main/java/org/hyperledger/indy/sdk/ledger/Ledger.java +++ b/wrappers/java/src/main/java/org/hyperledger/indy/sdk/ledger/Ledger.java @@ -511,7 +511,7 @@ public static CompletableFuture buildGetNymRequest( * @param data Credential schema. * { * id: identifier of schema - * attrNames: array of attribute name strings (the number of attributes should be less or equal than 150) + * attrNames: array of attribute name strings (the number of attributes should be less or equal than 125) * name: Schema's name string * version: Schema's version string, * ver: Version of the Schema json diff --git a/wrappers/nodejs/README.md b/wrappers/nodejs/README.md index c63f1e34cd..b2673bde55 100644 --- a/wrappers/nodejs/README.md +++ b/wrappers/nodejs/README.md @@ -110,7 +110,7 @@ After that can call issuerCreateAndStoreCredentialDef to build corresponding Cre * `issuerDid`: String - DID of schema issuer * `name`: String - a name the schema * `version`: String - a version of the schema -* `attrNames`: Json - a list of schema attributes descriptions (the number of attributes should be less or equal than 150) +* `attrNames`: Json - a list of schema attributes descriptions (the number of attributes should be less or equal than 125) * __->__ [ `id`: String, `schema`: Json ] - schema\_id: identifier of created schema schema\_json: schema as json @@ -1328,7 +1328,7 @@ Builds a SCHEMA request. Request to add Credential's schema. { id: identifier of schema attrNames: array of attribute name strings - name: Schema's name string (the number of attributes should be less or equal than 150) + name: Schema's name string (the number of attributes should be less or equal than 125) version: Schema's version string, ver: Version of the Schema json } diff --git a/wrappers/python/indy/anoncreds.py b/wrappers/python/indy/anoncreds.py index bb461e8404..d954a46c0f 100644 --- a/wrappers/python/indy/anoncreds.py +++ b/wrappers/python/indy/anoncreds.py @@ -24,7 +24,7 @@ async def issuer_create_schema(issuer_did: str, :param issuer_did: DID of schema issuer :param name: a name the schema :param version: a version of the schema - :param attrs: a list of schema attributes descriptions (the number of attributes should be less or equal than 150) + :param attrs: a list of schema attributes descriptions (the number of attributes should be less or equal than 125) :return: schema_id: identifier of created schema schema_json: schema as json diff --git a/wrappers/python/indy/ledger.py b/wrappers/python/indy/ledger.py index e558b70149..6e09a02bf4 100644 --- a/wrappers/python/indy/ledger.py +++ b/wrappers/python/indy/ledger.py @@ -433,7 +433,7 @@ async def build_schema_request(submitter_did: str, :param data: Credential schema. { id: identifier of schema - attrNames: array of attribute name strings (the number of attributes should be less or equal than 150) + attrNames: array of attribute name strings (the number of attributes should be less or equal than 125) name: Schema's name string version: Schema's version string, ver: Version of the Schema json diff --git a/wrappers/rust/src/anoncreds.rs b/wrappers/rust/src/anoncreds.rs index ff510dec78..51d4acad4c 100644 --- a/wrappers/rust/src/anoncreds.rs +++ b/wrappers/rust/src/anoncreds.rs @@ -31,7 +31,7 @@ use ffi::{ResponseStringStringCB, /// * `issuer_did`: DID of schema issuer /// * `name`: a name the schema /// * `version`: a version of the schema -/// * `attrs`: a list of schema attributes descriptions (the number of attributes should be less or equal than 150) +/// * `attrs`: a list of schema attributes descriptions (the number of attributes should be less or equal than 125) /// /// # Returns /// * `schema_id`: identifier of created schema diff --git a/wrappers/rust/src/ledger.rs b/wrappers/rust/src/ledger.rs index 205ae3f648..09322b29d8 100644 --- a/wrappers/rust/src/ledger.rs +++ b/wrappers/rust/src/ledger.rs @@ -348,7 +348,7 @@ fn _build_get_attrib_request(command_handle: IndyHandle, submitter_did: Option<& /// * `data` - Credential schema. /// { /// id: identifier of schema -/// attrNames: array of attribute name strings (the number of attributes should be less or equal than 150) +/// attrNames: array of attribute name strings (the number of attributes should be less or equal than 125) /// name: Schema's name string /// version: Schema's version string, /// ver: Version of the Schema json From 582f82f52002cacf04228da6a85beb5898b862f0 Mon Sep 17 00:00:00 2001 From: "artem.ivanov" Date: Thu, 10 Jan 2019 09:23:17 +0300 Subject: [PATCH 17/17] Updated url to libsodium installation Signed-off-by: artem.ivanov --- doc/rhel-build.md | 2 +- doc/ubuntu-build.md | 2 +- doc/windows-build.md | 2 +- libindy/ci/amazon.dockerfile | 2 +- libindy/ci/ubuntu.dockerfile | 2 +- 5 files changed, 5 insertions(+), 5 deletions(-) diff --git a/doc/rhel-build.md b/doc/rhel-build.md index dd64d4aec9..1ae8bf8a34 100644 --- a/doc/rhel-build.md +++ b/doc/rhel-build.md @@ -42,7 +42,7 @@ dnf install -y \ For Amazon Linux 2017.03 or other distributions without `libsodium` available in system repositories: ``` cd /tmp -curl https://download.libsodium.org/libsodium/releases/libsodium-1.0.14.tar.gz | tar -xz +curl https://download.libsodium.org/libsodium/releases/old/libsodium-1.0.14.tar.gz | tar -xz cd /tmp/libsodium-1.0.14 ./configure make diff --git a/doc/ubuntu-build.md b/doc/ubuntu-build.md index 474eeee7a7..3ad424ff42 100644 --- a/doc/ubuntu-build.md +++ b/doc/ubuntu-build.md @@ -19,7 +19,7 @@ Because of this, it requires to build and install `libsodium` from source: ``` cd /tmp && \ - curl https://download.libsodium.org/libsodium/releases/libsodium-1.0.14.tar.gz | tar -xz && \ + curl https://download.libsodium.org/libsodium/releases/old/libsodium-1.0.14.tar.gz | tar -xz && \ cd /tmp/libsodium-1.0.14 && \ ./configure --disable-shared && \ make && \ diff --git a/doc/windows-build.md b/doc/windows-build.md index 375ba46d2d..e09b0f5265 100644 --- a/doc/windows-build.md +++ b/doc/windows-build.md @@ -43,7 +43,7 @@ git clone https://github.com/hyperledger/indy-sdk.git ### Binary deps - https://www.npcglib.org/~stathis/downloads/openssl-1.0.2k-vs2017.7z -- https://download.libsodium.org/libsodium/releases/libsodium-1.0.14-msvc.zip +- https://download.libsodium.org/libsodium/releases/old/libsodium-1.0.14-msvc.zip ### Source deps diff --git a/libindy/ci/amazon.dockerfile b/libindy/ci/amazon.dockerfile index 87a4575e36..80329b91de 100755 --- a/libindy/ci/amazon.dockerfile +++ b/libindy/ci/amazon.dockerfile @@ -22,7 +22,7 @@ RUN curl --silent --location https://rpm.nodesource.com/setup_8.x | bash - RUN yum -y install nodejs RUN cd /tmp && \ - curl https://download.libsodium.org/libsodium/releases/libsodium-1.0.14.tar.gz | tar -xz && \ + curl https://download.libsodium.org/libsodium/releases/old/libsodium-1.0.14.tar.gz | tar -xz && \ cd /tmp/libsodium-1.0.14 && \ ./configure && \ make && \ diff --git a/libindy/ci/ubuntu.dockerfile b/libindy/ci/ubuntu.dockerfile index 7807348c48..896248128c 100755 --- a/libindy/ci/ubuntu.dockerfile +++ b/libindy/ci/ubuntu.dockerfile @@ -39,7 +39,7 @@ RUN pip3 install -U \ deb-pkg-tools RUN cd /tmp && \ - curl https://download.libsodium.org/libsodium/releases/libsodium-1.0.14.tar.gz | tar -xz && \ + curl https://download.libsodium.org/libsodium/releases/old/libsodium-1.0.14.tar.gz | tar -xz && \ cd /tmp/libsodium-1.0.14 && \ ./configure --disable-shared && \ make && \