Skip to content

Commit

Permalink
Browse files Browse the repository at this point in the history
2710: Wallet encryption r=Eitu33 a=Eitu33



Co-authored-by: Thomas Plisson <[email protected]>
  • Loading branch information
bors[bot] and Eitu33 authored Jun 21, 2022
2 parents 935192c + 69b6f27 commit e875cf9
Show file tree
Hide file tree
Showing 12 changed files with 287 additions and 27 deletions.
97 changes: 95 additions & 2 deletions Cargo.lock

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

1 change: 1 addition & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ members = [
"massa-async-pool",
"massa-bootstrap",
"massa-client",
"massa-cipher",
"massa-consensus-exports",
"massa-consensus-worker",
"massa-execution-exports",
Expand Down
20 changes: 20 additions & 0 deletions massa-cipher/Cargo.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
[package]
name = "massa_cipher"
version = "0.1.0"
edition = "2021"

# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html

[dependencies]
displaydoc = "0.2"
serde = { version = "1.0", features = ["derive"] }
serde_json = "1.0"
serde_qs = "0.8"
thiserror = "1.0"
aes-gcm-siv = "0.10"
rand = "0.8"

# custom modules
massa_hash = { path = "../massa-hash" }
massa_models = { path = "../massa-models" }
massa_signature = { path = "../massa-signature" }
6 changes: 6 additions & 0 deletions massa-cipher/src/constants.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
// Copyright (c) 2022 MASSA LABS <[email protected]>

/// Nonce size
///
/// Read `lib.rs` module documentation for more information.
pub const NONCE_SIZE: usize = 12;
35 changes: 35 additions & 0 deletions massa-cipher/src/decrypt.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
// Copyright (c) 2022 MASSA LABS <[email protected]>

use aes_gcm_siv::aead::{Aead, NewAead};
use aes_gcm_siv::{Aes256GcmSiv, Key, Nonce};
use massa_hash::Hash;

use crate::constants::NONCE_SIZE;
use crate::error::CipherError;

/// Decryption function using AES-GCM-SIV cipher.
///
/// Read `lib.rs` module documentation for more information.
pub fn decrypt(password: &str, data: &[u8]) -> Result<Vec<u8>, CipherError> {
let cipher = Aes256GcmSiv::new(Key::from_slice(
Hash::compute_from(password.as_bytes()).to_bytes(),
));
let nonce = Nonce::from_slice(data.get(..NONCE_SIZE).ok_or_else(|| {
CipherError::DecryptionError(
"wallet file truncated: nonce missing or incomplete".to_string(),
)
})?);
let decrypted_bytes = cipher
.decrypt(
nonce,
data.get(NONCE_SIZE..).ok_or_else(|| {
CipherError::DecryptionError(
"wallet file truncated: encrypted data missing or incomplete".to_string(),
)
})?,
)
.map_err(|_| {
CipherError::DecryptionError("wrong password or corrupted data".to_string())
})?;
Ok(decrypted_bytes)
}
27 changes: 27 additions & 0 deletions massa-cipher/src/encrypt.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
// Copyright (c) 2022 MASSA LABS <[email protected]>

use aes_gcm_siv::aead::{Aead, NewAead};
use aes_gcm_siv::{Aes256GcmSiv, Key, Nonce};
use massa_hash::Hash;
use rand::{thread_rng, RngCore};

use crate::constants::NONCE_SIZE;
use crate::error::CipherError;

/// Encryption function using AES-GCM-SIV cipher.
///
/// Read `lib.rs` module documentation for more information.
pub fn encrypt(password: &str, data: &[u8]) -> Result<Vec<u8>, CipherError> {
let cipher = Aes256GcmSiv::new(Key::from_slice(
Hash::compute_from(password.as_bytes()).to_bytes(),
));
let mut nonce_bytes = [0u8; NONCE_SIZE];
thread_rng().fill_bytes(&mut nonce_bytes);
let nonce = Nonce::from_slice(&nonce_bytes);
let encrypted_bytes = cipher
.encrypt(nonce, data.as_ref())
.map_err(|e| CipherError::EncryptionError(e.to_string()))?;
let mut content = nonce_bytes.to_vec();
content.extend(encrypted_bytes);
Ok(content)
}
13 changes: 13 additions & 0 deletions massa-cipher/src/error.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
// Copyright (c) 2022 MASSA LABS <[email protected]>

use displaydoc::Display;
use thiserror::Error;

/// Cipher error
#[derive(Display, Error, Debug)]
pub enum CipherError {
/// Encryption error: {0}
EncryptionError(String),
/// Decryption error: {0}
DecryptionError(String),
}
24 changes: 24 additions & 0 deletions massa-cipher/src/lib.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
// Copyright (c) 2022 MASSA LABS <[email protected]>

//! Cipher crate
//!
//! `massa-cipher` uses AES-GCM-SIV ([RFC 8452](https://datatracker.ietf.org/doc/html/rfc8452)).
//!
//! AES-GCM-SIV is a state-of-the-art high-performance Authenticated Encryption with Associated Data (AEAD)
//! cipher which also provides nonce reuse misuse resistance.
//! Suitable as a general purpose symmetric encryption cipher, AES-GCM-SIV also removes many of the sharp edges of AES-GCM.
//!
//! A nonce is a single-use value which enables securely encrypting multiple messages under the same key.
//! Nonces need not be random: a counter can be used, so long as the values are never repeated under the same key.
//!
//! No complete security audits of the crate we use has been performed.
//! But some of this crate's dependencies were audited by by NCC Group as part of an audit of the AES-GCM crate
mod constants;
mod decrypt;
mod encrypt;
mod error;

pub use decrypt::decrypt;
pub use encrypt::encrypt;
pub use error::CipherError;
30 changes: 26 additions & 4 deletions massa-client/src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,11 +8,12 @@ use anyhow::Result;
use atty::Stream;
use cmds::Command;
use console::style;
use dialoguer::Password;
use massa_sdk::Client;
use massa_wallet::Wallet;
use serde::Serialize;
use std::net::IpAddr;
use std::path::PathBuf;
use std::path::{Path, PathBuf};
use structopt::StructOpt;

mod cmds;
Expand All @@ -31,7 +32,7 @@ struct Args {
/// Port to listen on (Massa private API).
#[structopt(long)]
private_port: Option<u16>,
/// Address to listen on.
/// Address to listen on
#[structopt(long)]
ip: Option<IpAddr>,
/// Command that client would execute (non-interactive mode)
Expand All @@ -40,7 +41,7 @@ struct Args {
/// Optional command parameter (as a JSON string)
#[structopt(name = "PARAMETERS")]
parameters: Vec<String>,
/// Path of wallet file.
/// Path of wallet file
#[structopt(
short = "w",
long = "wallet",
Expand All @@ -51,13 +52,33 @@ struct Args {
/// Enable a mode where input/output are serialized as JSON
#[structopt(short = "j", long = "json")]
json: bool,
#[structopt(short = "p", long = "pwd")]
/// Wallet password
password: Option<String>,
}

#[derive(Serialize)]
struct JsonError {
error: String,
}

/// Ask for the wallet password
/// If the wallet does not exist, it will require password confirmation
fn ask_password(wallet_path: &Path) -> String {
if wallet_path.is_file() {
Password::new()
.with_prompt("Enter wallet password")
.interact()
.expect("IO error: Password reading failed, walled couldn't be unlocked")
} else {
Password::new()
.with_prompt("Enter new password for wallet")
.with_confirmation("Confirm password", "Passwords mismatching")
.interact()
.expect("IO error: Password reading failed, wallet couldn't be created")
}
}

#[paw::main]
#[tokio::main]
async fn main(args: Args) -> Result<()> {
Expand All @@ -76,7 +97,8 @@ async fn main(args: Args) -> Result<()> {
None => settings.default_node.private_port,
};
// ...
let mut wallet = Wallet::new(args.wallet)?;
let password = args.password.unwrap_or_else(|| ask_password(&args.wallet));
let mut wallet = Wallet::new(args.wallet, password)?;
let client = Client::new(address, public_port, private_port).await;
if atty::is(Stream::Stdout) && args.command == Command::help && !args.json {
// Interactive mode
Expand Down
1 change: 1 addition & 0 deletions massa-wallet/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ serde_qs = "0.8"
thiserror = "1.0"

# custom modules
massa_cipher = { path = "../massa-cipher" }
massa_hash = { path = "../massa-hash" }
massa_models = { path = "../massa-models" }
massa_signature = { path = "../massa-signature" }
2 changes: 2 additions & 0 deletions massa-wallet/src/error.rs
Original file line number Diff line number Diff line change
Expand Up @@ -20,4 +20,6 @@ pub enum WalletError {
MassaHashError(#[from] massa_hash::MassaHashError),
/// Missing key error: {0}
MissingKeyError(Address),
/// `MassaCipher` error: {0}
MassaCipherError(#[from] massa_cipher::CipherError),
}
Loading

0 comments on commit e875cf9

Please sign in to comment.