Skip to content

Commit a38c5f2

Browse files
committed
firmware/tests: test against bitbox02 simulator
1 parent fa7f932 commit a38c5f2

File tree

10 files changed

+497
-1
lines changed

10 files changed

+497
-1
lines changed

api/firmware/bip85_test.go

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
// Copyright 2024 Shift Crypto AG
2+
//
3+
// Licensed under the Apache License, Version 2.0 (the "License");
4+
// you may not use this file except in compliance with the License.
5+
// You may obtain a copy of the License at
6+
//
7+
// http://www.apache.org/licenses/LICENSE-2.0
8+
//
9+
// Unless required by applicable law or agreed to in writing, software
10+
// distributed under the License is distributed on an "AS IS" BASIS,
11+
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
// See the License for the specific language governing permissions and
13+
// limitations under the License.
14+
15+
package firmware
16+
17+
import (
18+
"encoding/hex"
19+
20+
"github.com/stretchr/testify/require"
21+
)
22+
23+
func (s *initializedSimulatorTestSuite) TestBIP85AppBip39() {
24+
// Can't test this yet as the simulator panics at trinary_choice (12, 18, 24 word choice).
25+
//require.NoError(s.T(), s.device.BIP85AppBip39())
26+
}
27+
28+
func (s *initializedSimulatorTestSuite) TestBIP85AppLN() {
29+
entropy, err := s.device.BIP85AppLN()
30+
require.NoError(s.T(), err)
31+
require.Equal(s.T(),
32+
"d05448562b8b64994b7de7eac43cdc8a",
33+
hex.EncodeToString(entropy))
34+
}

api/firmware/btc_test.go

Lines changed: 78 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
// Copyright 2018-2019 Shift Cryptosecurity AG
2+
// Copyright 2024 Shift Crypto AG
23
//
34
// Licensed under the Apache License, Version 2.0 (the "License");
45
// you may not use this file except in compliance with the License.
@@ -20,12 +21,26 @@ import (
2021

2122
"github.com/BitBoxSwiss/bitbox02-api-go/api/firmware/messages"
2223
"github.com/BitBoxSwiss/bitbox02-api-go/util/semver"
24+
"github.com/btcsuite/btcd/btcec/v2"
25+
"github.com/btcsuite/btcd/btcec/v2/ecdsa"
26+
"github.com/btcsuite/btcd/btcutil/hdkeychain"
27+
"github.com/btcsuite/btcd/chaincfg/chainhash"
2328
"github.com/stretchr/testify/require"
2429
"google.golang.org/protobuf/proto"
2530
)
2631

2732
const hardenedKeyStart = 0x80000000
2833

34+
func parseECDSASignature(t *testing.T, sig []byte) *ecdsa.Signature {
35+
t.Helper()
36+
require.Len(t, sig, 64)
37+
r := new(btcec.ModNScalar)
38+
r.SetByteSlice(sig[:32])
39+
s := new(btcec.ModNScalar)
40+
s.SetByteSlice(sig[32:])
41+
return ecdsa.NewSignature(r, s)
42+
}
43+
2944
func TestNewXPub(t *testing.T) {
3045
xpub, err := NewXPub(
3146
"xpub6FEZ9Bv73h1vnE4TJG4QFj2RPXJhhsPbnXgFyH3ErLvpcZrDcynY65bhWga8PazWHLSLi23PoBhGcLcYW6JRiJ12zXZ9Aop4LbAqsS3gtcy")
@@ -39,6 +54,69 @@ func TestNewXPub(t *testing.T) {
3954
}, xpub)
4055
}
4156

57+
func (s *initializedSimulatorTestSuite) TestBTCXpub() {
58+
xpub, err := s.device.BTCXPub(messages.BTCCoin_TBTC, []uint32{
59+
49 + hardenedKeyStart,
60+
1 + hardenedKeyStart,
61+
0 + hardenedKeyStart,
62+
}, messages.BTCPubRequest_YPUB, false)
63+
require.NoError(s.T(), err)
64+
require.Equal(s.T(), "ypub6WqXiL3fbDK5QNPe3hN4uSVkEvuE8wXoNCcecgggSuKVpU3Kc4fTvhuLgUhtnbAdaTb9gpz5PQdvzcsKPTLgW2CPkF5ZNRzQeKFT4NSc1xN", xpub)
65+
}
66+
67+
func (s *initializedSimulatorTestSuite) TestBTCAddress() {
68+
address, err := s.device.BTCAddress(
69+
messages.BTCCoin_TBTC,
70+
[]uint32{
71+
84 + hardenedKeyStart,
72+
1 + hardenedKeyStart,
73+
0 + hardenedKeyStart,
74+
1,
75+
10,
76+
},
77+
NewBTCScriptConfigSimple(messages.BTCScriptConfig_P2WPKH),
78+
false,
79+
)
80+
require.NoError(s.T(), err)
81+
require.Equal(s.T(), "tb1qq064dxjgl9h9wzgsmzy6t6306qew42w9ka02u3", address)
82+
}
83+
84+
func parseXPub(t *testing.T, xpubStr string, keypath ...uint32) *hdkeychain.ExtendedKey {
85+
t.Helper()
86+
xpub, err := hdkeychain.NewKeyFromString(xpubStr)
87+
require.NoError(t, err)
88+
89+
for _, child := range keypath {
90+
xpub, err = xpub.Derive(child)
91+
require.NoError(t, err)
92+
}
93+
return xpub
94+
}
95+
96+
func (s *initializedSimulatorTestSuite) TestBTCSignMessage() {
97+
coin := messages.BTCCoin_BTC
98+
accountKeypath := []uint32{49 + hardenedKeyStart, 0 + hardenedKeyStart, 0 + hardenedKeyStart}
99+
100+
xpubStr, err := s.device.BTCXPub(coin, accountKeypath, messages.BTCPubRequest_XPUB, false)
101+
require.NoError(s.T(), err)
102+
103+
xpub := parseXPub(s.T(), xpubStr, 0, 10)
104+
pubKey, err := xpub.ECPubKey()
105+
require.NoError(s.T(), err)
106+
107+
sig, _, _, err := s.device.BTCSignMessage(
108+
coin,
109+
&messages.BTCScriptConfigWithKeypath{
110+
ScriptConfig: NewBTCScriptConfigSimple(messages.BTCScriptConfig_P2WPKH_P2SH),
111+
Keypath: append(accountKeypath, 0, 10),
112+
},
113+
[]byte("message"),
114+
)
115+
require.NoError(s.T(), err)
116+
sigHash := chainhash.DoubleHashB([]byte("\x18Bitcoin Signed Message:\n\x07message"))
117+
require.True(s.T(), parseECDSASignature(s.T(), sig).Verify(sigHash, pubKey))
118+
}
119+
42120
func TestBTCXPub(t *testing.T) {
43121
testConfigurations(t, func(t *testing.T, env *testEnv) {
44122
t.Helper()

api/firmware/cardano_test.go

Lines changed: 66 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,66 @@
1+
// Copyright 2024 Shift Crypto AG
2+
//
3+
// Licensed under the Apache License, Version 2.0 (the "License");
4+
// you may not use this file except in compliance with the License.
5+
// You may obtain a copy of the License at
6+
//
7+
// http://www.apache.org/licenses/LICENSE-2.0
8+
//
9+
// Unless required by applicable law or agreed to in writing, software
10+
// distributed under the License is distributed on an "AS IS" BASIS,
11+
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
// See the License for the specific language governing permissions and
13+
// limitations under the License.
14+
15+
package firmware
16+
17+
import (
18+
"encoding/hex"
19+
20+
"github.com/BitBoxSwiss/bitbox02-api-go/api/firmware/messages"
21+
"github.com/stretchr/testify/require"
22+
)
23+
24+
func (s *initializedSimulatorTestSuite) TestCardanoXPubs() {
25+
xpubs, err := s.device.CardanoXPubs(
26+
[][]uint32{
27+
{1852 + hardenedKeyStart, 1815 + hardenedKeyStart, hardenedKeyStart},
28+
{1852 + hardenedKeyStart, 1815 + hardenedKeyStart, hardenedKeyStart + 1},
29+
},
30+
)
31+
require.NoError(s.T(), err)
32+
require.Len(s.T(), xpubs, 2)
33+
require.Equal(s.T(),
34+
"9fc9550e8379cb97c2d2557d89574207c6cf4d4ff62b37e377f2b3b3c284935b677f0fe5a4a6928c7b982c0c149f140c26c0930b73c2fe16feddfa21625e0316",
35+
hex.EncodeToString(xpubs[0]),
36+
)
37+
require.Equal(s.T(),
38+
"7ffd0bd7d54f1648ac59a357d3eb27b878c2f7c09739d3b7c7e6662d496dea16f10ef525258833d37db047cd530bf373ebcb283495aa4c768424a2af37cee661",
39+
hex.EncodeToString(xpubs[1]),
40+
)
41+
}
42+
43+
func (s *initializedSimulatorTestSuite) TestCardanoAddress() {
44+
const account = uint32(1)
45+
const rolePayment = uint32(0) // receive
46+
const roleStake = uint32(2) // stake role must be 2
47+
const addressIdx = uint32(10) // address index
48+
const stakeAddressIdx = uint32(0) // stake addr idx must be 0
49+
address, err := s.device.CardanoAddress(
50+
messages.CardanoNetwork_CardanoMainnet,
51+
&messages.CardanoScriptConfig{
52+
Config: &messages.CardanoScriptConfig_PkhSkh_{
53+
PkhSkh: &messages.CardanoScriptConfig_PkhSkh{
54+
KeypathPayment: []uint32{1852 + hardenedKeyStart, 1815 + hardenedKeyStart, account + hardenedKeyStart, rolePayment, addressIdx},
55+
KeypathStake: []uint32{1852 + hardenedKeyStart, 1815 + hardenedKeyStart, account + hardenedKeyStart, roleStake, stakeAddressIdx},
56+
},
57+
},
58+
},
59+
false,
60+
)
61+
require.NoError(s.T(), err)
62+
require.Equal(s.T(),
63+
"addr1qxdq2ez52f5gtva3m77xgf5x4a7ap78mal43e5hhszyqehaaddssj2eta30yv9chr0sf4gu0jw77gag2g464yq0c70gqks5cr4",
64+
address,
65+
)
66+
}

api/firmware/device_test.go

Lines changed: 65 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
// Copyright 2018-2019 Shift Cryptosecurity AG
2+
// Copyright 2024 Shift Crypto AG
23
//
34
// Licensed under the Apache License, Version 2.0 (the "License");
45
// you may not use this file except in compliance with the License.
@@ -18,17 +19,81 @@ import (
1819
"crypto/rand"
1920
"errors"
2021
"fmt"
22+
"net"
23+
"os/exec"
2124
"testing"
25+
"time"
2226

2327
"github.com/BitBoxSwiss/bitbox02-api-go/api/common"
2428
"github.com/BitBoxSwiss/bitbox02-api-go/api/firmware/messages"
2529
"github.com/BitBoxSwiss/bitbox02-api-go/api/firmware/mocks"
30+
"github.com/BitBoxSwiss/bitbox02-api-go/communication/u2fhid"
2631
"github.com/BitBoxSwiss/bitbox02-api-go/util/semver"
2732
"github.com/flynn/noise"
2833
"github.com/stretchr/testify/require"
34+
"github.com/stretchr/testify/suite"
2935
"google.golang.org/protobuf/proto"
3036
)
3137

38+
// Runs tests against a simulator which is not initialzed (not seeded).
39+
type simulatorTestSuite struct {
40+
suite.Suite
41+
42+
cmd *exec.Cmd
43+
device *Device
44+
conn net.Conn
45+
}
46+
47+
func (s *simulatorTestSuite) SetupSuite() {
48+
s.cmd = exec.Command("./testdata/simulator")
49+
require.NoError(s.T(), s.cmd.Start())
50+
51+
var err error
52+
for i := 0; i < 200; i++ {
53+
s.conn, err = net.Dial("tcp", "localhost:15423")
54+
if err == nil {
55+
break
56+
}
57+
time.Sleep(10 * time.Millisecond)
58+
}
59+
require.NoError(s.T(), err)
60+
61+
const bitboxCMD = 0x80 + 0x40 + 0x01
62+
63+
communication := u2fhid.NewCommunication(s.conn, bitboxCMD)
64+
s.device = NewDevice(nil, nil,
65+
&mocks.Config{}, communication, &mocks.Logger{},
66+
)
67+
require.NoError(s.T(), s.device.Init())
68+
s.device.ChannelHashVerify(true)
69+
70+
require.Equal(s.T(), common.ProductBitBox02Multi, s.device.Product())
71+
}
72+
73+
func (s *simulatorTestSuite) TearDownSuite() {
74+
require.NoError(s.T(), s.conn.Close())
75+
require.NoError(s.T(), s.cmd.Process.Kill())
76+
}
77+
78+
func TestSimulatorSuite(t *testing.T) {
79+
suite.Run(t, &simulatorTestSuite{})
80+
}
81+
82+
// Runs tests againt a simulator that is seeded with this mnemonic:
83+
// boring mistake dish oyster truth pigeon viable emerge sort crash wire portion cannon couple enact box walk height pull today solid off enable tide
84+
type initializedSimulatorTestSuite struct {
85+
simulatorTestSuite
86+
}
87+
88+
func (s *initializedSimulatorTestSuite) SetupSuite() {
89+
s.simulatorTestSuite.SetupSuite()
90+
require.NoError(s.T(), s.device.RestoreFromMnemonic())
91+
}
92+
93+
func TestInitializedSimulatorSuite(t *testing.T) {
94+
suite.Run(t, &initializedSimulatorTestSuite{})
95+
}
96+
3297
// newDevice creates a device to test with, with init/pairing already processed.
3398
func newDevice(
3499
t *testing.T,

api/firmware/eth.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -253,7 +253,7 @@ func (device *Device) ETHSignEIP1559(
253253
}
254254

255255
// ETHSignMessage signs an Ethereum message. The provided msg will be prefixed with "\x19Ethereum
256-
// message\n" + len(msg) in the hardware, e.g. "\x19Ethereum\n5hello" (yes, the len prefix is the
256+
// Signed Message\n" + len(msg) in the hardware, e.g. "\x19Ethereum Signed dMessage\n5hello" (yes, the len prefix is the
257257
// ascii representation with no fixed size or delimiter, WTF).
258258
// 27 is added to the recID to denote an uncompressed pubkey.
259259
func (device *Device) ETHSignMessage(

0 commit comments

Comments
 (0)