-
Notifications
You must be signed in to change notification settings - Fork 138
feat: add mina-bip32 and ledger-test-vectors for Ledger wallet validation #3388
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: master
Are you sure you want to change the base?
Conversation
querolita
left a comment
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Comments for commit dcf9885
| /// | ||
| /// BIP32 specifies using secp256k1 for key derivation even when the | ||
| /// target curve is different (like Pallas for Mina). | ||
| const SECP256K1_ORDER: [u8; 32] = [ |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
checked
|
|
||
| for i in (0..32).rev() { | ||
| let sum = a[i] as u16 + b[i] as u16 + carry; | ||
| result[i + 1] = sum as u8; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Just a note. So here, if sum is >255, what will the casting do? Will it perform the mod operation with 256? Meaning, will it discard the 9th bit?
| for i in (0..32).rev() { | ||
| let sum = a[i] as u16 + b[i] as u16 + carry; | ||
| result[i + 1] = sum as u8; | ||
| carry = sum >> 8; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Here it's clear that it shifts 8 positions to get the 9th bit (potential carry bit)
| break; | ||
| } | ||
| core::cmp::Ordering::Less => { | ||
| break; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
efficient, good
| for i in (0..32).rev() { | ||
| let diff = result[i + 1] as i16 - SECP256K1_ORDER[i] as i16 - borrow; | ||
| if diff < 0 { | ||
| result[i + 1] = (diff + 256) as u8; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
safe type, also the 256 comes from the borrow bit corresponding to the next significant byte which is 2^8 in this step. Just noting for correctness check.
| .expect("HMAC can take key of any size"); | ||
|
|
||
| // Construct the HMAC data based on derivation type | ||
| if index >= HARDENED_OFFSET { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I find this confusing, the fact that both branches do the same.
| mac.update(&self.private_key); | ||
| } | ||
| // Append the index in big-endian format | ||
| mac.update(&index.to_be_bytes()); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
everything big endian so far
|
|
||
| // Compute child_private_key = (parent_private_key + child_key_material) mod n | ||
| // where n is the secp256k1 curve order (standard BIP32) | ||
| let final_key = add_mod_secp256k1(&self.private_key, &child_key_material); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
why call it final_key in the variable but child_private_key in the comment?
| /// # Algorithm | ||
| /// | ||
| /// 1. Copy the 32-byte private key | ||
| /// 2. Mask the top 2 bits of the first byte to ensure the value |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Are we ok with collisions?
| key_bytes.reverse(); | ||
|
|
||
| // Convert to scalar field element | ||
| ScalarField::from_le_bytes_mod_order(&key_bytes) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🆗
querolita
left a comment
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Comments for commit c7b7918
| fee: String, | ||
| /// Account nonce | ||
| nonce: u32, | ||
| /// Valid until slot (null for no expiry) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
null or None?
| impl Hashable for Transaction { | ||
| type D = NetworkId; | ||
|
|
||
| fn to_roinput(&self) -> ROInput { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why this order? It's not the same as the order in the struct fields.
| /// on the Pallas curve. | ||
| #[derive(Clone, Debug, Serialize, Deserialize)] | ||
| pub struct PublicKeyJson { | ||
| /// X-coordinate of the public key point (hex-encoded, little-endian) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
here starts little-endian world
| /// | ||
| /// After BIP32 derivation and bit masking, this is the scalar | ||
| /// value used for signing. | ||
| pub private_key: String, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Do you mean private_key is the sk in the formula above s = k + e * sk?
Add a new crate implementing BIP32 hierarchical deterministic key derivation for Mina, as used by Ledger hardware wallets. Key features: - Master key derivation using HMAC-SHA512 with "Bitcoin seed" - Child key derivation with secp256k1 curve order (matching Ledger) - Mina BIP44 path: m/44'/12586'/account'/0/0 - Bit masking for valid Pallas scalar field elements - Comprehensive documentation for external implementers Note: Test vectors should be validated against actual Ledger devices. The Ledger uses proprietary os_perso_derive_node_bip32(CX_CURVE_256K1).
Add a tool to generate JSON test vectors for validating Mina Ledger hardware wallet signing implementations. Features: - BIP32-derived keys using mina-bip32 crate - Payment and delegation transaction types - Coverage for mainnet/testnet, multiple accounts - CLI: cargo run --package ledger-test-vectors Test vectors include: - Seed, private key, public key coordinates, address - Transaction details (type, to, amount, fee, nonce, memo) - Signature (rx, s) Note: Transaction types will be updated when o1-labs/mina-rust#1665 is implemented.
…ion) Add test vectors from ledger-mina repository for validation: - Test mnemonic and expected addresses for accounts 0, 1, 2, 3, 49370, 12586 - Tests are marked as ignored because our derivation produces different addresses than expected - requires further investigation Also removes deny(missing_docs) attribute and adds pbkdf2 dev-dependency.
8ad9f4f to
4f8422f
Compare
querolita
left a comment
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Approving for now, but let's keep in mind the mismatch between Ledger's implementation and this one.
Summary
mina-bip32crate for BIP32 hierarchical deterministic key derivation as used by Ledger hardware walletsledger-test-vectorstool for generating JSON test vectors for Mina Ledger hardware wallet signing validationmina-bip32 crate
os_perso_derive_node_bip32(CX_CURVE_256K1))m/44'/12586'/account'/0/0ledger-test-vectors tool
cargo run --package ledger-test-vectorsTest plan
cargo test --package mina-bip32- all 16 tests passmake lint- no warnings or errorscargo run --package ledger-test-vectors- generates valid JSON test vectorsNotes