rust: rename bitbox02-noise -> bitbox-noise
What changed, and why it matters
This commit is a simple rename of a Rust crate from 'bitbox02-noise' to 'bitbox-noise'. The code itself is unchanged; only file paths, package names, and import references are updated. There is no security fix or vulnerability introduced.
No security action required. Treat as routine refactoring/rename.
Security signals we found
No strong security signals were identified.
Evidence from the diff
The commit renames the Rust crate bitbox02-noise to bitbox-noise across Cargo manifests, Cargo.lock files, source imports, and directory structure. The diff shows identical file content being moved from src/rust/bitbox02-noise/ to src/rust/bitbox-noise/ and all dependent crates updating their dependency declarations and use statements accordingly. No functional changes to the Noise protocol implementation, cryptography, or logic are present.
Changed components
src/rust/bitbox-noise crate (formerly bitbox02-noise)src/rust/bitbox02-rustsrc/rust/bitbox02-rust-csrc/rust/bitbox02test/simulator-graphicaltest/simulator-graphical-bb03Inspect captured patch +480 / −480
diff --git a/src/rust/Cargo.lock b/src/rust/Cargo.lock
index 5f25a2d..e8eb196 100644
--- a/src/rust/Cargo.lock
+++ b/src/rust/Cargo.lock
@@ -186,6 +186,16 @@ dependencies = [
"cmake",
]
+[[package]]
+name = "bitbox-noise"
+version = "0.1.0"
+dependencies = [
+ "bitbox-hal",
+ "noise-protocol",
+ "noise-rust-crypto",
+ "x25519-dalek",
+]
+
[[package]]
name = "bitbox-platform-host"
version = "0.1.0"
@@ -243,10 +253,10 @@ dependencies = [
"bitbox-bytequeue",
"bitbox-framed-serial-link",
"bitbox-hal",
+ "bitbox-noise",
"bitbox-securechip",
"bitbox-securechip-sys",
"bitbox-usb-report-queue",
- "bitbox02-noise",
"bitbox02-sys",
"futures-lite",
"grounded",
@@ -257,16 +267,6 @@ dependencies = [
"zeroize",
]
-[[package]]
-name = "bitbox02-noise"
-version = "0.1.0"
-dependencies = [
- "bitbox-hal",
- "noise-protocol",
- "noise-rust-crypto",
- "x25519-dalek",
-]
-
[[package]]
name = "bitbox02-rust"
version = "0.1.0"
@@ -281,12 +281,12 @@ dependencies = [
"bitbox-da14531",
"bitbox-executor",
"bitbox-hal",
+ "bitbox-noise",
"bitbox-platform-host",
"bitbox-secp256k1",
"bitbox-u2fhid",
"bitbox-usb-report-queue",
"bitbox02",
- "bitbox02-noise",
"bitcoin",
"bitcoin_hashes",
"blake2",
@@ -324,9 +324,9 @@ dependencies = [
"bitbox-da14531",
"bitbox-framed-serial-link",
"bitbox-hal",
+ "bitbox-noise",
"bitbox-usb-report-queue",
"bitbox02",
- "bitbox02-noise",
"bitbox02-rust",
"bitcoin",
"cortex-m",
diff --git a/src/rust/Cargo.toml b/src/rust/Cargo.toml
index dbac1c8..162454d 100644
--- a/src/rust/Cargo.toml
+++ b/src/rust/Cargo.toml
@@ -15,7 +15,7 @@ members = [
"bitbox-core-utils",
"bitbox-framed-serial-link",
"util",
- "bitbox02-noise",
+ "bitbox-noise",
"bitbox02",
"bitbox-secp256k1",
"bitbox02-sys",
diff --git a/src/rust/bitbox-noise/Cargo.toml b/src/rust/bitbox-noise/Cargo.toml
new file mode 100644
index 0000000..e6b6efe
--- /dev/null
+++ b/src/rust/bitbox-noise/Cargo.toml
@@ -0,0 +1,27 @@
+# SPDX-License-Identifier: Apache-2.0
+
+[package]
+name = "bitbox-noise"
+version = "0.1.0"
+authors = ["Shift Crypto AG <support@bitbox.swiss>"]
+edition = "2024"
+description = "BitBox02 noise protocol primitives"
+license = "Apache-2.0"
+
+[dependencies.bitbox-hal]
+path = "../bitbox-hal"
+
+[dependencies.noise-protocol]
+version = "0.2.0"
+default-features = false
+features = ["use_alloc"]
+
+[dependencies.noise-rust-crypto]
+version = "0.6.2"
+default-features = false
+features = ["use-sha2", "use-chacha20poly1305"]
+
+[dependencies.x25519-dalek]
+version = "2.0.0"
+default-features = false
+features = ["static_secrets"]
diff --git a/src/rust/bitbox-noise/src/lib.rs b/src/rust/bitbox-noise/src/lib.rs
new file mode 100644
index 0000000..7932dec
--- /dev/null
+++ b/src/rust/bitbox-noise/src/lib.rs
@@ -0,0 +1,21 @@
+// SPDX-License-Identifier: Apache-2.0
+
+//! This crate implements the state machine for establishing and using a noise channel.
+//!
+//! The BitBox02 uses the following noise protocol config: `Noise_XX_25519_ChaChaPoly_SHA256`.
+//! [noiseexplorer.com/patterns/XX](https://noiseexplorer.com/patterns/XX/).
+
+#![no_std]
+
+#[cfg(test)]
+#[macro_use]
+extern crate std;
+
+mod noise_xx;
+pub mod testing;
+mod x25519;
+
+pub use noise_xx::{Error, HandshakeHash, HandshakeResult, State};
+pub use x25519::{X25519, genkey};
+
+pub use noise_rust_crypto::sensitive::Sensitive;
diff --git a/src/rust/bitbox-noise/src/noise_xx.rs b/src/rust/bitbox-noise/src/noise_xx.rs
new file mode 100644
index 0000000..ab7f975
--- /dev/null
+++ b/src/rust/bitbox-noise/src/noise_xx.rs
@@ -0,0 +1,282 @@
+// SPDX-License-Identifier: Apache-2.0
+
+extern crate alloc;
+use alloc::vec::Vec;
+
+use bitbox_hal::Random;
+
+use crate::x25519::{PrivateKey, PublicKey, X25519, genkey};
+use noise_rust_crypto::{ChaCha20Poly1305, Sha256, sensitive::Sensitive};
+
+/// Specialization of noise_protocol::HandshakeState, picking the implementations for Diffie
+/// Hellman, Cipher and Hash.
+/// cbindgen:ignore
+pub type HandshakeState = noise_protocol::HandshakeState<X25519, ChaCha20Poly1305, Sha256>;
+
+/// Common handshake hash that can be derived by both parties. The pairing code is derived from it.
+pub type HandshakeHash = [u8; 32];
+
+/// Manages a noise communication channel, including handshake and message encryption/decryption.
+///
+/// The required state flow is:
+///
+/// `Nothing --init()--> Initialized --handshake()--> Initialized --handshake() --> Ready.`
+pub enum State {
+ /// Noise not in use yet.
+ Nothing,
+ /// Initialized, ready for handhshake messages.
+ Initialized(HandshakeState),
+ /// Handshake is completed. Ready to confirm the pairing and process messages.
+ Ready {
+ /// Defaults to true. No encryption/decryption is possible until `set_pairing_verified()` is
+ /// called.
+ pairing_verification_required: bool,
+ /// Fetch with `get_handshake_hash()`, used to display the pairing code to verify the
+ /// pairing.
+ handshake_hash: HandshakeHash,
+ /// Communication partner's static public key. Can be used to remember the communication
+ /// partner, so the pairing verification can be skipped the next time.
+ remote_static_pubkey: PublicKey,
+ /// To encrypt outgoing messages.
+ send: noise_protocol::CipherState<ChaCha20Poly1305>,
+ /// To decrypt incoming messages.
+ receive: noise_protocol::CipherState<ChaCha20Poly1305>,
+ },
+}
+
+/// See documentation of `State.handshake()`.
+pub enum HandshakeResult {
+ Response(Vec<u8>),
+ Done,
+}
+
+/// Common error returned by all noise state functions.
+#[derive(Debug)]
+pub enum Error {
+ /// Cannot use this until `set_pairing_verified()` has been called.
+ PairingVerificationRequired,
+ /// Unexpected/internal errors returned by the `noise_protocol` crate.
+ Noise,
+ /// This function was called at the wrong time (see `State` documentation to see in which order
+ /// the functions need be called).
+ WrongState,
+}
+
+impl core::convert::From<Error> for () {
+ fn from(_error: Error) -> Self {}
+}
+
+impl core::convert::From<noise_protocol::Error> for Error {
+ fn from(_error: noise_protocol::Error) -> Self {
+ Error::Noise
+ }
+}
+
+impl State {
+ /// Can be called at any time to reset the state.
+ pub fn reset(&mut self) {
+ *self = State::Nothing;
+ }
+
+ /// Can be called at any time to start waiting for a new communication channel.
+ ///
+ /// `static_private_key` is the local static key. It can be generated using
+ /// `genkey()` with a HAL random source and then persisted. `random` is used to generate a
+ /// fresh ephemeral private key for each session.
+ pub fn init(&mut self, static_private_key: Sensitive<PrivateKey>, random: &mut impl Random) {
+ let ephemeral_private_key = genkey(random);
+ let hs = HandshakeState::new(
+ noise_protocol::patterns::noise_xx(),
+ false, /* is_initiator = false; the app is the initiator */
+ &b"Noise_XX_25519_ChaChaPoly_SHA256"[..],
+ Some(static_private_key),
+ Some(ephemeral_private_key),
+ None,
+ None,
+ );
+ *self = State::Initialized(hs);
+ }
+
+ /// In `noise_XX`, there are 3 handshake messages in total (2 by remote, 1 by us):
+ ///
+ /// `reqA(remote -> local); respB(local -> remote); reqC(remote -> local)`
+ ///
+ /// This function needs to be called twice (once for `A/B`, once for `C`).
+ ///
+ /// See also: [noiseexplorer.com/patterns/XX](https://noiseexplorer.com/patterns/XX/).
+ ///
+ /// Returns: `Response(<respB>)` (see above) for the 1st handshake message, and `Done` for the
+ /// 2nd handshake message.
+ pub fn handshake(&mut self, msg: &[u8]) -> Result<HandshakeResult, Error> {
+ use core::convert::TryInto;
+ match self {
+ State::Initialized(handshake_state) => {
+ let payload = match handshake_state.read_message_vec(msg) {
+ Ok(payload) => payload,
+ Err(err) => {
+ self.reset();
+ return Err(err.into());
+ }
+ };
+
+ if handshake_state.completed() {
+ let (receive, send) = handshake_state.get_ciphers();
+ let remote_static_pubkey = handshake_state.get_rs().ok_or(Error::Noise)?;
+ *self = State::Ready {
+ pairing_verification_required: true,
+ handshake_hash: handshake_state.get_hash().try_into().unwrap(),
+ remote_static_pubkey,
+ send,
+ receive,
+ };
+ return Ok(HandshakeResult::Done);
+ }
+ Ok(HandshakeResult::Response(
+ handshake_state.write_message_vec(&payload)?,
+ ))
+ }
+ _ => Err(Error::WrongState),
+ }
+ }
+
+ pub fn get_handshake_hash(&self) -> Result<HandshakeHash, Error> {
+ match self {
+ State::Ready { handshake_hash, .. } => Ok(*handshake_hash),
+ _ => Err(Error::WrongState),
+ }
+ }
+
+ /// The communication partner's identity.
+ pub fn remote_static_pubkey(&self) -> Result<PublicKey, Error> {
+ match self {
+ State::Ready {
+ remote_static_pubkey,
+ ..
+ } => Ok(*remote_static_pubkey),
+ _ => Err(Error::WrongState),
+ }
+ }
+
+ /// Mark the pairing as verified, unlocking encryption and decryption.
+ ///
+ /// This should only be called after the user verified the pairing code (see
+ /// `get_handshake_hash()`), or has done so in the past for the same remote communucation
+ /// partner (see `remote_static_pubkey()`).
+ pub fn set_pairing_verified(&mut self) -> Result<(), Error> {
+ match self {
+ State::Ready {
+ pairing_verification_required,
+ ..
+ } => {
+ *pairing_verification_required = false;
+ Ok(())
+ }
+ _ => Err(Error::WrongState),
+ }
+ }
+
+ /// Decrypt an encrypted message.
+ pub fn decrypt(&mut self, msg: &[u8]) -> Result<Vec<u8>, Error> {
+ match self {
+ State::Ready {
+ pairing_verification_required: true,
+ ..
+ } => Err(Error::PairingVerificationRequired),
+
+ State::Ready {
+ pairing_verification_required: false,
+ receive,
+ ..
+ } => match receive.decrypt_vec(msg) {
+ Ok(r) => Ok(r),
+ Err(()) => Err(Error::Noise),
+ },
+ _ => Err(Error::WrongState),
+ }
+ }
+
+ /// Encrypt a message. The ciphertext is appended to `out`.
+ pub fn encrypt(&mut self, msg: &[u8], out: &mut Vec<u8>) -> Result<(), Error> {
+ match self {
+ State::Ready {
+ pairing_verification_required: true,
+ ..
+ } => Err(Error::PairingVerificationRequired),
+
+ State::Ready {
+ pairing_verification_required: false,
+ send,
+ ..
+ } => {
+ let start = out.len();
+ // Extra 16 bytes for the aead authentication tag (MAC).
+ let encrypted_len = msg.len() + 16;
+ // Make space for result.
+ out.resize(start + encrypted_len, 0);
+ // This also adds the MAC.
+ send.encrypt(msg, &mut out[start..]);
+ Ok(())
+ }
+ _ => Err(Error::WrongState),
+ }
+ }
+}
+
+#[cfg(test)]
+mod tests {
+ use super::*;
+ use crate::testing::{MockRandom, make_mock_host};
+
+ impl HandshakeResult {
+ fn response(self) -> Result<Vec<u8>, ()> {
+ match self {
+ HandshakeResult::Response(r) => Ok(r),
+ HandshakeResult::Done => Err(()),
+ }
+ }
+ fn done(self) -> Result<(), ()> {
+ match self {
+ HandshakeResult::Response(_) => Err(()),
+ HandshakeResult::Done => Ok(()),
+ }
+ }
+ }
+
+ #[test]
+ pub fn test_full() {
+ let mut bb02_random = MockRandom;
+ let bb02_static_key = genkey(&mut bb02_random);
+
+ let mut host = make_mock_host();
+ let mut bb02 = State::Nothing;
+ bb02.init(bb02_static_key, &mut bb02_random);
+
+ let host_handshake_1 = host.write_message_vec(b"").unwrap();
+ let bb02_handshake_1 = bb02
+ .handshake(&host_handshake_1)
+ .unwrap()
+ .response()
+ .unwrap();
+
+ let host_handshake_2 = {
+ let payload = host.read_message_vec(&bb02_handshake_1).unwrap();
+ host.write_message_vec(&payload).unwrap()
+ };
+ bb02.handshake(&host_handshake_2).unwrap().done().unwrap();
+
+ bb02.set_pairing_verified().unwrap();
+
+ let (mut host_send, mut host_recv) = host.get_ciphers();
+
+ let encrypted = host_send.encrypt_vec(b"message from host");
+ let decrypted = bb02.decrypt(&encrypted).unwrap();
+ assert_eq!(decrypted, b"message from host");
+
+ let mut encrypted = b"prefix".to_vec();
+ bb02.encrypt(b"message from bb02", &mut encrypted).unwrap();
+ let (prefix, encrypted) = encrypted.split_at(b"prefix".len());
+ assert_eq!(&prefix, b"prefix");
+ let decrypted = host_recv.decrypt_vec(encrypted).unwrap();
+ assert_eq!(decrypted, b"message from bb02");
+ }
+}
diff --git a/src/rust/bitbox-noise/src/testing.rs b/src/rust/bitbox-noise/src/testing.rs
new file mode 100644
index 0000000..9c8f4d2
--- /dev/null
+++ b/src/rust/bitbox-noise/src/testing.rs
@@ -0,0 +1,37 @@
+// SPDX-License-Identifier: Apache-2.0
+
+use crate::noise_xx::HandshakeState;
+use crate::x25519::genkey;
+
+pub struct MockRandom;
+
+impl bitbox_hal::Random for MockRandom {
+ fn factory_randomness(&mut self) -> &'static [u8; 32] {
+ unreachable!()
+ }
+
+ fn mcu_32_bytes(&mut self, out: &mut [u8; 32]) {
+ out.copy_from_slice(b"llllllllllllllllllllllllllllllll")
+ }
+}
+
+pub type TestHandshakeState = HandshakeState;
+
+pub fn make_host(random: &mut impl bitbox_hal::Random) -> TestHandshakeState {
+ let host_static_key = genkey(random);
+ let host_ephemeral_key = genkey(random);
+ TestHandshakeState::new(
+ noise_protocol::patterns::noise_xx().clone(),
+ true,
+ &b"Noise_XX_25519_ChaChaPoly_SHA256"[..],
+ Some(host_static_key),
+ Some(host_ephemeral_key),
+ None,
+ None,
+ )
+}
+
+pub fn make_mock_host() -> TestHandshakeState {
+ let mut random = MockRandom;
+ make_host(&mut random)
+}
diff --git a/src/rust/bitbox-noise/src/x25519.rs b/src/rust/bitbox-noise/src/x25519.rs
new file mode 100644
index 0000000..55c5703
--- /dev/null
+++ b/src/rust/bitbox-noise/src/x25519.rs
@@ -0,0 +1,58 @@
+// SPDX-License-Identifier: Apache-2.0
+
+//! This module implements the X25519 trait needed by noise_protocol
+//! by using the x25519_dalek crate. It is adapted from
+//! https://github.com/sopium/noise-rust/blob/76fb694f06b429879c264087f496958a99710356/noise-rust-crypto/src/lib.rs#L31,
+//! but uses the HAL random source to generate keys.
+
+use bitbox_hal::Random;
+use core::ops::Deref;
+use noise_protocol::U8Array;
+use noise_rust_crypto::sensitive::Sensitive;
+
+pub struct X25519;
+
+pub type PrivateKey = [u8; 32];
+pub type PublicKey = [u8; 32];
+
+/// Generate a fresh x25519 private key by reading 32 random bytes from the HAL and applying
+/// the standard clamping.
+pub fn genkey(random: &mut impl Random) -> Sensitive<PrivateKey> {
+ let mut k: Sensitive<PrivateKey> = Sensitive::new();
+ random.mcu_32_bytes(&mut k);
+
+ // Copied from: https://github.com/sopium/noise-rust/blob/76fb694f06b429879c264087f496958a99710356/noise-rust-crypto/src/lib.rs#L49-L51
+ // which in turn copied it from:
+ // https://github.com/dalek-cryptography/x25519-dalek/blob/ecd6be674850a99ad26404f6aa29b0cf79642b97/src/x25519.rs#L162-L164
+ // which is also in our vendored deps: `vendor/x25519-dalek/src/x25519.rs`.
+ k[0] &= 248;
+ k[31] &= 127;
+ k[31] |= 64;
+
+ k
+}
+
+impl noise_protocol::DH for X25519 {
+ type Key = Sensitive<PrivateKey>;
+ type Pubkey = PublicKey;
+ type Output = [u8; 32];
+
+ fn name() -> &'static str {
+ "25519"
+ }
+
+ fn genkey() -> Self::Key {
+ panic!("implicit X25519 key generation is unsupported; generate keys explicitly")
+ }
+
+ fn pubkey(k: &Self::Key) -> Self::Pubkey {
+ let static_secret = x25519_dalek::StaticSecret::from(*k.deref());
+ *x25519_dalek::PublicKey::from(&static_secret).as_bytes()
+ }
+
+ fn dh(k: &Self::Key, pk: &Self::Pubkey) -> Result<Self::Output, ()> {
+ let k = x25519_dalek::StaticSecret::from(*k.deref());
+ let pk = x25519_dalek::PublicKey::from(*pk);
+ Ok(*k.diffie_hellman(&pk).as_bytes())
+ }
+}
diff --git a/src/rust/bitbox02-noise/Cargo.toml b/src/rust/bitbox02-noise/Cargo.toml
deleted file mode 100644
index 506780a..0000000
--- a/src/rust/bitbox02-noise/Cargo.toml
+++ /dev/null
@@ -1,27 +0,0 @@
-# SPDX-License-Identifier: Apache-2.0
-
-[package]
-name = "bitbox02-noise"
-version = "0.1.0"
-authors = ["Shift Crypto AG <support@bitbox.swiss>"]
-edition = "2024"
-description = "BitBox02 noise protocol primitives"
-license = "Apache-2.0"
-
-[dependencies.bitbox-hal]
-path = "../bitbox-hal"
-
-[dependencies.noise-protocol]
-version = "0.2.0"
-default-features = false
-features = ["use_alloc"]
-
-[dependencies.noise-rust-crypto]
-version = "0.6.2"
-default-features = false
-features = ["use-sha2", "use-chacha20poly1305"]
-
-[dependencies.x25519-dalek]
-version = "2.0.0"
-default-features = false
-features = ["static_secrets"]
diff --git a/src/rust/bitbox02-noise/src/lib.rs b/src/rust/bitbox02-noise/src/lib.rs
deleted file mode 100644
index 7932dec..0000000
--- a/src/rust/bitbox02-noise/src/lib.rs
+++ /dev/null
@@ -1,21 +0,0 @@
-// SPDX-License-Identifier: Apache-2.0
-
-//! This crate implements the state machine for establishing and using a noise channel.
-//!
-//! The BitBox02 uses the following noise protocol config: `Noise_XX_25519_ChaChaPoly_SHA256`.
-//! [noiseexplorer.com/patterns/XX](https://noiseexplorer.com/patterns/XX/).
-
-#![no_std]
-
-#[cfg(test)]
-#[macro_use]
-extern crate std;
-
-mod noise_xx;
-pub mod testing;
-mod x25519;
-
-pub use noise_xx::{Error, HandshakeHash, HandshakeResult, State};
-pub use x25519::{X25519, genkey};
-
-pub use noise_rust_crypto::sensitive::Sensitive;
diff --git a/src/rust/bitbox02-noise/src/noise_xx.rs b/src/rust/bitbox02-noise/src/noise_xx.rs
deleted file mode 100644
index ab7f975..0000000
--- a/src/rust/bitbox02-noise/src/noise_xx.rs
+++ /dev/null
@@ -1,282 +0,0 @@
-// SPDX-License-Identifier: Apache-2.0
-
-extern crate alloc;
-use alloc::vec::Vec;
-
-use bitbox_hal::Random;
-
-use crate::x25519::{PrivateKey, PublicKey, X25519, genkey};
-use noise_rust_crypto::{ChaCha20Poly1305, Sha256, sensitive::Sensitive};
-
-/// Specialization of noise_protocol::HandshakeState, picking the implementations for Diffie
-/// Hellman, Cipher and Hash.
-/// cbindgen:ignore
-pub type HandshakeState = noise_protocol::HandshakeState<X25519, ChaCha20Poly1305, Sha256>;
-
-/// Common handshake hash that can be derived by both parties. The pairing code is derived from it.
-pub type HandshakeHash = [u8; 32];
-
-/// Manages a noise communication channel, including handshake and message encryption/decryption.
-///
-/// The required state flow is:
-///
-/// `Nothing --init()--> Initialized --handshake()--> Initialized --handshake() --> Ready.`
-pub enum State {
- /// Noise not in use yet.
- Nothing,
- /// Initialized, ready for handhshake messages.
- Initialized(HandshakeState),
- /// Handshake is completed. Ready to confirm the pairing and process messages.
- Ready {
- /// Defaults to true. No encryption/decryption is possible until `set_pairing_verified()` is
- /// called.
- pairing_verification_required: bool,
- /// Fetch with `get_handshake_hash()`, used to display the pairing code to verify the
- /// pairing.
- handshake_hash: HandshakeHash,
- /// Communication partner's static public key. Can be used to remember the communication
- /// partner, so the pairing verification can be skipped the next time.
- remote_static_pubkey: PublicKey,
- /// To encrypt outgoing messages.
- send: noise_protocol::CipherState<ChaCha20Poly1305>,
- /// To decrypt incoming messages.
- receive: noise_protocol::CipherState<ChaCha20Poly1305>,
- },
-}
-
-/// See documentation of `State.handshake()`.
-pub enum HandshakeResult {
- Response(Vec<u8>),
- Done,
-}
-
-/// Common error returned by all noise state functions.
-#[derive(Debug)]
-pub enum Error {
- /// Cannot use this until `set_pairing_verified()` has been called.
- PairingVerificationRequired,
- /// Unexpected/internal errors returned by the `noise_protocol` crate.
- Noise,
- /// This function was called at the wrong time (see `State` documentation to see in which order
- /// the functions need be called).
- WrongState,
-}
-
-impl core::convert::From<Error> for () {
- fn from(_error: Error) -> Self {}
-}
-
-impl core::convert::From<noise_protocol::Error> for Error {
- fn from(_error: noise_protocol::Error) -> Self {
- Error::Noise
- }
-}
-
-impl State {
- /// Can be called at any time to reset the state.
- pub fn reset(&mut self) {
- *self = State::Nothing;
- }
-
- /// Can be called at any time to start waiting for a new communication channel.
- ///
- /// `static_private_key` is the local static key. It can be generated using
- /// `genkey()` with a HAL random source and then persisted. `random` is used to generate a
- /// fresh ephemeral private key for each session.
- pub fn init(&mut self, static_private_key: Sensitive<PrivateKey>, random: &mut impl Random) {
- let ephemeral_private_key = genkey(random);
- let hs = HandshakeState::new(
- noise_protocol::patterns::noise_xx(),
- false, /* is_initiator = false; the app is the initiator */
- &b"Noise_XX_25519_ChaChaPoly_SHA256"[..],
- Some(static_private_key),
- Some(ephemeral_private_key),
- None,
- None,
- );
- *self = State::Initialized(hs);
- }
-
- /// In `noise_XX`, there are 3 handshake messages in total (2 by remote, 1 by us):
- ///
- /// `reqA(remote -> local); respB(local -> remote); reqC(remote -> local)`
- ///
- /// This function needs to be called twice (once for `A/B`, once for `C`).
- ///
- /// See also: [noiseexplorer.com/patterns/XX](https://noiseexplorer.com/patterns/XX/).
- ///
- /// Returns: `Response(<respB>)` (see above) for the 1st handshake message, and `Done` for the
- /// 2nd handshake message.
- pub fn handshake(&mut self, msg: &[u8]) -> Result<HandshakeResult, Error> {
- use core::convert::TryInto;
- match self {
- State::Initialized(handshake_state) => {
- let payload = match handshake_state.read_message_vec(msg) {
- Ok(payload) => payload,
- Err(err) => {
- self.reset();
- return Err(err.into());
- }
- };
-
- if handshake_state.completed() {
- let (receive, send) = handshake_state.get_ciphers();
- let remote_static_pubkey = handshake_state.get_rs().ok_or(Error::Noise)?;
- *self = State::Ready {
- pairing_verification_required: true,
- handshake_hash: handshake_state.get_hash().try_into().unwrap(),
- remote_static_pubkey,
- send,
- receive,
- };
- return Ok(HandshakeResult::Done);
- }
- Ok(HandshakeResult::Response(
- handshake_state.write_message_vec(&payload)?,
- ))
- }
- _ => Err(Error::WrongState),
- }
- }
-
- pub fn get_handshake_hash(&self) -> Result<HandshakeHash, Error> {
- match self {
- State::Ready { handshake_hash, .. } => Ok(*handshake_hash),
- _ => Err(Error::WrongState),
- }
- }
-
- /// The communication partner's identity.
- pub fn remote_static_pubkey(&self) -> Result<PublicKey, Error> {
- match self {
- State::Ready {
- remote_static_pubkey,
- ..
- } => Ok(*remote_static_pubkey),
- _ => Err(Error::WrongState),
- }
- }
-
- /// Mark the pairing as verified, unlocking encryption and decryption.
- ///
- /// This should only be called after the user verified the pairing code (see
- /// `get_handshake_hash()`), or has done so in the past for the same remote communucation
- /// partner (see `remote_static_pubkey()`).
- pub fn set_pairing_verified(&mut self) -> Result<(), Error> {
- match self {
- State::Ready {
- pairing_verification_required,
- ..
- } => {
- *pairing_verification_required = false;
- Ok(())
- }
- _ => Err(Error::WrongState),
- }
- }
-
- /// Decrypt an encrypted message.
- pub fn decrypt(&mut self, msg: &[u8]) -> Result<Vec<u8>, Error> {
- match self {
- State::Ready {
- pairing_verification_required: true,
- ..
- } => Err(Error::PairingVerificationRequired),
-
- State::Ready {
- pairing_verification_required: false,
- receive,
- ..
- } => match receive.decrypt_vec(msg) {
- Ok(r) => Ok(r),
- Err(()) => Err(Error::Noise),
- },
- _ => Err(Error::WrongState),
- }
- }
-
- /// Encrypt a message. The ciphertext is appended to `out`.
- pub fn encrypt(&mut self, msg: &[u8], out: &mut Vec<u8>) -> Result<(), Error> {
- match self {
- State::Ready {
- pairing_verification_required: true,
- ..
- } => Err(Error::PairingVerificationRequired),
-
- State::Ready {
- pairing_verification_required: false,
- send,
- ..
- } => {
- let start = out.len();
- // Extra 16 bytes for the aead authentication tag (MAC).
- let encrypted_len = msg.len() + 16;
- // Make space for result.
- out.resize(start + encrypted_len, 0);
- // This also adds the MAC.
- send.encrypt(msg, &mut out[start..]);
- Ok(())
- }
- _ => Err(Error::WrongState),
- }
- }
-}
-
-#[cfg(test)]
-mod tests {
- use super::*;
- use crate::testing::{MockRandom, make_mock_host};
-
- impl HandshakeResult {
- fn response(self) -> Result<Vec<u8>, ()> {
- match self {
- HandshakeResult::Response(r) => Ok(r),
- HandshakeResult::Done => Err(()),
- }
- }
- fn done(self) -> Result<(), ()> {
- match self {
- HandshakeResult::Response(_) => Err(()),
- HandshakeResult::Done => Ok(()),
- }
- }
- }
-
- #[test]
- pub fn test_full() {
- let mut bb02_random = MockRandom;
- let bb02_static_key = genkey(&mut bb02_random);
-
- let mut host = make_mock_host();
- let mut bb02 = State::Nothing;
- bb02.init(bb02_static_key, &mut bb02_random);
-
- let host_handshake_1 = host.write_message_vec(b"").unwrap();
- let bb02_handshake_1 = bb02
- .handshake(&host_handshake_1)
- .unwrap()
- .response()
- .unwrap();
-
- let host_handshake_2 = {
- let payload = host.read_message_vec(&bb02_handshake_1).unwrap();
- host.write_message_vec(&payload).unwrap()
- };
- bb02.handshake(&host_handshake_2).unwrap().done().unwrap();
-
- bb02.set_pairing_verified().unwrap();
-
- let (mut host_send, mut host_recv) = host.get_ciphers();
-
- let encrypted = host_send.encrypt_vec(b"message from host");
- let decrypted = bb02.decrypt(&encrypted).unwrap();
- assert_eq!(decrypted, b"message from host");
-
- let mut encrypted = b"prefix".to_vec();
- bb02.encrypt(b"message from bb02", &mut encrypted).unwrap();
- let (prefix, encrypted) = encrypted.split_at(b"prefix".len());
- assert_eq!(&prefix, b"prefix");
- let decrypted = host_recv.decrypt_vec(encrypted).unwrap();
- assert_eq!(decrypted, b"message from bb02");
- }
-}
diff --git a/src/rust/bitbox02-noise/src/testing.rs b/src/rust/bitbox02-noise/src/testing.rs
deleted file mode 100644
index 9c8f4d2..0000000
--- a/src/rust/bitbox02-noise/src/testing.rs
+++ /dev/null
@@ -1,37 +0,0 @@
-// SPDX-License-Identifier: Apache-2.0
-
-use crate::noise_xx::HandshakeState;
-use crate::x25519::genkey;
-
-pub struct MockRandom;
-
-impl bitbox_hal::Random for MockRandom {
- fn factory_randomness(&mut self) -> &'static [u8; 32] {
- unreachable!()
- }
-
- fn mcu_32_bytes(&mut self, out: &mut [u8; 32]) {
- out.copy_from_slice(b"llllllllllllllllllllllllllllllll")
- }
-}
-
-pub type TestHandshakeState = HandshakeState;
-
-pub fn make_host(random: &mut impl bitbox_hal::Random) -> TestHandshakeState {
- let host_static_key = genkey(random);
- let host_ephemeral_key = genkey(random);
- TestHandshakeState::new(
- noise_protocol::patterns::noise_xx().clone(),
- true,
- &b"Noise_XX_25519_ChaChaPoly_SHA256"[..],
- Some(host_static_key),
- Some(host_ephemeral_key),
- None,
- None,
- )
-}
-
-pub fn make_mock_host() -> TestHandshakeState {
- let mut random = MockRandom;
- make_host(&mut random)
-}
diff --git a/src/rust/bitbox02-noise/src/x25519.rs b/src/rust/bitbox02-noise/src/x25519.rs
deleted file mode 100644
index 55c5703..0000000
--- a/src/rust/bitbox02-noise/src/x25519.rs
+++ /dev/null
@@ -1,58 +0,0 @@
-// SPDX-License-Identifier: Apache-2.0
-
-//! This module implements the X25519 trait needed by noise_protocol
-//! by using the x25519_dalek crate. It is adapted from
-//! https://github.com/sopium/noise-rust/blob/76fb694f06b429879c264087f496958a99710356/noise-rust-crypto/src/lib.rs#L31,
-//! but uses the HAL random source to generate keys.
-
-use bitbox_hal::Random;
-use core::ops::Deref;
-use noise_protocol::U8Array;
-use noise_rust_crypto::sensitive::Sensitive;
-
-pub struct X25519;
-
-pub type PrivateKey = [u8; 32];
-pub type PublicKey = [u8; 32];
-
-/// Generate a fresh x25519 private key by reading 32 random bytes from the HAL and applying
-/// the standard clamping.
-pub fn genkey(random: &mut impl Random) -> Sensitive<PrivateKey> {
- let mut k: Sensitive<PrivateKey> = Sensitive::new();
- random.mcu_32_bytes(&mut k);
-
- // Copied from: https://github.com/sopium/noise-rust/blob/76fb694f06b429879c264087f496958a99710356/noise-rust-crypto/src/lib.rs#L49-L51
- // which in turn copied it from:
- // https://github.com/dalek-cryptography/x25519-dalek/blob/ecd6be674850a99ad26404f6aa29b0cf79642b97/src/x25519.rs#L162-L164
- // which is also in our vendored deps: `vendor/x25519-dalek/src/x25519.rs`.
- k[0] &= 248;
- k[31] &= 127;
- k[31] |= 64;
-
- k
-}
-
-impl noise_protocol::DH for X25519 {
- type Key = Sensitive<PrivateKey>;
- type Pubkey = PublicKey;
- type Output = [u8; 32];
-
- fn name() -> &'static str {
- "25519"
- }
-
- fn genkey() -> Self::Key {
- panic!("implicit X25519 key generation is unsupported; generate keys explicitly")
- }
-
- fn pubkey(k: &Self::Key) -> Self::Pubkey {
- let static_secret = x25519_dalek::StaticSecret::from(*k.deref());
- *x25519_dalek::PublicKey::from(&static_secret).as_bytes()
- }
-
- fn dh(k: &Self::Key, pk: &Self::Pubkey) -> Result<Self::Output, ()> {
- let k = x25519_dalek::StaticSecret::from(*k.deref());
- let pk = x25519_dalek::PublicKey::from(*pk);
- Ok(*k.diffie_hellman(&pk).as_bytes())
- }
-}
diff --git a/src/rust/bitbox02-rust-c/Cargo.toml b/src/rust/bitbox02-rust-c/Cargo.toml
index 393369b..3a4ff57 100644
--- a/src/rust/bitbox02-rust-c/Cargo.toml
+++ b/src/rust/bitbox02-rust-c/Cargo.toml
@@ -16,7 +16,7 @@ bitbox-da14531 = { path = "../bitbox-da14531" }
bitbox-aes = { path = "../bitbox-aes", optional = true }
bitbox02 = { path = "../bitbox02" }
bitbox-hal = { path = "../bitbox-hal" }
-bitbox02-noise = { path = "../bitbox02-noise", optional = true }
+bitbox-noise = { path = "../bitbox-noise", optional = true }
cortex-m = { workspace = true }
util = { path = "../util" }
bitbox-framed-serial-link = { path = "../bitbox-framed-serial-link" }
@@ -66,13 +66,13 @@ target-c-unit-tests = [
]
platform-bitbox02 = []
-platform-bitbox02plus = ["util/sha2", "bitbox02-noise", "bitbox02-rust"]
+platform-bitbox02plus = ["util/sha2", "bitbox-noise", "bitbox02-rust"]
bootloader = []
firmware = [
"bitbox02-rust",
"bitbox02-rust/firmware",
- "bitbox02-noise",
+ "bitbox-noise",
"util/sha2",
"util/firmware",
"der",
diff --git a/src/rust/bitbox02-rust/Cargo.toml b/src/rust/bitbox02-rust/Cargo.toml
index 80acf13..81208f7 100644
--- a/src/rust/bitbox02-rust/Cargo.toml
+++ b/src/rust/bitbox02-rust/Cargo.toml
@@ -27,7 +27,7 @@ util = { path = "../util" }
erc20_params = { path = "../erc20_params", optional = true }
bitbox-executor = { path = "../bitbox-executor" }
binascii = { version = "0.1.4", default-features = false, features = ["encode"] }
-bitbox02-noise = {path = "../bitbox02-noise"}
+bitbox-noise = { path = "../bitbox-noise" }
streaming-silent-payments = { path = "../streaming-silent-payments", optional = true }
bitbox-aes = { path = "../bitbox-aes" }
hex = { workspace = true }
diff --git a/src/rust/bitbox02-rust/src/hww.rs b/src/rust/bitbox02-rust/src/hww.rs
index 0e9722c..8b9c19e 100644
--- a/src/rust/bitbox02-rust/src/hww.rs
+++ b/src/rust/bitbox02-rust/src/hww.rs
@@ -147,7 +147,7 @@ mod tests {
block_on(process_packet(&mut TestingHal::new(), b"h".to_vec())),
[OP_STATUS_SUCCESS].to_vec()
);
- let mut host_noise = bitbox02_noise::testing::make_mock_host();
+ let mut host_noise = bitbox_noise::testing::make_mock_host();
let host_handshake_1 = host_noise.write_message_vec(b"").unwrap();
let bb02_handshake_1 = {
let result = block_on(process_packet(&mut TestingHal::new(), {
@@ -182,7 +182,7 @@ mod tests {
// Handshake hash as computed by the host. Should be the same as computed on the
// device. The pairing code is derived from that.
- let handshake_hash: bitbox02_noise::HandshakeHash =
+ let handshake_hash: bitbox_noise::HandshakeHash =
host_noise.get_hash().try_into().unwrap();
let mut mock_hal = TestingHal::new();
diff --git a/src/rust/bitbox02-rust/src/hww/noise.rs b/src/rust/bitbox02-rust/src/hww/noise.rs
index 0111eff..d4509e1 100644
--- a/src/rust/bitbox02-rust/src/hww/noise.rs
+++ b/src/rust/bitbox02-rust/src/hww/noise.rs
@@ -11,20 +11,20 @@ const OP_HER_COMEZ_TEH_HANDSHAEK: u8 = b'H';
pub const OP_NOISE_MSG: u8 = b'n';
/// A safer version of the noise state. RefCell so we cannot accidentally borrow illegally.
-struct SafeNoiseState(RefCell<bitbox02_noise::State>);
+struct SafeNoiseState(RefCell<bitbox_noise::State>);
/// Safety: this implements Sync even though it is not thread safe. This is okay, as we run only in
/// a single thread in the BitBox02.
unsafe impl Sync for SafeNoiseState {}
/// Global noise state, enforcing a proper handshake.
-static NOISE_STATE: SafeNoiseState = SafeNoiseState(RefCell::new(bitbox02_noise::State::Nothing));
+static NOISE_STATE: SafeNoiseState = SafeNoiseState(RefCell::new(bitbox_noise::State::Nothing));
#[derive(Debug)]
pub struct Error;
-impl core::convert::From<bitbox02_noise::Error> for Error {
- fn from(_error: bitbox02_noise::Error) -> Self {
+impl core::convert::From<bitbox_noise::Error> for Error {
+ fn from(_error: bitbox_noise::Error) -> Self {
Error
}
}
@@ -65,7 +65,7 @@ pub(crate) async fn process(
// we started a new session in the middle of something.
hal.ui().reset();
let static_private_key =
- bitbox02_noise::Sensitive::from(hal.memory().get_noise_static_private_key()?);
+ bitbox_noise::Sensitive::from(hal.memory().get_noise_static_private_key()?);
NOISE_STATE
.0
@@ -76,11 +76,11 @@ pub(crate) async fn process(
Some((&OP_HER_COMEZ_TEH_HANDSHAEK, rest)) => {
let mut state = NOISE_STATE.0.borrow_mut();
match state.handshake(rest)? {
- bitbox02_noise::HandshakeResult::Response(msg) => {
+ bitbox_noise::HandshakeResult::Response(msg) => {
usb_out.extend(msg);
Ok(())
}
- bitbox02_noise::HandshakeResult::Done => {
+ bitbox_noise::HandshakeResult::Done => {
let already_verified = hal
.memory()
.check_noise_remote_static_pubkey(&state.remote_static_pubkey()?);
diff --git a/src/rust/bitbox02/Cargo.toml b/src/rust/bitbox02/Cargo.toml
index 8c5d77c..5e56d0d 100644
--- a/src/rust/bitbox02/Cargo.toml
+++ b/src/rust/bitbox02/Cargo.toml
@@ -12,7 +12,7 @@ license = "Apache-2.0"
bitbox02-sys = {path="../bitbox02-sys"}
bitbox-securechip = { path = "../bitbox-securechip" }
bitbox-securechip-sys = { path = "../bitbox-securechip-sys" }
-bitbox02-noise = { path = "../bitbox02-noise" }
+bitbox-noise = { path = "../bitbox-noise" }
bitbox-hal = { path = "../bitbox-hal" }
bitbox-bytequeue = { path = "../bitbox-bytequeue" }
bitbox-framed-serial-link = { path = "../bitbox-framed-serial-link" }
diff --git a/src/rust/bitbox02/src/random.rs b/src/rust/bitbox02/src/random.rs
index ecadb74..3409f15 100644
--- a/src/rust/bitbox02/src/random.rs
+++ b/src/rust/bitbox02/src/random.rs
@@ -23,7 +23,7 @@ pub extern "C" fn rust_noise_generate_static_private_key(
mut private_key_out: util::bytes::BytesMut,
) {
let mut random = crate::hal::random::BitBox02Random;
- let key = bitbox02_noise::genkey(&mut random);
+ let key = bitbox_noise::genkey(&mut random);
private_key_out.as_mut().copy_from_slice(&key[..]);
}
@@ -48,7 +48,7 @@ mod tests {
#[test]
fn test_generate_static_private_key() {
let mut random = crate::hal::random::BitBox02Random;
- let key = bitbox02_noise::genkey(&mut random);
+ let key = bitbox_noise::genkey(&mut random);
assert_eq!(key[0] & 0b111, 0);
assert_eq!(key[31] & 0b1000_0000, 0);
assert_eq!(key[31] & 0b0100_0000, 0b0100_0000);
diff --git a/test/simulator-graphical-bb03/Cargo.lock b/test/simulator-graphical-bb03/Cargo.lock
index e9407f9..3237fb4 100644
--- a/test/simulator-graphical-bb03/Cargo.lock
+++ b/test/simulator-graphical-bb03/Cargo.lock
@@ -393,6 +393,16 @@ dependencies = [
"cmake",
]
+[[package]]
+name = "bitbox-noise"
+version = "0.1.0"
+dependencies = [
+ "bitbox-hal",
+ "noise-protocol",
+ "noise-rust-crypto",
+ "x25519-dalek",
+]
+
[[package]]
name = "bitbox-platform-host"
version = "0.1.0"
@@ -446,10 +456,10 @@ dependencies = [
"bitbox-bytequeue",
"bitbox-framed-serial-link",
"bitbox-hal",
+ "bitbox-noise",
"bitbox-securechip",
"bitbox-securechip-sys",
"bitbox-usb-report-queue",
- "bitbox02-noise",
"bitbox02-sys",
"futures-lite",
"grounded",
@@ -460,16 +470,6 @@ dependencies = [
"zeroize",
]
-[[package]]
-name = "bitbox02-noise"
-version = "0.1.0"
-dependencies = [
- "bitbox-hal",
- "noise-protocol",
- "noise-rust-crypto",
- "x25519-dalek",
-]
-
[[package]]
name = "bitbox02-rust"
version = "0.1.0"
@@ -483,11 +483,11 @@ dependencies = [
"bitbox-da14531",
"bitbox-executor",
"bitbox-hal",
+ "bitbox-noise",
"bitbox-secp256k1",
"bitbox-u2fhid",
"bitbox-usb-report-queue",
"bitbox02",
- "bitbox02-noise",
"bitcoin",
"bitcoin_hashes",
"blake2",
diff --git a/test/simulator-graphical/Cargo.lock b/test/simulator-graphical/Cargo.lock
index 60afc2a..7d51b88 100644
--- a/test/simulator-graphical/Cargo.lock
+++ b/test/simulator-graphical/Cargo.lock
@@ -337,6 +337,16 @@ dependencies = [
"zeroize",
]
+[[package]]
+name = "bitbox-noise"
+version = "0.1.0"
+dependencies = [
+ "bitbox-hal",
+ "noise-protocol",
+ "noise-rust-crypto",
+ "x25519-dalek",
+]
+
[[package]]
name = "bitbox-platform-host"
version = "0.1.0"
@@ -390,10 +400,10 @@ dependencies = [
"bitbox-bytequeue",
"bitbox-framed-serial-link",
"bitbox-hal",
+ "bitbox-noise",
"bitbox-securechip",
"bitbox-securechip-sys",
"bitbox-usb-report-queue",
- "bitbox02-noise",
"bitbox02-sys",
"futures-lite",
"grounded",
@@ -404,16 +414,6 @@ dependencies = [
"zeroize",
]
-[[package]]
-name = "bitbox02-noise"
-version = "0.1.0"
-dependencies = [
- "bitbox-hal",
- "noise-protocol",
- "noise-rust-crypto",
- "x25519-dalek",
-]
-
[[package]]
name = "bitbox02-rust"
version = "0.1.0"
@@ -427,11 +427,11 @@ dependencies = [
"bitbox-da14531",
"bitbox-executor",
"bitbox-hal",
+ "bitbox-noise",
"bitbox-secp256k1",
"bitbox-u2fhid",
"bitbox-usb-report-queue",
"bitbox02",
- "bitbox02-noise",
"bitcoin",
"bitcoin_hashes",
"blake2",
@@ -468,9 +468,9 @@ dependencies = [
"bitbox-da14531",
"bitbox-framed-serial-link",
"bitbox-hal",
+ "bitbox-noise",
"bitbox-usb-report-queue",
"bitbox02",
- "bitbox02-noise",
"bitbox02-rust",
"cortex-m",
"der",
Why this scored 15/100
Community notes
Notes can correct, qualify, or add evidence to the AI analysis. Every note shown here has been validated by a human moderator.
The AI analysis stands alone for now. Submit a note if you can add evidence or important context.