Repository navigation
feat(ffi): expose sign_digest for arbitrary 32-byte digest signing - #71
Conversation
|
Should we drop the regenerated Swift bindings from this PR? They expect the new FFI symbols but Package.swift still points at the released v0.2.2 xcframework so root swift build breaks. (The release flow can regenerate bindings with the matching framework) |
Done, I’ve removed the Swift bindings from this PR |
|
I did a quick review and can give this a concept ACK but need to try testing locally and give it a deeper look. |
b84ba25 to
d583436
Compare
tks @notmandatory, ok I keep watching #74 |
notmandatory
left a comment
There was a problem hiding this comment.
Overall looks very good. I have one comment that is an easy fix.
You should rebase and also cleanup your commit history to not include the ffi generation and removal. Could be just two commits, one with the feature and another with the tests, the rest is noise. Thanks!
|
Also need to figure out why these emulator tests are failing: https://github.com/bitcoindevkit/rust-cktap/actions/runs/35165745575/job/107004613825 |
d583436 to
120fc52
Compare
Adds a public `sign_digest` wrapper on `TapSigner` and an FFI entry
point so Swift/Kotlin consumers can sign arbitrary digests (BIP-137
"Bitcoin Signed Message", proof-of-key challenges, generic
attestations) without going through `sign_psbt`.
The FFI returns `SignedDigest { signature, pubkey, rec_id }` — the
recovery id is computed at the boundary so callers can verify locally
or build a BIP-137 header byte without an extra round-trip. As with the
other FFI entry points, the CVC is validated locally before the card is
contacted, and `SignDigestError` carries a `Cvc` variant for it.
`rust_cktap` now re-exports `secp256k1` so the FFI crate can derive the
recovery id without depending on `bitcoin` directly.
Closes bitcoindevkit#70
FFI unit tests (no card required): - `derive_recovery_id_matches_signer_rec_id`, `derive_recovery_id_covers_both_compressed_ids` and `derive_recovery_id_errors_when_pubkey_does_not_match` exercise the recovery-id derivation, including both ids reachable with compressed-pubkey ECDSA. - `sign_digest_rejects_invalid_cvc_before_contacting_card`, `sign_digest_rejects_wrong_digest_length_before_contacting_card` and `sign_digest_reports_cvc_error_before_digest_length_error` use a counting transport double to assert invalid input never reaches the card; `sign_digest_with_valid_inputs_reaches_card` proves the double does observe card traffic. Emulator test: - `test_tap_signer_sign_digest` checks, for sub_paths `[]` and `[0, 5]`, that the signing key matches the card's account xpub derived at that sub_path and that the signature verifies only over the digest that was sent.
dad8513 to
09f2b91
Compare
|
Thanks @r1b2ns ! |
Summary
sign_digestonTapSignerand through the UniFFI surface so Swift/Kotlin consumers can sign arbitrary 32-byte digests (BIP-137 "Bitcoin Signed Message", proof-of-key challenges, generic attestations) without going throughsign_psbt.SignedDigest { signature, pubkey, rec_id }— the recovery id is computed at the FFI boundary so callers can verify locally or build a BIP-137 header byte without an extra round-trip.Closes #70.
Changes
lib/src/tap_signer.rs— publicsign_digest(digest, sub_path, cvc)wrapper around the existing crate-privateTapSignerShared::sign.lib/src/lib.rs— re-exportbitcoinso downstream FFI code can userust_cktap::bitcoin::secp256k1.cktap-ffi/src/tap_signer.rs— UniFFI entry point +SignedDigestrecord + brute-force recovery-id derivation against the returned compressed pubkey.cktap-ffi/src/error.rs— newSignDigestErrorwithCkTap,InvalidDigestLength { len: u32 }, andRecoveryId { msg }variants.Test plan
cargo build --all-features --all-targets(workspace)cargo test—cktap-ffirecovery-id coverage (derive_recovery_id_matches_signer_rec_id,derive_recovery_id_errors_when_pubkey_does_not_match,derive_recovery_id_covers_both_compressed_ids) passescargo test -p rust-cktap --features emulator -- --nocaptureagainst the coinkite emulatorsignDigest(digest:subPath:cvc:)and verify the returned signature against the returned pubkey using therec_id