Skip to content

Commit

Permalink
Merge branch 'tapminiscript'
Browse files Browse the repository at this point in the history
  • Loading branch information
benma committed Sep 24, 2024
2 parents 07f7a9d + aa9b932 commit 3dfb8fa
Show file tree
Hide file tree
Showing 11 changed files with 875 additions and 97 deletions.
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ customers cannot upgrade their bootloader, its changes are recorded separately.

### [Unreleased]
- Bitcoin: add support for sending to silent payment (BIP-352) addresses
- Bitcoin: add support for Taproot wallet policies and Miniscript on Taproot (MiniTapscript)
- Bitcoin: add support for regtest
- Cardano: add support for vote delegation

Expand Down
25 changes: 11 additions & 14 deletions src/keystore.c
Original file line number Diff line number Diff line change
Expand Up @@ -941,9 +941,10 @@ bool keystore_secp256k1_schnorr_bip86_pubkey(const uint8_t* pubkey33, uint8_t* p
return secp256k1_xonly_pubkey_serialize(ctx, pubkey_out, &tweaked_xonly_pubkey) == 1;
}

static bool _schnorr_bip86_keypair(
static bool _schnorr_keypair(
const uint32_t* keypath,
size_t keypath_len,
const uint8_t* tweak,
secp256k1_keypair* keypair_out,
secp256k1_xonly_pubkey* pubkey_out)
{
Expand All @@ -959,36 +960,32 @@ static bool _schnorr_bip86_keypair(
if (!secp256k1_keypair_create(ctx, keypair_out, secret_key)) {
return false;
}
if (!secp256k1_keypair_xonly_pub(ctx, pubkey_out, NULL, keypair_out)) {
return false;
}
uint8_t pubkey_serialized[32] = {0};
if (!secp256k1_xonly_pubkey_serialize(ctx, pubkey_serialized, pubkey_out)) {
return false;
if (tweak != NULL) {
if (secp256k1_keypair_xonly_tweak_add(ctx, keypair_out, tweak) != 1) {
return false;
}
}
uint8_t hash[32] = {0};
_tagged_hash("TapTweak", pubkey_serialized, sizeof(pubkey_serialized), hash);

if (secp256k1_keypair_xonly_tweak_add(ctx, keypair_out, hash) != 1) {
if (!secp256k1_keypair_xonly_pub(ctx, pubkey_out, NULL, keypair_out)) {
return false;
}
return secp256k1_keypair_xonly_pub(ctx, pubkey_out, NULL, keypair_out) == 1;
return true;
}

static void _cleanup_keypair(secp256k1_keypair* keypair)
{
util_zero(keypair, sizeof(secp256k1_keypair));
}

bool keystore_secp256k1_schnorr_bip86_sign(
bool keystore_secp256k1_schnorr_sign(
const uint32_t* keypath,
size_t keypath_len,
const uint8_t* msg32,
const uint8_t* tweak,
uint8_t* sig64_out)
{
secp256k1_keypair __attribute__((__cleanup__(_cleanup_keypair))) keypair = {0};
secp256k1_xonly_pubkey pubkey = {0};
if (!_schnorr_bip86_keypair(keypath, keypath_len, &keypair, &pubkey)) {
if (!_schnorr_keypair(keypath, keypath_len, tweak, &keypair, &pubkey)) {
return false;
}
const secp256k1_context* ctx = wally_get_secp_context();
Expand Down
5 changes: 4 additions & 1 deletion src/keystore.h
Original file line number Diff line number Diff line change
Expand Up @@ -295,12 +295,15 @@ USE_RESULT bool keystore_secp256k1_schnorr_bip86_pubkey(
* @param[in] keypath derivation keypath
* @param[in] keypath_len number of elements in keypath
* @param[in] msg32 32 byte message to sign
* @param[in] tweak 32 bytes, tweak private key before signing with this tweak. Use NULL to not
* tweak.
* @param[out] sig64_out resulting 64 byte signature
*/
USE_RESULT bool keystore_secp256k1_schnorr_bip86_sign(
USE_RESULT bool keystore_secp256k1_schnorr_sign(
const uint32_t* keypath,
size_t keypath_len,
const uint8_t* msg32,
const uint8_t* tweak,
uint8_t* sig64_out);

/**
Expand Down
3 changes: 2 additions & 1 deletion src/rust/bitbox02-rust/src/hww/api/bitcoin.rs
Original file line number Diff line number Diff line change
Expand Up @@ -254,7 +254,8 @@ async fn address_policy(
.await?;
}

let address = common::Payload::from_policy(&parsed, keypath)?.address(coin_params)?;
let address =
common::Payload::from_policy(coin_params, &parsed, keypath)?.address(coin_params)?;
if display {
confirm::confirm(&confirm::Params {
title,
Expand Down
44 changes: 41 additions & 3 deletions src/rust/bitbox02-rust/src/hww/api/bitcoin/bip341.rs
Original file line number Diff line number Diff line change
Expand Up @@ -27,14 +27,16 @@ pub struct Args {
pub hash_outputs: [u8; 32],
// Data about this input:
pub input_index: u32,
// tapleaf_hash as described in https://github.com/bitcoin/bips/blob/85cda4e225b4d5fd7aff403f69d827f23f6afbbc/bip-0342.mediawiki#common-signature-message-extension
// Providing this means we use the above tapscript message extension.
pub tapleaf_hash: Option<[u8; 32]>,
}

/// Compute the BIP341 signature hash.
///
/// https://github.com/bitcoin/bips/blob/bb8dc57da9b3c6539b88378348728a2ff43f7e9c/bip-0341.mediawiki#common-signature-message
///
/// The hash_type is assumed 0 (`SIGHASH_DEFAULT`). The `ext_flag` is
/// assumed 0 and `annex` is assumed to be not present.
/// The hash_type is assumed 0 (`SIGHASH_DEFAULT`). `annex` is assumed to be not present.
pub fn sighash(args: &Args) -> [u8; 32] {
let tag = Sha256::digest(b"TapSighash");
let mut ctx = Sha256::new();
Expand All @@ -53,10 +55,28 @@ pub fn sighash(args: &Args) -> [u8; 32] {
ctx.update(args.hash_sequences);
ctx.update(args.hash_outputs);
// spend_type is 0 because ext_flag is 0 and annex is absent.
ctx.update(0u8.to_le_bytes());
let ext_flag = if args.tapleaf_hash.is_some() {
// ext_flag = 1 for Taproot leaf scripts
// See https://github.com/bitcoin/bips/blob/85cda4e225b4d5fd7aff403f69d827f23f6afbbc/bip-0342.mediawiki#common-signature-message-extension
1
} else {
0
};
let spend_type: u8 = 2 * ext_flag;
ctx.update(spend_type.to_le_bytes());
// Data about this input:
ctx.update(args.input_index.to_le_bytes());

if let Some(hash) = args.tapleaf_hash.as_ref() {
// See https://github.com/bitcoin/bips/blob/85cda4e225b4d5fd7aff403f69d827f23f6afbbc/bip-0342.mediawiki#common-signature-message-extension
// tapleaf_hash
ctx.update(hash);
// keyversion
ctx.update(0u8.to_le_bytes());
// codesep_pos - we do not use any OP_CODESEPARATORs.
let codesep_pos: u32 = 0xFFFFFFFF;
ctx.update(codesep_pos.to_le_bytes());
}
ctx.finalize().into()
}

Expand All @@ -79,7 +99,25 @@ mod tests {
hash_sequences: *b"\x18\x95\x9c\x72\x21\xab\x5c\xe9\xe2\x6c\x3c\xd6\x7b\x22\xc2\x4f\x8b\xaa\x54\xba\xc2\x81\xd8\xe6\xb0\x5e\x40\x0e\x6c\x3a\x95\x7e",
hash_outputs: *b"\xa2\xe6\xda\xb7\xc1\xf0\xdc\xd2\x97\xc8\xd6\x16\x47\xfd\x17\xd8\x21\x54\x1e\xa6\x9c\x3c\xc3\x7d\xcb\xad\x7f\x90\xd4\xeb\x4b\xc5",
input_index: 4,
tapleaf_hash: None,
}),
*b"\x4f\x90\x0a\x0b\xae\x3f\x14\x46\xfd\x48\x49\x0c\x29\x58\xb5\xa0\x23\x22\x8f\x01\x66\x1c\xda\x34\x96\xa1\x1d\xa5\x02\xa7\xf7\xef");
}

#[test]
fn test_sighash_tapleaf() {
assert_eq!(
sighash(&Args {
version: 2,
locktime: 500000000,
hash_prevouts: *b"\xe3\xb3\x3b\xb4\xef\x3a\x52\xad\x1f\xff\xb5\x55\xc0\xd8\x28\x28\xeb\x22\x73\x70\x36\xea\xeb\x02\xa2\x35\xd8\x2b\x90\x9c\x4c\x3f",
hash_amounts: *b"\x58\xa6\x96\x4a\x4f\x5f\x8f\x0b\x64\x2d\xed\x0a\x8a\x55\x3b\xe7\x62\x2a\x71\x9d\xa7\x1d\x1f\x5b\xef\xce\xfc\xde\xe8\xe0\xfd\xe6",
hash_scriptpubkeys: *b"\x23\xad\x0f\x61\xad\x2b\xca\x5b\xa6\xa7\x69\x3f\x50\xfc\xe9\x88\xe1\x7c\x37\x80\xbf\x2b\x1e\x72\x0c\xfb\xb3\x8f\xbd\xd5\x2e\x21",
hash_sequences: *b"\x18\x95\x9c\x72\x21\xab\x5c\xe9\xe2\x6c\x3c\xd6\x7b\x22\xc2\x4f\x8b\xaa\x54\xba\xc2\x81\xd8\xe6\xb0\x5e\x40\x0e\x6c\x3a\x95\x7e",
hash_outputs: *b"\xa2\xe6\xda\xb7\xc1\xf0\xdc\xd2\x97\xc8\xd6\x16\x47\xfd\x17\xd8\x21\x54\x1e\xa6\x9c\x3c\xc3\x7d\xcb\xad\x7f\x90\xd4\xeb\x4b\xc5",
input_index: 4,
tapleaf_hash: Some(*b"\x34\xe7\x21\x15\xc0\x9c\x91\x3c\x8b\xe1\x2e\x46\xfc\x14\x5f\xcf\x7c\x53\xca\xd9\xca\x2a\x05\xf9\x3a\x7c\xa2\xe0\xca\x88\xd0\x07"),
}),
*b"\xba\xe0\xaa\xcb\xa5\xae\xa9\xee\xbe\x19\xe1\x57\xa9\x8f\x1e\xe7\x0d\x7d\x28\x8c\x28\x0f\x27\x3e\x63\xbb\x8a\x85\xd1\xee\xf3\xc2");
}
}
15 changes: 13 additions & 2 deletions src/rust/bitbox02-rust/src/hww/api/bitcoin/common.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
// Copyright 2022 Shift Crypto AG
// Copyright 2022-2024 Shift Crypto AG
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
Expand Down Expand Up @@ -156,6 +156,7 @@ impl Payload {
/// derived using keypath m/48'/1'/0'/3'/11/5 derives the payload for
/// wsh(and_v(v:pk(@0/11/5),pk(@1/21/5))).
pub fn from_policy(
params: &Params,
policy: &super::policies::ParsedPolicy,
keypath: &[u32],
) -> Result<Self, Error> {
Expand All @@ -165,6 +166,16 @@ impl Payload {
data: Sha256::digest(wsh.witness_script()).to_vec(),
output_type: BtcOutputType::P2wsh,
}),
super::policies::Descriptor::Tr(tr) => {
if params.taproot_support {
Ok(Payload {
data: tr.output_key().to_vec(),
output_type: BtcOutputType::P2tr,
})
} else {
Err(Error::InvalidInput)
}
}
}
}

Expand All @@ -186,7 +197,7 @@ impl Payload {
keypath[keypath.len() - 2],
keypath[keypath.len() - 1],
),
ValidatedScriptConfig::Policy(policy) => Self::from_policy(policy, keypath),
ValidatedScriptConfig::Policy(policy) => Self::from_policy(params, policy, keypath),
}
}

Expand Down
Loading

0 comments on commit 3dfb8fa

Please sign in to comment.