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 )
@@ -90,7 +138,7 @@ func main() {
90
138
value := uint64 (123456 )
91
139
paymentRequestIndex := uint32 (0 )
92
140
93
- // Manually created. To test with a real device, modify and compile/use a firmware where "Test
141
+ // To test with a real device, modify and compile/use a firmware where "Test
94
142
// Merchant" (private key "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa") is included in the identities in
95
143
// payment_request.rs.
96
144
paymentRequest := & messages.BTCPaymentRequestRequest {
@@ -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 \n TextMemo 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