Skip to content

Commit 96eb036

Browse files
committed
fix(wallet): fix keypair decoding from storage
1 parent 2fb3a04 commit 96eb036

File tree

9 files changed

+140
-16
lines changed

9 files changed

+140
-16
lines changed

data_structures/src/chain/mod.rs

+2-2
Original file line numberDiff line numberDiff line change
@@ -1115,8 +1115,8 @@ pub enum Hash {
11151115
impl Default for Hash {
11161116
fn default() -> Hash {
11171117
Hash::SHA256([
1118-
227, 176, 196, 66, 152, 252, 28, 20, 154, 251, 244, 200, 153, 111, 185, 36, 39, 174, 65, 228,
1119-
100, 155, 147, 76, 164, 149, 153, 27, 120, 82, 184, 85,
1118+
227, 176, 196, 66, 152, 252, 28, 20, 154, 251, 244, 200, 153, 111, 185, 36, 39, 174,
1119+
65, 228, 100, 155, 147, 76, 164, 149, 153, 27, 120, 82, 184, 85,
11201120
])
11211121
}
11221122
}

validations/src/validations.rs

+14-8
Original file line numberDiff line numberDiff line change
@@ -11,14 +11,19 @@ use witnet_config::defaults::{
1111
PSEUDO_CONSENSUS_CONSTANTS_WIP0022_REWARD_COLLATERAL_RATIO,
1212
PSEUDO_CONSENSUS_CONSTANTS_WIP0027_COLLATERAL_AGE,
1313
};
14-
use witnet_crypto::{hash::{calculate_sha256, Sha256}, hash, merkle::{merkle_tree_root as crypto_merkle_tree_root, ProgressiveMerkleTree}, signature::{PublicKey, Signature, verify}};
14+
use witnet_crypto::{
15+
hash,
16+
hash::{calculate_sha256, Sha256},
17+
merkle::{merkle_tree_root as crypto_merkle_tree_root, ProgressiveMerkleTree},
18+
signature::{verify, PublicKey, Signature},
19+
};
1520
use witnet_data_structures::{
1621
chain::{
17-
Block, BlockMerkleRoots, CheckpointBeacon, CheckpointVRF, ConsensusConstants,
18-
DataRequestOutput, DataRequestStage, DataRequestState, Epoch, EpochConstants,
19-
Hash, Hashable, Input, KeyedSignature, OutputPointer, PublicKeyHash, RADRequest,
20-
RADTally, RADType, Reputation, ReputationEngine, SignaturesToVerify, StakeOutput,
21-
tapi::ActiveWips, ValueTransferOutput,
22+
tapi::ActiveWips, Block, BlockMerkleRoots, CheckpointBeacon, CheckpointVRF,
23+
ConsensusConstants, DataRequestOutput, DataRequestStage, DataRequestState, Epoch,
24+
EpochConstants, Hash, Hashable, Input, KeyedSignature, OutputPointer, PublicKeyHash,
25+
RADRequest, RADTally, RADType, Reputation, ReputationEngine, SignaturesToVerify,
26+
StakeOutput, ValueTransferOutput,
2227
},
2328
data_request::{
2429
calculate_reward_collateral_ratio, calculate_tally_change, calculate_witness_reward,
@@ -46,7 +51,7 @@ use witnet_rad::{
4651
error::RadError,
4752
operators::RadonOpCodes,
4853
script::{create_radon_script_from_filters_and_reducer, unpack_radon_script},
49-
types::{RadonTypes, serial_iter_decode},
54+
types::{serial_iter_decode, RadonTypes},
5055
};
5156

5257
// TODO: move to a configuration
@@ -2003,7 +2008,8 @@ pub fn validate_block_transactions(
20032008
if merkle_roots != block.block_header.merkle_roots {
20042009
log::debug!(
20052010
"{:?} vs {:?}",
2006-
merkle_roots, block.block_header.merkle_roots
2011+
merkle_roots,
2012+
block.block_header.merkle_roots
20072013
);
20082014
Err(BlockError::NotValidMerkleTree.into())
20092015
} else {

wallet/src/actors/worker/methods.rs

+7
Original file line numberDiff line numberDiff line change
@@ -300,6 +300,7 @@ impl Worker {
300300
wallet_id: &str,
301301
password: &[u8],
302302
) -> Result<types::UnlockedSessionWallet> {
303+
log::debug!("Unlocking wallet with ID {wallet_id}");
303304
let (salt, iv) = self
304305
.wallets
305306
.wallet_salt_and_iv(wallet_id)
@@ -308,7 +309,9 @@ impl Worker {
308309
| repository::Error::WalletNotFound => Error::WalletNotFound,
309310
err => Error::Repository(err),
310311
})?;
312+
log::debug!("Found salt and IV for wallet with ID {wallet_id}. Deriving key now.");
311313
let key = crypto::key_from_password(password, &salt, self.params.db_hash_iterations);
314+
log::debug!("Derived key for wallet with ID {wallet_id}. Generating session now.");
312315
let session_id: types::SessionId = From::from(crypto::gen_session_id(
313316
&mut self.rng,
314317
&self.params.id_hash_function,
@@ -320,20 +323,24 @@ impl Worker {
320323
let wallet_db = db::EncryptedDb::new(self.db.clone(), prefix, key, iv);
321324

322325
// Check if password-derived key is able to read the special stored value
326+
log::debug!("Wallet {wallet_id} now has a session with ID {session_id}");
323327
wallet_db
324328
.get(&constants::ENCRYPTION_CHECK_KEY)
325329
.map_err(|err| match err {
326330
db::Error::DbKeyNotFound { .. } => Error::WrongPassword,
327331
err => Error::Db(err),
328332
})?;
329333

334+
log::debug!("Encryption key for wallet {wallet_id} seems to be valid. Decrypting wallet object now.");
330335
let wallet = Arc::new(repository::Wallet::unlock(
331336
wallet_id,
332337
session_id.clone(),
333338
wallet_db,
334339
self.params.clone(),
335340
)?);
341+
log::debug!("Extracting public data for wallet {wallet_id}.");
336342
let data = wallet.public_data()?;
343+
log::debug!("Wallet data: {:?}", data);
337344

338345
Ok(types::UnlockedSessionWallet {
339346
wallet,

wallet/src/db/encrypted/engine.rs

+2-1
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
1-
use super::*;
21
use crate::types;
32

3+
use super::*;
4+
45
#[derive(Clone)]
56
pub struct CryptoEngine {
67
key: types::Secret,

wallet/src/db/encrypted/mod.rs

+23
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
use serde::de::DeserializeOwned;
12
use std::sync::Arc;
23

34
use witnet_crypto::cipher;
@@ -101,3 +102,25 @@ impl Database for EncryptedDb {
101102
EncryptedWriteBatch::new(self.prefixer.clone(), self.engine.clone())
102103
}
103104
}
105+
106+
impl GetWith for EncryptedDb {
107+
fn get_with_opt<K, V, F>(&self, key: &Key<K, V>, with: F) -> Result<Option<V>>
108+
where
109+
K: AsRef<[u8]>,
110+
V: DeserializeOwned,
111+
F: Fn(&[u8]) -> Vec<u8>,
112+
{
113+
let prefix_key = self.prefixer.prefix(key);
114+
let enc_key = self.engine.encrypt(&prefix_key)?;
115+
let res = self.as_ref().get(enc_key)?;
116+
117+
match res {
118+
Some(dbvec) => {
119+
let value = self.engine.decrypt_with(&dbvec, with)?;
120+
121+
Ok(Some(value))
122+
}
123+
None => Ok(None),
124+
}
125+
}
126+
}

wallet/src/db/mod.rs

+20
Original file line numberDiff line numberDiff line change
@@ -71,3 +71,23 @@ pub trait WriteBatch {
7171
V: serde::Serialize + ?Sized,
7272
Vref: Borrow<V>;
7373
}
74+
75+
pub trait GetWith {
76+
fn get_with<K, V, F>(&self, key: &Key<K, V>, with: F) -> Result<V>
77+
where
78+
K: AsRef<[u8]> + Debug,
79+
V: serde::de::DeserializeOwned,
80+
F: Fn(&[u8]) -> Vec<u8>,
81+
{
82+
let opt = self.get_with_opt(key, with)?;
83+
84+
opt.ok_or_else(|| Error::DbKeyNotFound {
85+
key: format!("{:?}", key),
86+
})
87+
}
88+
fn get_with_opt<K, V, F>(&self, key: &Key<K, V>, with: F) -> Result<Option<V>>
89+
where
90+
K: AsRef<[u8]>,
91+
V: serde::de::DeserializeOwned,
92+
F: Fn(&[u8]) -> Vec<u8>;
93+
}

wallet/src/db/tests.rs

+21
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
use serde::de::DeserializeOwned;
12
use std::{cell::RefCell, collections::HashMap, rc::Rc};
23

34
use super::*;
@@ -137,6 +138,26 @@ impl IntoIterator for HashMapWriteBatch {
137138
}
138139
}
139140

141+
impl GetWith for HashMapDb {
142+
fn get_with_opt<K, V, F>(&self, key: &Key<K, V>, with: F) -> Result<Option<V>>
143+
where
144+
K: AsRef<[u8]>,
145+
V: DeserializeOwned,
146+
F: Fn(&[u8]) -> Vec<u8>,
147+
{
148+
let k = key.as_ref().to_vec();
149+
let res = match RefCell::borrow(&self.rc).get(&k) {
150+
Some(value) => {
151+
let value = with(value);
152+
Some(bincode::deserialize(&value)?)
153+
}
154+
None => None,
155+
};
156+
157+
Ok(res)
158+
}
159+
}
160+
140161
#[test]
141162
fn test_hashmap_db() {
142163
let db = HashMapDb::default();

wallet/src/repository/wallet/mod.rs

+50-5
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ use state::State;
1313
use witnet_crypto::{
1414
hash::calculate_sha256,
1515
key::{ExtendedPK, ExtendedSK, KeyPath, PK},
16-
signature,
16+
secp256k1, signature,
1717
};
1818
use witnet_data_structures::{
1919
chain::{
@@ -37,7 +37,7 @@ use witnet_util::timestamp::get_timestamp;
3737

3838
use crate::{
3939
constants, crypto,
40-
db::{Database, WriteBatch as _},
40+
db::{Database, GetWith, WriteBatch as _},
4141
model,
4242
params::Params,
4343
types,
@@ -189,7 +189,7 @@ pub struct Wallet<T> {
189189

190190
impl<T> Wallet<T>
191191
where
192-
T: Database,
192+
T: Database + GetWith,
193193
{
194194
/// Generate transient addresses for synchronization purposes
195195
/// This function only creates and inserts addresses
@@ -295,6 +295,11 @@ where
295295
let id = id.to_owned();
296296
let name = db.get_opt(&keys::wallet_name())?;
297297
let description = db.get_opt(&keys::wallet_description())?;
298+
log::debug!(
299+
"Unlocking wallet with name '{}' and description '{}'",
300+
name.clone().unwrap_or_default(),
301+
description.clone().unwrap_or_default()
302+
);
298303
let account = db.get_or_default(&keys::wallet_default_account())?;
299304
let available_accounts = db
300305
.get_opt(&keys::wallet_accounts())?
@@ -333,6 +338,7 @@ where
333338
unconfirmed: balance_info,
334339
confirmed: balance_info,
335340
};
341+
log::debug!("Wallet {id} has balance: {:?}", balance);
336342

337343
let last_sync = db
338344
.get(&keys::wallet_last_sync())
@@ -342,17 +348,34 @@ where
342348
});
343349

344350
let last_confirmed = last_sync;
351+
log::debug!(
352+
"Wallet {id} has last_sync={:?} and last_confirmed={:?}",
353+
last_sync,
354+
last_confirmed
355+
);
345356

346-
let external_key = db.get(&keys::account_key(account, constants::EXTERNAL_KEYCHAIN))?;
357+
let external_key = db.get_with(
358+
&keys::account_key(account, constants::EXTERNAL_KEYCHAIN),
359+
backwards_compatible_keypair_decoding,
360+
)?;
347361
let next_external_index = db.get_or_default(&keys::account_next_index(
348362
account,
349363
constants::EXTERNAL_KEYCHAIN,
350364
))?;
351-
let internal_key = db.get(&keys::account_key(account, constants::INTERNAL_KEYCHAIN))?;
365+
log::debug!(
366+
"Loaded external keys for wallet {id}. Next external index is {next_external_index}."
367+
);
368+
let internal_key = db.get_with(
369+
&keys::account_key(account, constants::INTERNAL_KEYCHAIN),
370+
backwards_compatible_keypair_decoding,
371+
)?;
352372
let next_internal_index = db.get_or_default(&keys::account_next_index(
353373
account,
354374
constants::INTERNAL_KEYCHAIN,
355375
))?;
376+
log::debug!(
377+
"Loaded internal keys for wallet {id}. Next internal index is {next_internal_index}."
378+
);
356379
let keychains = [external_key, internal_key];
357380
let epoch_constants = params.epoch_constants;
358381
let birth_date = db.get(&keys::birth_date()).unwrap_or(CheckpointBeacon {
@@ -2275,6 +2298,15 @@ fn vtt_to_outputs(
22752298
.collect::<Vec<model::Output>>()
22762299
}
22772300

2301+
#[inline]
2302+
fn backwards_compatible_keypair_decoding(bytes: &[u8]) -> Vec<u8> {
2303+
let skip = bytes
2304+
.len()
2305+
.saturating_sub(8 + 2 * secp256k1::constants::SECRET_KEY_SIZE);
2306+
2307+
bytes[skip..].to_vec()
2308+
}
2309+
22782310
#[cfg(test)]
22792311
impl<T> Wallet<T>
22802312
where
@@ -2361,3 +2393,16 @@ fn test_get_tx_ranges_exceed() {
23612393
assert_eq!(pending_range, None);
23622394
assert_eq!(db_range, Some(0..5));
23632395
}
2396+
2397+
#[test]
2398+
fn test_backwards_compatible_keypair_decoding() {
2399+
// Test that the old keypair prefix is detected and omitted
2400+
let old = backwards_compatible_keypair_decoding(&hex::decode("20000000000000001837c1be8e2995ec11cda2b066151be2cfb48adf9e47b151d46adab3a21cdf6720000000000000007923408dadd3c7b56eed15567707ae5e5dca089de972e07f3b860450e2a3b70e").unwrap());
2401+
let new = backwards_compatible_keypair_decoding(&hex::decode("1837c1be8e2995ec11cda2b066151be2cfb48adf9e47b151d46adab3a21cdf6720000000000000007923408dadd3c7b56eed15567707ae5e5dca089de972e07f3b860450e2a3b70e").unwrap());
2402+
assert_eq!(old, new);
2403+
2404+
// Test that shorter strings don't get clipped or panic
2405+
let short = hex::decode("fabadaacabadaa").unwrap();
2406+
let compatible = backwards_compatible_keypair_decoding(&short);
2407+
assert_eq!(short, compatible);
2408+
}

wallet/src/types.rs

+1
Original file line numberDiff line numberDiff line change
@@ -122,6 +122,7 @@ pub struct Account {
122122
pub internal: ExtendedSK,
123123
}
124124

125+
#[derive(Debug)]
125126
pub struct WalletData {
126127
pub id: String,
127128
pub name: Option<String>,

0 commit comments

Comments
 (0)