Btc: accept SWAPKIT payment request identities
What changed, and why it matters
This commit adds a new trusted Bitcoin payment-request signer called SWAPKIT to the BitBox02 hardware wallet. It also makes matching more flexible for SWAPKIT so names like 'swapkit (Provider)' or 'SwapKit' are accepted. The change is a routine allow-list update, not a fix for a security bug.
No immediate action required. Treat as a normal vendor feature addition. If reviewing for supply-chain risk, verify the SWAPKIT public key against an independent, vendor-published source.
Security signals we found
Addition of a new trusted signing identity to a payment-request allow-list
Case-insensitive substring matching for one identity only, increasing name flexibility
No validation, parsing, or cryptographic logic changes beyond identity lookup
Evidence from the diff
The patch updates src/rust/bitbox02-rust/src/hww/api/bitcoin/payment_request.rs. It reformats two existing public keys using the hex_lit::hex! macro and adds a new Identity entry named SWAPKIT with a secp256k1 public key. A new find_identity helper now returns the SWAPKIT identity for any recipient name whose ASCII-uppercase form contains ‘SWAPKIT’, while still requiring exact matches for other identities. Unit tests cover the new matching behavior.
Changed components
BitBox02 firmware Bitcoin payment request verificationsrc/rust/bitbox02-rust/src/hww/api/bitcoin/payment_request.rsInspect captured patch +37 / −2
diff --git a/src/rust/bitbox02-rust/src/hww/api/bitcoin/payment_request.rs b/src/rust/bitbox02-rust/src/hww/api/bitcoin/payment_request.rs
index 05b6688..da22c25 100644
--- a/src/rust/bitbox02-rust/src/hww/api/bitcoin/payment_request.rs
+++ b/src/rust/bitbox02-rust/src/hww/api/bitcoin/payment_request.rs
@@ -17,6 +17,7 @@ use crate::hal::Ui;
use crate::secp256k1::SECP256K1;
use crate::workflow::verify_message;
+use hex_lit::hex;
use sha2::{Digest, Sha256};
use bitcoin::secp256k1;
@@ -32,17 +33,32 @@ struct Identity {
const IDENTITIES: &[Identity] = &[
Identity {
name: "POCKET",
- public_key: b"\x02\x29\x02\xb4\xed\xe4\x82\xa9\x07\xce\x16\xa1\xc6\x34\x14\x5e\x72\x8f\x1d\xe4\xf2\x49\x04\x3a\x8b\xe4\x7d\xf2\x7d\xb9\x32\x0c\x2c",
+ public_key: &hex!("022902b4ede482a907ce16a1c634145e728f1de4f249043a8be47df27db9320c2c"),
+ },
+ Identity {
+ name: "SWAPKIT",
+ public_key: &hex!("03098cba9cde720171796a5c58cb774b0cd19deb62e9b51df5967aefeba34632ff"),
},
#[cfg(any(feature = "testing", feature = "c-unit-testing"))]
Identity {
name: "Test Merchant",
// private_key: b"aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"
- public_key: b"\x02\xe5\xa0\x18\xb3\xa2\xe1\x55\x31\x61\x09\xd9\xcd\xc5\xea\xb7\x39\x75\x9c\x0e\x07\xe0\xc0\x0b\xf9\xfc\xcb\x82\x37\xfe\x4d\x7f\x02",
+ public_key: &hex!("02e5a018b3a2e155316109d9cdc5eab739759c0e07e0c00bf9fccb8237fe4d7f02"),
},
];
+/// Looks up the signing identity for a payment request recipient name.
+///
+/// Most recipients must match an entry in `IDENTITIES` exactly. `SWAPKIT` is a
+/// special case: any recipient name containing `swapkit`, in any ASCII case,
+/// is matched to the fixed `SWAPKIT` identity so provider-specific or legacy
+/// naming variants are accepted.
fn find_identity(name: &str) -> Option<&Identity> {
+ if name.to_ascii_uppercase().contains("SWAPKIT") {
+ return IDENTITIES
+ .iter()
+ .find(|identity| identity.name == "SWAPKIT");
+ }
IDENTITIES.iter().find(|identity| identity.name == name)
}
@@ -279,6 +295,25 @@ mod tests {
)
}
+ #[test]
+ fn test_find_identity() {
+ assert_eq!(find_identity("POCKET").unwrap().name, "POCKET");
+
+ let swapkit_identity = find_identity("SWAPKIT (Provider)").unwrap();
+ assert_eq!(swapkit_identity.name, "SWAPKIT");
+ assert_eq!(
+ swapkit_identity.public_key,
+ hex!("03098cba9cde720171796a5c58cb774b0cd19deb62e9b51df5967aefeba34632ff")
+ );
+
+ assert_eq!(find_identity("SWAPKIT Provider").unwrap().name, "SWAPKIT");
+ assert_eq!(find_identity("swapkit (Provider)").unwrap().name, "SWAPKIT");
+ assert_eq!(find_identity("SWAPKIT").unwrap().name, "SWAPKIT");
+ assert_eq!(find_identity("SwapKit").unwrap().name, "SWAPKIT");
+
+ assert!(find_identity("Provider").is_none());
+ }
+
#[test]
fn test_sighash() {
let coin_params = params::get(pb::BtcCoin::Tbtc);
Why this scored 18/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.