Skip to content

v1.15.0 release

Choose a tag to compare

@azuchi azuchi released this 12 Oct 08:58
· 80 commits to master since this release

This release fixes two SLIP-39 bugs, one of which affects compatibility with other implementations, and makes BIP-322 message signing reject a key the address does not commit to. Both SLIP-39 fixes change what this gem writes, so please read the Upgrade notes if you hold shares generated by an earlier version.

Bug Fixes

SLIP-39

  • Fix Bitcoin::SLIP39::SSS encoding the identifier of the PBKDF2 salt with the minimum number of bytes, while SLIP-39 requires two bytes. An identifier below 256 produced a one byte salt, so the shares written for about 1 in 128 master secrets could not be recovered by the reference implementation, and shares written by another implementation with such an identifier recovered to a wrong master secret here. The salt now always carries two bytes, which is verified against vectors generated with the reference implementation (python shamir-mnemonic 0.3.0). recover_secret takes a legacy_salt: option to recover the shares this gem used to write.
  • Fix Bitcoin::SLIP39::Share dropping the leading zero bytes of the share value. #from_words stripped them when it read the value back, and #to_words sized the value by its bit length, so a value starting with a zero byte was written with fewer words than a value of the same length and did not survive a round trip. #to_words now writes the value at its byte length, and rejects a value whose byte length is odd since the padding #from_words derives from the word count only matches an even one.

Message signing

  • Fix Bitcoin::MessageSign.sign_message producing a signature for a BIP-322 address the key does not correspond to. A P2WPKH address of another key, or a P2TR address which is not the tweaked output key of the given key, used to return a signature which verifies against no one, and BIP-322 carries nothing that would report it. It now raises ArgumentError for such a key. A P2WSH address is also rejected, since the witness script it commits to cannot be derived from a key alone.
  • Add regression specs confirming that Bitcoin::MessageSign.verify_message rejects a signature made by an unrelated key for a P2PKH, P2WPKH, P2SH-P2WPKH, P2WSH and P2TR address, including a signature valid for the sighash of the address itself where only the binding between the key and the address rejects it.

Improvements

  • Add an as_internal: keyword argument to Bitcoin::Key#to_p2tr. The default keeps returning the address whose output key is the x-only public key of this key as it is, which is what the rawtr() descriptor does. With as_internal: true this key is the internal key and the output key is tweaked with an empty merkle root, which is what the tr() descriptor and BIP-86 define for a single key address.

Upgrade notes

  • SLIP-39 shares written by bitcoinrb 1.14.0 and earlier whose identifier is below 256 can no longer be recovered with the default salt. Pass legacy_salt: true to Bitcoin::SLIP39::SSS.recover_secret for those, and consider regenerating them so other implementations can read them. Shares with an identifier of 256 or above are unaffected, since both encodings produce the same two bytes.
  • Bitcoin::SLIP39::Share#to_words raises ArgumentError for a share value whose byte length is odd, which used to be written as a different value rather than rejected.
  • Bitcoin::MessageSign.sign_message raises ArgumentError where it used to return an unverifiable signature, when the address: given does not correspond to key, and for a P2WSH address. Note that the P2TR address it expects is the tweaked one, which Bitcoin::Key#to_p2tr(as_internal: true) returns.

Full Changelog: v1.14.0...v1.15.0