Skip to content

Commit b8b6d06

Browse files
committed
keystore: allow creation of 16 byte seeds
Corresponds to 12 recovery words / 128 bits.
1 parent 8ffbb5e commit b8b6d06

File tree

7 files changed

+43
-22
lines changed

7 files changed

+43
-22
lines changed

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
- Attempt to fix flaky SD behavior
55
- Add securechip_model to DeviceInfo: ATECCC608A or ATECC608B.
66
- Added reboot purpose for clearer UX: "Proceed to upgrade?" vs. "Go to startup settings?"
7+
- Allow creation of 128 bit seeds (12 BIP39 recovery words)
78

89
## 9.5.0 [released 2021-03-10]
910
- RestoreFrommnemonic: ported to Rust. Will now return UserAbortError on user abort instead of GenericError.

py/bitbox02/bitbox02/bitbox02/bitbox02.py

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -157,14 +157,18 @@ def set_device_name(self, device_name: str) -> None:
157157
request.device_name.name = device_name
158158
self._msg_query(request, expected_response="success")
159159

160-
def set_password(self) -> bool:
160+
def set_password(self, entropy_size: int = 32) -> bool:
161161
"""
162162
Returns True if the user entered the password correctly (passwords match).
163-
Returns False otherwise.
163+
Returns False otherwise. Entropy size determines the seed size in bytes; must be 16 or 32.
164164
"""
165+
assert entropy_size in (16, 32)
166+
if entropy_size == 16:
167+
self._require_atleast(semver.VersionInfo(9, 6, 0))
168+
165169
# pylint: disable=no-member
166170
request = hww.Request()
167-
request.set_password.entropy = os.urandom(32)
171+
request.set_password.entropy = os.urandom(entropy_size)
168172
try:
169173
self._msg_query(request, expected_response="success")
170174
except Bitbox02Exception as err:

src/keystore.c

Lines changed: 10 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -257,8 +257,14 @@ bool keystore_encrypt_and_store_seed(
257257
return true;
258258
}
259259

260-
bool keystore_create_and_store_seed(const char* password, const uint8_t* host_entropy)
260+
bool keystore_create_and_store_seed(
261+
const char* password,
262+
const uint8_t* host_entropy,
263+
size_t host_entropy_size)
261264
{
265+
if (host_entropy_size != 16 && host_entropy_size != 32) {
266+
return false;
267+
}
262268
if (KEYSTORE_MAX_SEED_LENGTH != RANDOM_NUM_SIZE) {
263269
Abort("keystore create: size mismatch");
264270
}
@@ -267,7 +273,7 @@ bool keystore_create_and_store_seed(const char* password, const uint8_t* host_en
267273
random_32_bytes(seed);
268274

269275
// Mix in Host entropy.
270-
for (size_t i = 0; i < KEYSTORE_MAX_SEED_LENGTH; i++) {
276+
for (size_t i = 0; i < host_entropy_size; i++) {
271277
seed[i] ^= host_entropy[i];
272278
}
273279

@@ -282,10 +288,10 @@ bool keystore_create_and_store_seed(const char* password, const uint8_t* host_en
282288
return false;
283289
}
284290

285-
for (size_t i = 0; i < KEYSTORE_MAX_SEED_LENGTH; i++) {
291+
for (size_t i = 0; i < host_entropy_size; i++) {
286292
seed[i] ^= password_salted_hashed[i];
287293
}
288-
return keystore_encrypt_and_store_seed(seed, KEYSTORE_MAX_SEED_LENGTH, password);
294+
return keystore_encrypt_and_store_seed(seed, host_entropy_size, password);
289295
}
290296

291297
static void _free_string(char** str)

src/keystore.h

Lines changed: 9 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -68,11 +68,16 @@ USE_RESULT bool keystore_encrypt_and_store_seed(
6868
const char* password);
6969

7070
/**
71-
Generates 32 bytes of entropy, mixes it with host_entropy, and stores it encrypted with the
72-
password.
73-
@param[in] host_entropy 32 bytes of entropy to be mixed in.
71+
Generates the seed, mixes it with host_entropy, and stores it encrypted with the
72+
password. The size of the host entropy determines the size of the seed. Can be either 16 or 32
73+
bytes, resulting in 12 or 24 BIP39 recovery words.
74+
@param[in] host_entropy bytes of entropy to be mixed in.
75+
@param[in] host_entropy_size must be 16 or 32.
7476
*/
75-
USE_RESULT bool keystore_create_and_store_seed(const char* password, const uint8_t* host_entropy);
77+
USE_RESULT bool keystore_create_and_store_seed(
78+
const char* password,
79+
const uint8_t* host_entropy,
80+
size_t host_entropy_size);
7681

7782
/** Unlocks the keystore seed or checks the password:
7883
* If the keystore is locked, it decrypts and loads the seed, unlocking the keystore:

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

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -16,23 +16,23 @@ use super::Error;
1616
use crate::pb;
1717

1818
use crate::workflow::password;
19-
use core::convert::TryInto;
2019
use pb::response::Response;
2120

2221
/// Handles the SetPassword api call. This has the user enter a password twice and creates the
2322
/// seed/keystore. After this call is finished, the keystore is fully unlocked.
2423
///
25-
/// `entropy` must be exactly 32 bytes and provides additional entropy used when
26-
/// creating the seed.
24+
/// `entropy` must be exactly 16 or 32 bytes and provides additional entropy used when creating the
25+
/// seed. If 16 bytes are provided, the seed will also be 16 bytes long, corresponding to 12 BIP39
26+
/// recovery words. If 32 bytes are provided, the seed will also be 32 bytes long, corresponding to
27+
/// 24 BIP39 recovery words.
2728
pub async fn process(
2829
pb::SetPasswordRequest { entropy }: &pb::SetPasswordRequest,
2930
) -> Result<Response, Error> {
30-
let entropy32: [u8; 32] = match entropy.as_slice().try_into() {
31-
Err(_) => return Err(Error::InvalidInput),
32-
Ok(e) => e,
33-
};
31+
if entropy.len() != 16 && entropy.len() != 32 {
32+
return Err(Error::InvalidInput);
33+
}
3434
let password = password::enter_twice().await?;
35-
if !bitbox02::keystore::create_and_store_seed(&password, &entropy32) {
35+
if !bitbox02::keystore::create_and_store_seed(&password, &entropy) {
3636
return Err(Error::Generic);
3737
}
3838
if bitbox02::keystore::unlock(&password).is_err() {

src/rust/bitbox02/src/keystore.rs

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -62,9 +62,13 @@ pub fn unlock_bip39(mnemonic_passphrase: &SafeInputString) -> Result<(), Error>
6262
}
6363
}
6464

65-
pub fn create_and_store_seed(password: &SafeInputString, host_entropy: &[u8; 32]) -> bool {
65+
pub fn create_and_store_seed(password: &SafeInputString, host_entropy: &[u8]) -> bool {
6666
unsafe {
67-
bitbox02_sys::keystore_create_and_store_seed(password.as_cstr(), host_entropy.as_ptr())
67+
bitbox02_sys::keystore_create_and_store_seed(
68+
password.as_cstr(),
69+
host_entropy.as_ptr(),
70+
host_entropy.len() as _,
71+
)
6872
}
6973
}
7074

test/device-test/src/test_backup.c

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -126,7 +126,8 @@ int main(void)
126126

127127
screen_print_debug("Creating initial backup...", 1000);
128128

129-
if (!keystore_create_and_store_seed("device-test", "host-entropy")) {
129+
uint8_t host_entropy[32] = "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa";
130+
if (!keystore_create_and_store_seed("device-test", host_entropy, sizeof(host_entropy))) {
130131
Abort("Failed to create keystore");
131132
}
132133
uint8_t remaining_attempts;

0 commit comments

Comments
 (0)