|
16 | 16 | package main
|
17 | 17 |
|
18 | 18 | import (
|
| 19 | + "crypto/sha256" |
| 20 | + "encoding/binary" |
19 | 21 | "encoding/hex"
|
| 22 | + "errors" |
20 | 23 | "fmt"
|
| 24 | + "hash" |
21 | 25 | "log"
|
22 | 26 |
|
23 | 27 | "github.com/BitBoxSwiss/bitbox02-api-go/api/common"
|
24 | 28 | "github.com/BitBoxSwiss/bitbox02-api-go/api/firmware"
|
25 | 29 | "github.com/BitBoxSwiss/bitbox02-api-go/api/firmware/messages"
|
26 | 30 | "github.com/BitBoxSwiss/bitbox02-api-go/api/firmware/mocks"
|
27 | 31 | "github.com/BitBoxSwiss/bitbox02-api-go/communication/u2fhid"
|
| 32 | + "github.com/btcsuite/btcd/btcec/v2" |
| 33 | + "github.com/btcsuite/btcd/btcec/v2/ecdsa" |
| 34 | + "github.com/btcsuite/btcd/wire" |
28 | 35 | "github.com/karalabe/usb"
|
29 | 36 | )
|
30 | 37 |
|
@@ -58,6 +65,47 @@ func isBitBox02(deviceInfo *usb.DeviceInfo) bool {
|
58 | 65 | (deviceInfo.UsagePage == 0xffff || deviceInfo.Interface == 0)
|
59 | 66 | }
|
60 | 67 |
|
| 68 | +func hashDataLenPrefixed(hasher hash.Hash, data []byte) { |
| 69 | + _ = wire.WriteVarInt(hasher, 0, uint64(len(data))) |
| 70 | + hasher.Write(data) |
| 71 | +} |
| 72 | + |
| 73 | +func computeSighash(paymentRequest *messages.BTCPaymentRequestRequest, slip44 uint32, outputValue uint64, outputAddress string) ([]byte, error) { |
| 74 | + sighash := sha256.New() |
| 75 | + |
| 76 | + // versionMagic |
| 77 | + sighash.Write([]byte("SL\x00\x24")) |
| 78 | + |
| 79 | + // nonce |
| 80 | + hashDataLenPrefixed(sighash, paymentRequest.Nonce) |
| 81 | + |
| 82 | + // recipientName |
| 83 | + hashDataLenPrefixed(sighash, []byte(paymentRequest.RecipientName)) |
| 84 | + |
| 85 | + // memos |
| 86 | + _ = wire.WriteVarInt(sighash, 0, uint64(len(paymentRequest.Memos))) |
| 87 | + for _, memo := range paymentRequest.Memos { |
| 88 | + switch m := memo.Memo.(type) { |
| 89 | + case *messages.BTCPaymentRequestRequest_Memo_TextMemo_: |
| 90 | + _ = binary.Write(sighash, binary.LittleEndian, uint32(1)) |
| 91 | + hashDataLenPrefixed(sighash, []byte(m.TextMemo.Note)) |
| 92 | + default: |
| 93 | + return nil, errors.New("unsupported memo type") |
| 94 | + } |
| 95 | + } |
| 96 | + |
| 97 | + // coinType |
| 98 | + _ = binary.Write(sighash, binary.LittleEndian, slip44) |
| 99 | + |
| 100 | + // outputsHash (only one output for now) |
| 101 | + outputHasher := sha256.New() |
| 102 | + _ = binary.Write(outputHasher, binary.LittleEndian, outputValue) |
| 103 | + hashDataLenPrefixed(outputHasher, []byte(outputAddress)) |
| 104 | + sighash.Write(outputHasher.Sum(nil)) |
| 105 | + |
| 106 | + return sighash.Sum(nil), nil |
| 107 | +} |
| 108 | + |
61 | 109 | func main() {
|
62 | 110 | deviceInfo := func() *usb.DeviceInfo {
|
63 | 111 | infos, err := usb.EnumerateHid(0, 0)
|
@@ -99,16 +147,24 @@ func main() {
|
99 | 147 | {
|
100 | 148 | Memo: &messages.BTCPaymentRequestRequest_Memo_TextMemo_{
|
101 | 149 | TextMemo: &messages.BTCPaymentRequestRequest_Memo_TextMemo{
|
102 |
| - Note: "TextMemo", |
| 150 | + Note: "TextMemo line1\nTextMemo line2", |
103 | 151 | },
|
104 | 152 | },
|
105 | 153 | },
|
106 | 154 | },
|
107 | 155 | Nonce: nil,
|
108 | 156 | TotalAmount: value,
|
109 |
| - Signature: unhex("b719cf98cc8a0f9191d4be1a6609037b5b084674d8e64b13199408813459a1b3033ff58c6468b35acc4ded661c8e23348823887046c778e6eba2e5b9586b9a25"), |
110 | 157 | }
|
111 | 158 |
|
| 159 | + // Sign the payment request. |
| 160 | + sighash, err := computeSighash(paymentRequest, 1, value, "tb1q2q0j6gmfxynj40p0kxsr9jkagcvgpuqvqynnup") |
| 161 | + errpanic(err) |
| 162 | + privKey, _ := btcec.PrivKeyFromBytes([]byte("aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa")) |
| 163 | + errpanic(err) |
| 164 | + signature, err := ecdsa.SignCompact(privKey, sighash, true) |
| 165 | + errpanic(err) |
| 166 | + paymentRequest.Signature = signature[1:] |
| 167 | + |
112 | 168 | _, err = device.BTCSign(
|
113 | 169 | messages.BTCCoin_TBTC,
|
114 | 170 | []*messages.BTCScriptConfigWithKeypath{
|
|
0 commit comments