Skip to content

Commit 8bc1a82

Browse files
committed
wip
1 parent 865d6d9 commit 8bc1a82

File tree

12 files changed

+170
-18
lines changed

12 files changed

+170
-18
lines changed

src/rust/Cargo.lock

Lines changed: 1 addition & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

src/rust/bitbox02-rust/Cargo.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@ doctest = false
2828

2929
[dependencies]
3030
bitbox02 = {path = "../bitbox02"}
31+
bitbox-aes = { path = "../bitbox-aes", features = ["use-wally-sha512"] }
3132
util = { path = "../util" }
3233
erc20_params = { path = "../erc20_params", optional = true }
3334
binascii = { version = "0.1.4", default-features = false, features = ["encode"] }

src/rust/bitbox02-rust/src/hww/api/restore.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -66,7 +66,7 @@ pub async fn from_file(
6666
}
6767

6868
let password = password::enter_twice(hal).await?;
69-
if let Err(err) = bitbox02::keystore::encrypt_and_store_seed(data.get_seed(), &password) {
69+
if let Err(err) = crate::keystore::encrypt_and_store_seed(data.get_seed(), &password) {
7070
hal.ui()
7171
.status(&format!("Could not\nrestore backup\n{:?}", err), false)
7272
.await;
@@ -145,7 +145,7 @@ pub async fn from_mnemonic(
145145
}
146146
};
147147

148-
if let Err(err) = bitbox02::keystore::encrypt_and_store_seed(&seed, &password) {
148+
if let Err(err) = crate::keystore::encrypt_and_store_seed(&seed, &password) {
149149
hal.ui()
150150
.status(&format!("Could not\nrestore backup\n{:?}", err), false)
151151
.await;

src/rust/bitbox02-rust/src/keystore.rs

Lines changed: 54 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,60 @@ pub mod ed25519;
1818
use alloc::vec::Vec;
1919

2020
use crate::bip32;
21-
use bitbox02::keystore;
21+
use bitbox02::{keystore, memory, securechip};
22+
23+
#[derive(Debug)]
24+
pub enum Error {
25+
AlreadyInitialized,
26+
Memory,
27+
SeedSize,
28+
SecureChip,
29+
IncorrectPassword,
30+
}
31+
32+
fn validate_seed_length(len: usize) -> Result<(), Error> {
33+
match len {
34+
16 | 24 | 32 => Ok(()),
35+
_ => Err(Error::SeedSize),
36+
}
37+
}
38+
39+
fn get_and_decrypt_seed(password: &str) -> Result<zeroize::Zeroizing<Vec<u8>>, Error> {
40+
let encrypted_seed_and_hmac =
41+
memory::get_encrypted_seed_and_hmac().map_err(|_| Error::Memory)?;
42+
43+
// TODO check actual SC result
44+
let secret: zeroize::Zeroizing<Vec<u8>> =
45+
securechip::stretch_password(password).map_err(|_| Error::SecureChip)?;
46+
let seed = bitbox_aes::decrypt_with_hmac(&secret, &encrypted_seed_and_hmac)
47+
.map_err(|_| Error::IncorrectPassword)?;
48+
validate_seed_length(seed.len())?;
49+
Ok(seed)
50+
}
51+
52+
pub fn encrypt_and_store_seed(seed: &[u8], password: &str) -> Result<(), Error> {
53+
if memory::is_initialized() {
54+
return Err(Error::AlreadyInitialized);
55+
}
56+
keystore::lock();
57+
validate_seed_length(seed.len())?;
58+
securechip::init_new_password(password).map_err(|_| Error::SecureChip)?;
59+
let secret: zeroize::Zeroizing<Vec<u8>> =
60+
securechip::stretch_password(password).map_err(|_| Error::SecureChip)?;
61+
// TODO: set IV randomly using random_32_bytes().
62+
let iv = &[0u8; 16];
63+
let encrypted_seed: Vec<u8> =
64+
bitbox_aes::encrypt_with_hmac(iv, secret.as_slice().try_into().unwrap(), seed);
65+
memory::set_encrypted_seed_and_hmac(&encrypted_seed).map_err(|_| Error::Memory)?;
66+
67+
// Verify seed.
68+
if get_and_decrypt_seed(password)?.as_slice() != seed {
69+
// TODO: reset hww
70+
return Err(Error::Memory);
71+
}
72+
73+
Ok(())
74+
}
2275

2376
/// Derives an xpub from the keystore seed at the given keypath.
2477
pub fn get_xpub(keypath: &[u32]) -> Result<bip32::Xpub, ()> {

src/rust/bitbox02-sys/build.rs

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -65,7 +65,6 @@ const ALLOWLIST_FNS: &[&str] = &[
6565
"keystore_copy_seed",
6666
"keystore_create_and_store_seed",
6767
"keystore_encode_xpub_at_keypath",
68-
"keystore_encrypt_and_store_seed",
6968
"keystore_get_bip39_mnemonic",
7069
"keystore_get_bip39_word",
7170
"keystore_get_ed25519_seed",
@@ -99,6 +98,8 @@ const ALLOWLIST_FNS: &[&str] = &[
9998
"memory_set_initialized",
10099
"memory_set_mnemonic_passphrase_enabled",
101100
"memory_set_seed_birthdate",
101+
"memory_set_encrypted_seed_and_hmac",
102+
"memory_get_encrypted_seed_and_hmac",
102103
"memory_setup",
103104
"menu_create",
104105
"mock_memory_factoryreset",
@@ -127,6 +128,8 @@ const ALLOWLIST_FNS: &[&str] = &[
127128
"securechip_model",
128129
"securechip_monotonic_increments_remaining",
129130
"securechip_u2f_counter_set",
131+
"securechip_init_new_password",
132+
"securechip_stretch_password",
130133
"smarteeprom_bb02_config",
131134
"status_create",
132135
"trinary_choice_create",
@@ -139,6 +142,7 @@ const ALLOWLIST_FNS: &[&str] = &[
139142
"wally_free_string",
140143
"wally_get_secp_context",
141144
"wally_sha512",
145+
"cipher_aes_hmac_encrypt",
142146
];
143147

144148
const RUSTIFIED_ENUMS: &[&str] = &[

src/rust/bitbox02-sys/wrapper.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@
1313
// limitations under the License.
1414

1515
#include <bip32.h>
16+
#include <cipher/cipher.h>
1617
#include <keystore.h>
1718
#include <memory/bitbox02_smarteeprom.h>
1819
#include <memory/memory.h>

src/rust/bitbox02/src/cipher.rs

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
// Copyright 2020 Shift Crypto AG
2+
//
3+
// Licensed under the Apache License, Version 2.0 (the "License");
4+
// you may not use this file except in compliance with the License.
5+
// You may obtain a copy of the License at
6+
//
7+
// http://www.apache.org/licenses/LICENSE-2.0
8+
//
9+
// Unless required by applicable law or agreed to in writing, software
10+
// distributed under the License is distributed on an "AS IS" BASIS,
11+
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
// See the License for the specific language governing permissions and
13+
// limitations under the License.
14+
15+
use alloc::vec::Vec;
16+
17+
pub fn aes_hmac_encrypt(input: &[u8], key: &[u8]) -> Result<zeroize::Zeroizing<Vec<u8>>, ()> {
18+
let mut output = zeroize::Zeroizing::new(vec![0u8; input.len() + 64]);
19+
let mut output_size: usize = output.len();
20+
match unsafe {
21+
bitbox02_sys::cipher_aes_hmac_encrypt(
22+
input.as_ptr(),
23+
input.len() as _,
24+
output.as_mut_ptr(),
25+
&mut output_size,
26+
key.as_ptr(),
27+
)
28+
} {
29+
true => {
30+
output.truncate(output_size);
31+
Ok(output)
32+
}
33+
false => Err(()),
34+
}
35+
}

src/rust/bitbox02/src/keystore.rs

Lines changed: 0 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -293,19 +293,6 @@ pub fn bip39_mnemonic_to_seed(mnemonic: &str) -> Result<zeroize::Zeroizing<Vec<u
293293
}
294294
}
295295

296-
pub fn encrypt_and_store_seed(seed: &[u8], password: &str) -> Result<(), Error> {
297-
match unsafe {
298-
bitbox02_sys::keystore_encrypt_and_store_seed(
299-
seed.as_ptr(),
300-
seed.len(),
301-
crate::util::str_to_cstr_vec(password).unwrap().as_ptr(),
302-
)
303-
} {
304-
keystore_error_t::KEYSTORE_OK => Ok(()),
305-
err => Err(err.into()),
306-
}
307-
}
308-
309296
pub fn get_ed25519_seed() -> Result<zeroize::Zeroizing<Vec<u8>>, ()> {
310297
let mut seed = zeroize::Zeroizing::new([0u8; 96].to_vec());
311298
match unsafe { bitbox02_sys::keystore_get_ed25519_seed(seed.as_mut_ptr()) } {

src/rust/bitbox02/src/lib.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,7 @@ use alloc::string::String;
3434
pub mod testing;
3535

3636
pub mod bip32;
37+
pub mod cipher;
3738
pub mod keystore;
3839
pub mod memory;
3940
pub mod random;

src/rust/bitbox02/src/memory.rs

Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@
1515

1616
extern crate alloc;
1717
use alloc::string::String;
18+
use alloc::vec::Vec;
1819

1920
// deduct one for the null terminator.
2021
pub const DEVICE_NAME_MAX_LEN: usize = bitbox02_sys::MEMORY_DEVICE_NAME_MAX_LEN as usize - 1;
@@ -160,13 +161,57 @@ pub fn multisig_get_by_hash(hash: &[u8]) -> Option<String> {
160161
}
161162
}
162163

164+
pub fn set_encrypted_seed_and_hmac(encrypted_seed_and_hmac: &[u8]) -> Result<(), ()> {
165+
match unsafe {
166+
bitbox02_sys::memory_set_encrypted_seed_and_hmac(
167+
encrypted_seed_and_hmac.as_ptr(),
168+
encrypted_seed_and_hmac.len().try_into().unwrap(),
169+
)
170+
} {
171+
true => Ok(()),
172+
false => Err(()),
173+
}
174+
}
175+
176+
pub fn get_encrypted_seed_and_hmac() -> Result<Vec<u8>, ()> {
177+
let mut out = vec![0u8; 96];
178+
let mut out_size = 0u8;
179+
match unsafe {
180+
bitbox02_sys::memory_get_encrypted_seed_and_hmac(out.as_mut_ptr(), &mut out_size)
181+
} {
182+
true => {
183+
out.truncate(out_size as usize);
184+
Ok(out)
185+
}
186+
false => Err(()),
187+
}
188+
}
189+
163190
#[cfg(test)]
164191
mod tests {
165192
use super::*;
193+
use crate::testing::mock_memory;
166194

167195
#[test]
168196
fn test_get_attestation_bootloader_hash() {
197+
mock_memory();
198+
169199
let expected: [u8; 32] = *b"\x71\x3d\xf0\xd5\x8c\x71\x7d\x40\x31\x78\x7c\xdc\x8f\xa3\x5b\x90\x25\x82\xbe\x6a\xb6\xa2\x2e\x09\xde\x44\x77\xd3\x0e\x22\x30\xfc";
170200
assert_eq!(get_attestation_bootloader_hash(), expected);
171201
}
202+
203+
#[test]
204+
fn test_encrypted_seed_and_hmac_roundtrip() {
205+
for len in 0..=96 {
206+
mock_memory();
207+
let value: Vec<u8> = (0..len as u8).collect();
208+
assert!(set_encrypted_seed_and_hmac(&value).is_ok());
209+
assert_eq!(get_encrypted_seed_and_hmac().unwrap(), value);
210+
}
211+
{
212+
mock_memory();
213+
let value: Vec<u8> = (0..97 as u8).collect();
214+
assert!(set_encrypted_seed_and_hmac(&value).is_err());
215+
}
216+
}
172217
}

0 commit comments

Comments
 (0)