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

Snapshot migration v3age 2.0 #474

Merged
merged 66 commits into from
May 24, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
66 commits
Select commit Hold shift + click to select a range
3e0a9d5
snapshot migration: v2->v3 (age)
semenov-vladyslav Feb 23, 2023
826ed65
cargo: update crypto.rs dependency
semenov-vladyslav Feb 24, 2023
a84d56d
snapshot tests: updated test vectors
semenov-vladyslav Feb 24, 2023
f107ee5
Derive Display for Error
thibault-martinez Mar 1, 2023
27eeca9
Fix warnings
thibault-martinez Mar 1, 2023
49d2ba9
snapshot: fail on associated data
semenov-vladyslav Mar 1, 2023
24d0954
snapshot error
semenov-vladyslav Mar 2, 2023
690e765
snapshot: v2 + wallet + identity + refactor
semenov-vladyslav Mar 2, 2023
d66c688
snapshot: v2 generalize value ctor + CryptoError
semenov-vladyslav Mar 2, 2023
9cc6d4c
snapshot: v2 factor out wallet/identity
semenov-vladyslav Mar 2, 2023
cda735c
Split into module files
thibault-martinez Mar 2, 2023
ca19aa5
Visibility and warnings
thibault-martinez Mar 2, 2023
0bbe822
publicly export error
thibault-martinez Mar 2, 2023
a9999f6
Fmt
thibault-martinez Mar 3, 2023
47aebf4
Bump crypto.rs
thibault-martinez Mar 9, 2023
d9f0842
snapshot age: update to crypto-0.16
semenov-vladyslav Mar 9, 2023
ebaf39c
snapshot age: 0 work_factor for strong keys
semenov-vladyslav Mar 9, 2023
743a17b
Remove redundant mod
thibault-martinez Mar 9, 2023
bbe980a
Clippy
thibault-martinez Mar 9, 2023
88254fe
Add missing license headers
thibault-martinez Mar 10, 2023
2da9ed2
refactor + security comments
semenov-vladyslav Mar 10, 2023
a8b39d7
bump crypto-0.17.0
semenov-vladyslav Mar 13, 2023
d03f7ba
snapshot errors
semenov-vladyslav Mar 13, 2023
fe19796
runtime: xor_mut
semenov-vladyslav Mar 16, 2023
82d6c10
keyprovider: ctors cleanup
semenov-vladyslav Mar 16, 2023
7c8c5a8
zeroizing
semenov-vladyslav Mar 16, 2023
7a6e580
Zeroizing
semenov-vladyslav Mar 20, 2023
aa09fb4
more Zeroizing
semenov-vladyslav Mar 20, 2023
cf9d9ee
deprecate NCM::refresh
semenov-vladyslav Mar 20, 2023
bf08302
age release work factor
semenov-vladyslav Mar 20, 2023
a9920cd
fmt
semenov-vladyslav Mar 20, 2023
e7e4bd7
nits
semenov-vladyslav Mar 20, 2023
8f190de
unsafe rewritten
semenov-vladyslav Mar 20, 2023
898c175
cleanup
semenov-vladyslav Mar 21, 2023
e95b31a
KeyProvider deperecated notes added
semenov-vladyslav Mar 22, 2023
9e70dc8
undeprecate NCM::refresh
semenov-vladyslav Mar 22, 2023
38849ec
fix build errors
semenov-vladyslav Mar 22, 2023
8949fb9
nits
semenov-vladyslav Mar 22, 2023
194cde3
clippy + silenced warnings
semenov-vladyslav Mar 22, 2023
c9aa140
license new lines
semenov-vladyslav Mar 22, 2023
e483df2
get_guards simplify
semenov-vladyslav Mar 30, 2023
9ea2f7a
deref, do not clone
semenov-vladyslav Mar 30, 2023
43d08f1
removed test migrate tool
semenov-vladyslav Apr 4, 2023
537a8c7
changes
semenov-vladyslav Apr 4, 2023
c0acac9
reexport engine from client
semenov-vladyslav Apr 6, 2023
8a321f8
cargo: iota-crypto 0.18.0
semenov-vladyslav May 4, 2023
8d1add9
merged snapshot-migration-v3age
semenov-vladyslav May 18, 2023
b5ba649
fmt + clippy
semenov-vladyslav May 19, 2023
a90ae54
snapshot: removed unused empty associated data
semenov-vladyslav May 19, 2023
475585f
associated data + changelog
semenov-vladyslav May 23, 2023
d4eb67f
lower case in deprecated messages
semenov-vladyslav May 23, 2023
0d939fb
nits + fmt
semenov-vladyslav May 23, 2023
e99d598
error message: lower case
semenov-vladyslav May 23, 2023
b8b5988
error messages
semenov-vladyslav May 23, 2023
b30d3b0
bump iota-crypto-0.20.0
semenov-vladyslav May 23, 2023
06c13e1
error message
semenov-vladyslav May 23, 2023
522b209
slip10 update
semenov-vladyslav May 23, 2023
6d7fe65
deprecated removed
semenov-vladyslav May 24, 2023
e72409c
changelog: typo
semenov-vladyslav May 24, 2023
c6949f6
better changelog message
semenov-vladyslav May 24, 2023
8371036
fmt
semenov-vladyslav May 24, 2023
400f74e
removed unused allow deprecated
semenov-vladyslav May 24, 2023
fa2f6cb
typo
semenov-vladyslav May 24, 2023
bb10936
removed irrelevant comment
semenov-vladyslav May 24, 2023
9898fa8
changes packages
semenov-vladyslav May 24, 2023
f73d9f9
NCM better names
semenov-vladyslav May 24, 2023
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
2 changes: 1 addition & 1 deletion .changes/readme.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@
| iota-stronghold | The Client | Rust | Yes | Yes |
| stronghold-engine | The Engine | Rust | Yes | Yes |
| stronghold-p2p | Communication Subsystem | Rust | Yes | No |
| runtime | Secure Zone | Rust | Yes | No |
| stronghold-runtime | Secure Zone | Rust | Yes | No |
| vault | Engine's memory Store | Rust | No | No |
| snapshot | Engine's Persistence | Rust | No | No |
| store | Engine's Readable Storage Interface | Rust | No | No |
Expand Down
2 changes: 1 addition & 1 deletion .changes/secp256k1.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,4 +5,4 @@
---

Secp256k1 ECDSA + SLIP-10 support added.
Bump `iota-crypto` version to 0.19.0.
Bump `iota-crypto` version to 0.20.0.
11 changes: 11 additions & 0 deletions .changes/snapshot-migration-v3age-zeroize.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
---

"iota-stronghold": minor
"stronghold-engine" : minor
"stronghold-runtime" : minor

---

Upgraded snapshot format to age-encryption.org/v1 with password-based recipient stanza. This resolves the issue with the previous snapshot format encryption being insecure if used with weak passwords. Snapshot encryption doesn't use associated data.
Added sensitive data zeroization which would otherwise leak in stack and heap memory in plaintext after use.
`KeyProvider` unsafe constructors `with_passphrase_truncated`, `with_passphrase_hashed_argon2` were removed, `with_passphrase_hashed` constructor should be used instead.
3 changes: 2 additions & 1 deletion bindings/native/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ iota_stronghold = { package = "iota_stronghold", path = "../../client/
engine = { package = "stronghold_engine", path = "../../engine", version = "1.0.0" }
tokio = { version = "1.15.0", features = ["full"] }
base64 = { version = "0.13.0" }
iota-crypto = { version = "0.19.0", default-features = false, features = [
iota-crypto = { version = "0.20.0", default-features = false, features = [
"aes-gcm",
"aes-kw",
"random",
Expand All @@ -39,3 +39,4 @@ iota-crypto = { version = "0.19.0", default-features = false, features = [
lazy_static = "1.4.0"
env_logger = { version = "0.9.0" }
log = { version = "0.4.14" }
zeroize = { version = "1.5.7", default-features = false }
3 changes: 2 additions & 1 deletion bindings/native/src/lib.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
// Copyright 2020-2022 IOTA Stiftung
// SPDX-License-Identifier: Apache-2.0

extern crate core;

mod shared;
Expand Down Expand Up @@ -213,7 +214,7 @@ pub unsafe extern "C" fn stronghold_write_vault(

info!("[Rust] Got Stronghold instance from Box");

if let Err(err) = stronghold_wrapper.write_vault(key_as_hash, record_path, data.to_vec()) {
if let Err(err) = stronghold_wrapper.write_vault(key_as_hash, record_path, data.to_vec().into()) {
set_last_error(err);
return false;
}
Expand Down
8 changes: 6 additions & 2 deletions bindings/native/src/shared.rs
Original file line number Diff line number Diff line change
@@ -1,9 +1,13 @@
// Copyright 2020-2022 IOTA Stiftung
// SPDX-License-Identifier: Apache-2.0

use crypto::hashes::{blake2b::Blake2b256, Digest};
use zeroize::Zeroizing;

pub fn hash_blake2b(input: String) -> Vec<u8> {
pub fn hash_blake2b(input: String) -> Zeroizing<Vec<u8>> {
let mut hasher = Blake2b256::new();
hasher.update(input.as_bytes());
hasher.finalize().to_vec()
let mut hash = Zeroizing::new(vec![0_u8; Blake2b256::output_size()]);
hasher.finalize_into((&mut hash[..]).into());
hash
}
59 changes: 24 additions & 35 deletions bindings/native/src/wrapper.rs
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ use iota_stronghold::{
};
use log::*;
use thiserror::Error as DeriveError;
use zeroize::Zeroizing;

const CLIENT_PATH: &str = "wasp";
const VAULT_PATH: &str = "wasp";
Expand All @@ -27,33 +28,30 @@ pub struct StrongholdWrapper {
#[derive(Debug, DeriveError)]
#[non_exhaustive]
pub enum WrapperError {
#[error("Failed to open snapshot")]
#[error("failed to open snapshot")]
OpenSnapshot,

#[error("Failed to commit to snapshot")]
#[error("failed to commit to snapshot")]
CommitToSnapshot,

#[error("Failed to create client")]
#[error("failed to create client")]
CreateClient,

#[error("Failed to write client")]
#[error("failed to write client")]
WriteClient,

#[error("Failed to execute procedure: ({0})")]
#[error("failed to execute procedure: ({0})")]
ExecuteProcedure(String),
}

impl StrongholdWrapper {
pub fn from_file<R>(snapshot_path: String, key_as_hash: R) -> Result<Self, WrapperError>
where
R: AsRef<[u8]>,
{
pub fn from_file(snapshot_path: String, key_as_hash: Zeroizing<Vec<u8>>) -> Result<Self, WrapperError> {
let stronghold = Stronghold::default();

log::info!("[Rust] Loading snapshot => {}", snapshot_path);

let commit_snapshot_path = &SnapshotPath::from_path(snapshot_path.clone());
let key_provider = &KeyProvider::try_from(key_as_hash.as_ref().to_vec()).unwrap();
let key_provider = &KeyProvider::try_from(key_as_hash).unwrap();

let client = stronghold.load_client_from_snapshot(CLIENT_PATH, key_provider, commit_snapshot_path);

Expand All @@ -69,10 +67,7 @@ impl StrongholdWrapper {
})
}

pub fn create_new<R>(snapshot_path: String, key_as_hash: R) -> Result<Self, WrapperError>
where
R: AsRef<[u8]>,
{
pub fn create_new(snapshot_path: String, key_as_hash: Zeroizing<Vec<u8>>) -> Result<Self, WrapperError> {
let stronghold = Stronghold::default();

let client = match stronghold.create_client(CLIENT_PATH) {
Expand All @@ -99,14 +94,11 @@ impl StrongholdWrapper {
Ok(result)
}

fn commit_with_key<R>(&self, key_as_hash: R) -> Result<bool, WrapperError>
where
R: AsRef<[u8]>,
{
fn commit_with_key(&self, key_as_hash: Zeroizing<Vec<u8>>) -> Result<bool, WrapperError> {
log::info!("[Rust] Committing to snapshot");

let commit_snapshot_path = &SnapshotPath::from_path(self.snapshot_path.clone());
let key_provider = &KeyProvider::try_from(key_as_hash.as_ref().to_vec()).unwrap();
let key_provider = &KeyProvider::try_from(key_as_hash).unwrap();

match self
.stronghold
Expand Down Expand Up @@ -138,10 +130,12 @@ impl StrongholdWrapper {
Ok(output)
}

pub fn write_vault<R>(&self, key_as_hash: R, record_path: String, data: Vec<u8>) -> Result<bool, WrapperError>
where
R: AsRef<[u8]>,
{
pub fn write_vault(
&self,
key_as_hash: Zeroizing<Vec<u8>>,
record_path: String,
data: Zeroizing<Vec<u8>>,
) -> Result<bool, WrapperError> {
let location = Location::Generic {
record_path: record_path.as_bytes().to_vec(),
vault_path: VAULT_PATH.as_bytes().to_vec(),
Expand Down Expand Up @@ -174,10 +168,7 @@ impl StrongholdWrapper {
Ok(signature)
}

pub fn derive_seed<R>(&self, key_as_hash: R, address_index: u32) -> Result<ChainCode, WrapperError>
where
R: AsRef<[u8]>,
{
pub fn derive_seed(&self, key_as_hash: Zeroizing<Vec<u8>>, address_index: u32) -> Result<ChainCode, WrapperError> {
let seed_derived_path = format!("{RECORD_PATH_SEED}.{address_index}");

let seed_location = Location::Generic {
Expand Down Expand Up @@ -227,10 +218,7 @@ impl StrongholdWrapper {
}
}

pub fn generate_seed<R>(&self, key_as_hash: R) -> Result<bool, WrapperError>
where
R: AsRef<[u8]>,
{
pub fn generate_seed(&self, key_as_hash: Zeroizing<Vec<u8>>) -> Result<bool, WrapperError> {
let output = Location::Generic {
record_path: RECORD_PATH_SEED.as_bytes().to_vec(),
vault_path: VAULT_PATH.as_bytes().to_vec(),
Expand Down Expand Up @@ -259,10 +247,11 @@ impl StrongholdWrapper {
self.commit_with_key(key_as_hash)
}

pub fn generate_ed25519_keypair<R>(&self, key_as_hash: R, record_path: String) -> Result<bool, WrapperError>
where
R: AsRef<[u8]>,
{
pub fn generate_ed25519_keypair(
&self,
key_as_hash: Zeroizing<Vec<u8>>,
record_path: String,
) -> Result<bool, WrapperError> {
let output = Location::Generic {
record_path: record_path.as_bytes().to_vec(),
vault_path: VAULT_PATH.as_bytes().to_vec(),
Expand Down
4 changes: 2 additions & 2 deletions client/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -18,9 +18,9 @@ insecure = [ ]

[dependencies]
thiserror = { version = "1.0.30" }
zeroize = { version = "1.5.7", default-features = false, features = [ "zeroize_derive" ] }
zeroize = { version = "1.5.7", default-features = false, features = [ "zeroize_derive", "serde" ] }
serde = { version = "1.0", features = [ "derive" ] }
iota-crypto = { version = "0.19.0", default-features = false, features = [
iota-crypto = { version = "0.20.0", default-features = false, features = [
"aes-gcm",
"blake2b",
"aes-kw",
Expand Down
1 change: 1 addition & 0 deletions client/benches/config.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
// Copyright 2020-2021 IOTA Stiftung
// SPDX-License-Identifier: Apache-2.0

use criterion::{criterion_group, criterion_main, Criterion};

/// Primitve benchmark
Expand Down
8 changes: 6 additions & 2 deletions client/examples/cli/main.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
// Copyright 2020-2022 IOTA Stiftung
// SPDX-License-Identifier: Apache-2.0

#![allow(unused_imports)]

use std::{error::Error, hash::Hash, num::NonZeroUsize, str::FromStr};
Expand All @@ -18,6 +19,7 @@ use stronghold::{
};
use stronghold_utils::random as rand;
use thiserror::Error as DeriveError;
use zeroize::Zeroizing;

#[derive(Debug)]
pub struct ChainInput {
Expand Down Expand Up @@ -203,10 +205,12 @@ pub enum Command {
}

/// Calculates the Blake2b from a String
fn hash_blake2b(input: String) -> Vec<u8> {
fn hash_blake2b(input: String) -> Zeroizing<Vec<u8>> {
let mut hasher = Blake2b256::new();
hasher.update(input.as_bytes());
hasher.finalize().to_vec()
let mut hash = Zeroizing::new(vec![0_u8; Blake2b256::output_size()]);
hasher.finalize_into((&mut hash[..]).into());
hash
}

async fn command_write_and_read_from_store(key: String, value: String) -> Result<(), ClientError> {
Expand Down
4 changes: 2 additions & 2 deletions client/examples/repl/command.rs
Original file line number Diff line number Diff line change
Expand Up @@ -180,7 +180,7 @@ impl Command for BackupCommand {
};
let password = parameters[1].clone().as_bytes().to_vec();
let snapshot_path = SnapshotPath::from_path(&parameters[0]);
let keyprovider = KeyProvider::with_passphrase_truncated(password)?;
let keyprovider = KeyProvider::with_passphrase_hashed_blake2b(password)?;

stronghold.commit_with_keyprovider(&snapshot_path, &keyprovider)?;

Expand Down Expand Up @@ -211,7 +211,7 @@ impl Command for RestoreCommand {
};
let password = parameters[1].clone().as_bytes().to_vec();
let snapshot_path = SnapshotPath::from_path(&parameters[0]);
let keyprovider = KeyProvider::with_passphrase_truncated(password)?;
let keyprovider = KeyProvider::with_passphrase_hashed_blake2b(password)?;

stronghold.load_snapshot(&keyprovider, &snapshot_path)?;

Expand Down
1 change: 1 addition & 0 deletions client/fuzz/basic.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
// Copyright 2020-2021 IOTA Stiftung
// SPDX-License-Identifier: Apache-2.0

#![no_main]

fuzz_target!(|data: &[u8]| {});
1 change: 1 addition & 0 deletions client/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
#[cfg(feature = "std")]
pub use crate::{internal::Provider, security::*, types::*, utils::*};

pub use engine;
#[cfg(feature = "std")]
pub use engine::runtime::MemoryError;

Expand Down
31 changes: 7 additions & 24 deletions client/src/procedures/clientrunner.rs
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,8 @@ use engine::{
vault::{BoxProvider, ClientId, DbView, Key, RecordHint, RecordId, VaultId},
};

use zeroize::Zeroizing;

use crate::{
derive_vault_id,
procedures::{
Expand Down Expand Up @@ -52,22 +54,10 @@ impl Runner for Client {
where
F: FnOnce([Buffer<u8>; N]) -> Result<T, FatalProcedureError>,
{
let mut ret = None;
let execute_procedure = |guard: [Buffer<u8>; N]| {
ret = Some(f(guard)?);
Ok(())
};
thibault-martinez marked this conversation as resolved.
Show resolved Hide resolved

let keystore = self.keystore.read().map_err(|_| VaultError::LockPoisoned)?;
let db = self.db.read().map_err(|_| VaultError::LockPoisoned)?;
let ids: [(Key<Provider>, VaultId, RecordId); N] = resolve_locations!(self, locations, keystore)?;

let res = db.get_guards(ids, execute_procedure);

match res {
Ok(()) => Ok(ret.unwrap()),
Err(e) => Err(e),
}
db.get_guards(ids, f)
}

fn exec_proc<F, T, const N: usize>(
Expand All @@ -81,11 +71,9 @@ impl Runner for Client {
{
let (target_vid, target_rid) = target_location.resolve();

let mut ret = None;
let execute_procedure = |guards: [Buffer<u8>; N]| {
let Products { output: plain, secret } = f(guards)?;
ret = Some(plain);
Ok(secret)
Ok((secret, plain))
};

let random_hint = RecordHint::new(rand::variable_bytestring(DEFAULT_RANDOM_HINT_SIZE)).unwrap();
Expand All @@ -106,22 +94,17 @@ impl Runner for Client {
.get_key(target_vid)
.ok_or(VaultError::VaultNotFound(target_vid))?;

let res = db.exec_procedure(
db.exec_procedure(
sources,
&target_key,
target_vid,
target_rid,
random_hint,
execute_procedure,
);

match res {
Ok(()) => Ok(ret.unwrap()),
Err(e) => Err(e),
}
)
}

fn write_to_vault(&self, location: &Location, value: Vec<u8>) -> Result<(), RecordError> {
fn write_to_vault(&self, location: &Location, value: Zeroizing<Vec<u8>>) -> Result<(), RecordError> {
let (vault_id, record_id) = location.resolve();

let mut keystore = self.keystore.write().map_err(|_| RecordError::LockPoisoned)?;
Expand Down
Loading