Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(crypto): mnemonic generation/encryption/decryption/storage #2014

Merged
merged 22 commits into from
Apr 9, 2024
Merged
Show file tree
Hide file tree
Changes from 19 commits
Commits
Show all changes
22 commits
Select commit Hold shift + click to select a range
03c0e59
wip: Implement mnemonic generation, encryption, decryption, and stora…
shamardy Nov 24, 2023
714e72d
Wasm encryption/decryption and wallets mnemonic files storage
shamardy Nov 28, 2023
d0e3f44
rollback some Cargo.lock changes that were done due to the use of ope…
shamardy Nov 28, 2023
2e50871
rollback some adex-cli Cargo.lock changes to be the same as cargo.loc…
shamardy Nov 28, 2023
eb04b12
Fix review notes: use consts where applicable, refactor MnemonicsTabl…
shamardy Nov 30, 2023
825ecdb
Fix review notes: add EncryptionAlgorithm enum
shamardy Nov 30, 2023
8365567
Fix review notes: fix on_upgrade_needed fn for mnemonics table, make…
shamardy Dec 1, 2023
69117b8
Fix review notes: fix doc comments of read_encrypted_passphrase
shamardy Dec 1, 2023
3bdb2d5
allow importing of an encrypted passphrase
shamardy Jan 9, 2024
2df7834
refactor handle_passphrase_logic
shamardy Jan 10, 2024
839beb6
make global wallets db part of ctx
shamardy Jan 17, 2024
4b7c014
fix wasm clippy
shamardy Jan 17, 2024
5a65a48
add get_mnemonic rpc
shamardy Feb 6, 2024
6d5e271
add SLIP-0021 keys derivation
shamardy Feb 9, 2024
c37316e
add encrypt.rs for encryption of general data regardless of key deriv…
shamardy Feb 9, 2024
37f16fb
add decrypt.rs for decryption of general data regardless of key deriv…
shamardy Feb 9, 2024
9ad3358
Introduce SLIP-0021 encryption and decryption in slip21.rs
shamardy Feb 12, 2024
66085b0
Merge remote-tracking branch 'origin/dev' into feat-seed-gen
shamardy Feb 12, 2024
4f666c1
Review Fixes:
shamardy Feb 14, 2024
ba0fb59
Review fixes:
shamardy Feb 16, 2024
eb26362
Review fixes: Update base64 crate to 0.21.2 and replace deprecated fu…
shamardy Feb 16, 2024
47cdc19
Merge remote-tracking branch 'origin/dev' into feat-seed-gen
shamardy Apr 8, 2024
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
173 changes: 133 additions & 40 deletions Cargo.lock

Large diffs are not rendered by default.

68 changes: 54 additions & 14 deletions mm2src/adex_cli/Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

3 changes: 1 addition & 2 deletions mm2src/coins/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -95,12 +95,11 @@ serde_json = { version = "1", features = ["preserve_order", "raw_value"] }
serialization = { path = "../mm2_bitcoin/serialization" }
serialization_derive = { path = "../mm2_bitcoin/serialization_derive" }
spv_validation = { path = "../mm2_bitcoin/spv_validation" }
sha2 = "0.9"
sha2 = "0.10"
sha3 = "0.9"
utxo_signer = { path = "utxo_signer" }
# using the same version as cosmrs
tendermint-rpc = { version = "=0.23.7", default-features = false }
tiny-bip39 = "0.8.0"
url = { version = "2.2.2", features = ["serde"] }
uuid = { version = "1.2.2", features = ["fast-rng", "serde", "v4"] }
# One of web3 dependencies is the old `tokio-uds 0.1.7` which fails cross-compiling to ARM.
Expand Down
11 changes: 4 additions & 7 deletions mm2src/coins/solana/solana_common_tests.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
use super::*;
use crate::solana::spl::{SplToken, SplTokenFields};
use bip39::Language;
use crypto::privkey::key_pair_from_seed;
use crypto::privkey::{bip39_seed_from_passphrase, key_pair_from_seed};
use ed25519_dalek_bip32::{DerivationPath, ExtendedSecretKey};
use mm2_core::mm_ctx::MmCtxBuilder;
use solana_client::rpc_client::RpcClient;
Expand All @@ -22,13 +21,11 @@ pub fn solana_net_to_url(net_type: SolanaNet) -> String {
}
}

pub fn generate_key_pair_from_seed(seed: String) -> Keypair {
pub fn generate_key_pair_from_seed(seed: &str) -> Keypair {
let derivation_path = DerivationPath::from_str("m/44'/501'/0'").unwrap();
let mnemonic = bip39::Mnemonic::from_phrase(seed.as_str(), Language::English).unwrap();
let seed = bip39::Seed::new(&mnemonic, "");
let seed_bytes: &[u8] = seed.as_bytes();
let seed = bip39_seed_from_passphrase(seed).unwrap();

let ext = ExtendedSecretKey::from_seed(seed_bytes)
let ext = ExtendedSecretKey::from_seed(&seed.0)
.unwrap()
.derive(&derivation_path)
.unwrap();
Expand Down
5 changes: 2 additions & 3 deletions mm2src/coins/solana/solana_tests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -35,9 +35,8 @@ fn solana_keypair_from_secp() {
fn solana_prerequisites() {
// same test as trustwallet
{
let fin = generate_key_pair_from_seed(
"hood vacant left trim hard mushroom device flavor ask better arrest again".to_string(),
);
let fin =
generate_key_pair_from_seed("hood vacant left trim hard mushroom device flavor ask better arrest again");
let public_address = fin.pubkey().to_string();
let priv_key = &fin.secret().to_bytes()[..].to_base58();
assert_eq!(public_address.len(), 44);
Expand Down
2 changes: 1 addition & 1 deletion mm2src/common/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,7 @@ serde_derive = "1"
serde_json = { version = "1", features = ["preserve_order", "raw_value"] }
ser_error = { path = "../derives/ser_error" }
ser_error_derive = { path = "../derives/ser_error_derive" }
sha2 = "0.9"
sha2 = "0.10"
shared_ref_counter = { path = "shared_ref_counter", optional = true }
uuid = { version = "1.2.2", features = ["fast-rng", "serde", "v4"] }
instant = { version = "0.1.12" }
Expand Down
15 changes: 14 additions & 1 deletion mm2src/crypto/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -7,17 +7,24 @@ edition = "2018"
doctest = false

[dependencies]
aes = "0.8.3"
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Q: Can we use version 0.7.5 instead of duplicating this dependency?

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I wanted to use the latest version for such an important encryption, after this #1957 is merged I can try to update 0.7.5 to 0.8.3 instead in librustzcash

argon2 = { version = "0.5.2", features = ["zeroize"] }
arrayref = "0.3"
async-trait = "0.1"
base64 = "0.11.0"
onur-ozkan marked this conversation as resolved.
Show resolved Hide resolved
bip32 = { version = "0.2.2", default-features = false, features = ["alloc", "secp256k1-ffi"] }
bip39 = { version = "2.0.0", features = ["rand_core", "zeroize"], default-features = false }
bitcrypto = { path = "../mm2_bitcoin/crypto" }
bs58 = "0.4.0"
cbc = "0.1.2"
cipher = "0.4.4"
common = { path = "../common" }
derive_more = "0.99"
enum_from = { path = "../derives/enum_from" }
enum-primitive-derive = "0.2"
futures = "0.3"
hex = "0.4.2"
hmac = "0.12.1"
onur-ozkan marked this conversation as resolved.
Show resolved Hide resolved
http = "0.2"
hw_common = { path = "../hw_common" }
keys = { path = "../mm2_bitcoin/keys" }
Expand All @@ -36,14 +43,20 @@ ser_error_derive = { path = "../derives/ser_error_derive" }
serde = "1.0"
serde_derive = "1.0"
serde_json = { version = "1", features = ["preserve_order", "raw_value"] }
tiny-bip39 = "0.8.0"
sha2 = "0.10"
trezor = { path = "../trezor" }
zeroize = { version = "1.5", features = ["zeroize_derive"] }

[target.'cfg(target_arch = "wasm32")'.dependencies]
cfg-if = "1.0"
mm2_eth = { path = "../mm2_eth" }
mm2_metamask = { path = "../mm2_metamask" }
wasm-bindgen-test = { version = "0.3.2" }
web3 = { git = "https://github.com/KomodoPlatform/rust-web3", tag = "v0.19.0", default-features = false }

[dev-dependencies]
cfg-if = "1.0"
tokio = { version = "1.20", default-features = false }

[features]
trezor-udp = ["trezor/trezor-udp"]
61 changes: 61 additions & 0 deletions mm2src/crypto/src/decrypt.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
use crate::EncryptedData;
use aes::cipher::{block_padding::Pkcs7, BlockDecryptMut, KeyIvInit};
use aes::Aes256;
use derive_more::Display;
use hmac::{Hmac, Mac};
use mm2_err_handle::prelude::*;
use sha2::Sha256;

type Aes256CbcDec = cbc::Decryptor<Aes256>;

#[derive(Debug, Display, PartialEq)]
pub enum DecryptionError {
#[display(fmt = "AES cipher error: {}", _0)]
AESCipherError(String),
#[display(fmt = "Error decoding string: {}", _0)]
DecodeError(String),
#[display(fmt = "HMAC error: {}", _0)]
HMACError(String),
Internal(String),
}

impl From<base64::DecodeError> for DecryptionError {
fn from(e: base64::DecodeError) -> Self { DecryptionError::DecodeError(e.to_string()) }
}

/// Decrypts the provided encrypted data using AES-256-CBC decryption and HMAC for integrity check.
///
/// This function performs several operations:
/// - It decodes the Base64-encoded values of the IV, ciphertext, and HMAC tag from the `EncryptedData`.
/// - It verifies the HMAC tag before decrypting to ensure the integrity of the data.
/// - It creates an AES-256-CBC cipher instance and decrypts the ciphertext with the provided key and the decoded IV.
///
/// # Returns
/// `MmResult<Vec<u8>, DecryptionError>` - The result is either a byte vector containing the decrypted data,
/// or a [`DecryptionError`] in case of failure.
///
/// # Errors
/// This function can return various errors related to Base64 decoding, HMAC verification, and AES decryption.
pub fn decrypt_data(
encrypted_data: &EncryptedData,
key_aes: &[u8; 32],
key_hmac: &[u8; 32],
) -> MmResult<Vec<u8>, DecryptionError> {
// Decode the Base64-encoded values
let iv = base64::decode(&encrypted_data.iv)?;
let mut ciphertext = base64::decode(&encrypted_data.ciphertext)?;
let tag = base64::decode(&encrypted_data.tag)?;

// Verify HMAC tag before decrypting
let mut mac = Hmac::<Sha256>::new_from_slice(key_hmac).map_to_mm(|e| DecryptionError::Internal(e.to_string()))?;
mac.update(&ciphertext);
mac.update(&iv);
mac.verify_slice(&tag)
.map_to_mm(|e| DecryptionError::HMACError(e.to_string()))?;

// Decrypt the ciphertext and return the result
Aes256CbcDec::new(key_aes.into(), iv.as_slice().into())
.decrypt_padded_mut::<Pkcs7>(&mut ciphertext)
.map_to_mm(|e| DecryptionError::AESCipherError(e.to_string()))
.map(|plaintext| plaintext.to_vec())
}
Loading
Loading