diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index f7e7594..c10f241 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -11,9 +11,9 @@ jobs: runs-on: ubuntu-22.04 steps: - name: Clone the repo - uses: actions/checkout@v3 + uses: actions/checkout@v4 - name: Enable caching - uses: actions/cache@v3 + uses: actions/cache@v4 with: # Increment cache number to invalidate. key: ${{runner.os}}-cache-1 @@ -22,7 +22,7 @@ jobs: ~/.cache/go-build ~/.cache/golangci-lint - name: Install Go - uses: actions/setup-go@v3 + uses: actions/setup-go@v4 with: go-version: ${{env.GO_VERSION}} # Keep the linter version and its config in .golangci.yml in sync with the app repo at @@ -37,9 +37,9 @@ jobs: runs-on: ubuntu-22.04 steps: - name: Clone the repo - uses: actions/checkout@v3 + uses: actions/checkout@v4 - name: Enable caching - uses: actions/cache@v3 + uses: actions/cache@v4 with: # Increment cache number to invalidate. key: ${{runner.os}}-cache-1 @@ -47,21 +47,27 @@ jobs: ~/go/pkg ~/.cache/go-build ~/.cache/golangci-lint + - name: Enable simulators caching + uses: actions/cache@v4 + with: + key: ${{runner.os}}-simulators-cache-${{hashFiles('./api/firmware/testdata/simulators.json')}} + path: | + ./api/firmware/testdata/simulators - name: Install Go - uses: actions/setup-go@v3 + uses: actions/setup-go@v4 with: go-version: ${{env.GO_VERSION}} - name: Test run: | go version - go test ./... + go test ./... -v build: runs-on: ubuntu-22.04 steps: - name: Clone the repo - uses: actions/checkout@v3 + uses: actions/checkout@v4 - name: Enable caching - uses: actions/cache@v3 + uses: actions/cache@v4 with: # Increment cache number to invalidate. key: ${{runner.os}}-cache-1 @@ -70,7 +76,7 @@ jobs: ~/.cache/go-build ~/.cache/golangci-lint - name: Install Go - uses: actions/setup-go@v3 + uses: actions/setup-go@v4 with: go-version: ${{env.GO_VERSION}} - name: Build diff --git a/api/bootloader/device_test.go b/api/bootloader/device_test.go index 40b6532..319f6f3 100644 --- a/api/bootloader/device_test.go +++ b/api/bootloader/device_test.go @@ -19,7 +19,7 @@ import ( "encoding/hex" "errors" "fmt" - "io/ioutil" + "os" "testing" "github.com/BitBoxSwiss/bitbox02-api-go/api/bootloader" @@ -184,7 +184,7 @@ func TestGetHashes(t *testing.T) { // TestSignedFirmwareVersion tests device.SignedFirmwareVersion. func TestSignedFirmwareVersion(t *testing.T) { - signedFirmware, err := ioutil.ReadFile("testdata/firmware-btc.v4.2.2.signed.bin") + signedFirmware, err := os.ReadFile("testdata/firmware-btc.v4.2.2.signed.bin") if err != nil { panic(err) } @@ -209,11 +209,11 @@ func TestSignedFirmwareVersion(t *testing.T) { // TestUpgradeFirmware tests a successful firmware upgrade with a real-world signed firmware // fixture. func TestUpgradeFirmware(t *testing.T) { - signedFirmware, err := ioutil.ReadFile("testdata/firmware-btc.v4.2.2.signed.bin") + signedFirmware, err := os.ReadFile("testdata/firmware-btc.v4.2.2.signed.bin") if err != nil { panic(err) } - unsignedFirmware, err := ioutil.ReadFile("testdata/firmware-btc.v4.2.2.bin") + unsignedFirmware, err := os.ReadFile("testdata/firmware-btc.v4.2.2.bin") if err != nil { panic(err) } diff --git a/api/bootloader/util_test.go b/api/bootloader/util_test.go index 5e9dc3b..b430a6b 100644 --- a/api/bootloader/util_test.go +++ b/api/bootloader/util_test.go @@ -16,7 +16,7 @@ package bootloader import ( "encoding/hex" - "io/ioutil" + "os" "testing" "github.com/BitBoxSwiss/bitbox02-api-go/api/common" @@ -27,7 +27,7 @@ func TestHashFirmware(t *testing.T) { emptyHash := []byte("\xad\x27\x67\x91\x84\x74\xf3\x30\x02\x95\xb2\xef\x94\x9a\xe8\x13\xd7\x87\x0c\xed\x70\x30\x58\x29\xa0\x12\x91\xa4\x8f\x8b\xbc\x78") require.Equal(t, emptyHash, HashFirmware(5, []byte{})) - unsignedFirmware, err := ioutil.ReadFile("testdata/firmware-btc.v4.2.2.bin") + unsignedFirmware, err := os.ReadFile("testdata/firmware-btc.v4.2.2.bin") require.NoError(t, err) require.Equal(t, []byte("\x9a\xfc\x65\xa1\x99\x6c\x0d\xfd\xbb\x17\x08\xbf\x51\x8d\x96\x8c\xde\xc7\xe3\xc3\x52\x56\x1e\x2b\x09\x1d\x91\x83\x6c\x06\x8a\xe5"), @@ -36,10 +36,10 @@ func TestHashFirmware(t *testing.T) { } func TestParseSignedFirmmare(t *testing.T) { - unsignedFirmware, err := ioutil.ReadFile("testdata/firmware-btc.v4.2.2.bin") + unsignedFirmware, err := os.ReadFile("testdata/firmware-btc.v4.2.2.bin") require.NoError(t, err) - signedFirmware, err := ioutil.ReadFile("testdata/firmware-btc.v4.2.2.signed.bin") + signedFirmware, err := os.ReadFile("testdata/firmware-btc.v4.2.2.signed.bin") require.NoError(t, err) product, sigData, firmware, err := ParseSignedFirmware(signedFirmware) diff --git a/api/firmware/backup_test.go b/api/firmware/backup_test.go new file mode 100644 index 0000000..9d23f25 --- /dev/null +++ b/api/firmware/backup_test.go @@ -0,0 +1,59 @@ +// Copyright 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. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package firmware + +import ( + "testing" + + "github.com/stretchr/testify/require" +) + +func TestSimulatorBackups(t *testing.T) { + const seedLen = 32 + const testName = "test wallet name" + testSimulatorsAfterPairing(t, func(t *testing.T, device *Device) { + t.Helper() + require.NoError(t, device.SetDeviceName(testName)) + + require.NoError(t, device.SetPassword(seedLen)) + require.Equal(t, StatusSeeded, device.Status()) + + list, err := device.ListBackups() + require.NoError(t, err) + require.Empty(t, list) + + _, err = device.CheckBackup(true) + require.Error(t, err) + + require.NoError(t, device.CreateBackup()) + require.Equal(t, StatusInitialized, device.Status()) + + list, err = device.ListBackups() + require.NoError(t, err) + require.Len(t, list, 1) + require.Equal(t, testName, list[0].Name) + + id, err := device.CheckBackup(true) + require.NoError(t, err) + require.Equal(t, list[0].ID, id) + + require.Error(t, device.RestoreBackup(list[0].ID)) + require.NoError(t, device.Reset()) + require.NoError(t, device.RestoreBackup(list[0].ID)) + id, err = device.CheckBackup(true) + require.NoError(t, err) + require.Equal(t, list[0].ID, id) + }) +} diff --git a/api/firmware/bip85_test.go b/api/firmware/bip85_test.go new file mode 100644 index 0000000..37914e6 --- /dev/null +++ b/api/firmware/bip85_test.go @@ -0,0 +1,38 @@ +// Copyright 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. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package firmware + +import ( + "encoding/hex" + "testing" + + "github.com/stretchr/testify/require" +) + +func TestSimulatorBIP85AppBip39(t *testing.T) { + // Can't test this yet as the simulator panics at trinary_choice (12, 18, 24 word choice). + t.Skip() +} + +func TestSimulatorBIP85AppLN(t *testing.T) { + testInitializedSimulators(t, func(t *testing.T, device *Device) { + t.Helper() + entropy, err := device.BIP85AppLN() + require.NoError(t, err) + require.Equal(t, + "d05448562b8b64994b7de7eac43cdc8a", + hex.EncodeToString(entropy)) + }) +} diff --git a/api/firmware/btc_test.go b/api/firmware/btc_test.go index 2d97f30..f3b26dc 100644 --- a/api/firmware/btc_test.go +++ b/api/firmware/btc_test.go @@ -1,4 +1,5 @@ // Copyright 2018-2019 Shift Cryptosecurity AG +// Copyright 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. @@ -20,12 +21,26 @@ import ( "github.com/BitBoxSwiss/bitbox02-api-go/api/firmware/messages" "github.com/BitBoxSwiss/bitbox02-api-go/util/semver" + "github.com/btcsuite/btcd/btcec/v2" + "github.com/btcsuite/btcd/btcec/v2/ecdsa" + "github.com/btcsuite/btcd/btcutil/hdkeychain" + "github.com/btcsuite/btcd/chaincfg/chainhash" "github.com/stretchr/testify/require" "google.golang.org/protobuf/proto" ) const hardenedKeyStart = 0x80000000 +func parseECDSASignature(t *testing.T, sig []byte) *ecdsa.Signature { + t.Helper() + require.Len(t, sig, 64) + r := new(btcec.ModNScalar) + r.SetByteSlice(sig[:32]) + s := new(btcec.ModNScalar) + s.SetByteSlice(sig[32:]) + return ecdsa.NewSignature(r, s) +} + func TestNewXPub(t *testing.T) { xpub, err := NewXPub( "xpub6FEZ9Bv73h1vnE4TJG4QFj2RPXJhhsPbnXgFyH3ErLvpcZrDcynY65bhWga8PazWHLSLi23PoBhGcLcYW6JRiJ12zXZ9Aop4LbAqsS3gtcy") @@ -39,7 +54,79 @@ func TestNewXPub(t *testing.T) { }, xpub) } -func TestBTCXPub(t *testing.T) { +func TestBTCXpub(t *testing.T) { + testInitializedSimulators(t, func(t *testing.T, device *Device) { + t.Helper() + xpub, err := device.BTCXPub(messages.BTCCoin_TBTC, []uint32{ + 49 + hardenedKeyStart, + 1 + hardenedKeyStart, + 0 + hardenedKeyStart, + }, messages.BTCPubRequest_YPUB, false) + require.NoError(t, err) + require.Equal(t, "ypub6WqXiL3fbDK5QNPe3hN4uSVkEvuE8wXoNCcecgggSuKVpU3Kc4fTvhuLgUhtnbAdaTb9gpz5PQdvzcsKPTLgW2CPkF5ZNRzQeKFT4NSc1xN", xpub) + }) +} + +func TestBTCAddress(t *testing.T) { + testInitializedSimulators(t, func(t *testing.T, device *Device) { + t.Helper() + address, err := device.BTCAddress( + messages.BTCCoin_TBTC, + []uint32{ + 84 + hardenedKeyStart, + 1 + hardenedKeyStart, + 0 + hardenedKeyStart, + 1, + 10, + }, + NewBTCScriptConfigSimple(messages.BTCScriptConfig_P2WPKH), + false, + ) + require.NoError(t, err) + require.Equal(t, "tb1qq064dxjgl9h9wzgsmzy6t6306qew42w9ka02u3", address) + }) +} + +func parseXPub(t *testing.T, xpubStr string, keypath ...uint32) *hdkeychain.ExtendedKey { + t.Helper() + xpub, err := hdkeychain.NewKeyFromString(xpubStr) + require.NoError(t, err) + + for _, child := range keypath { + xpub, err = xpub.Derive(child) + require.NoError(t, err) + } + return xpub +} + +func TestSimulatorBTCSignMessage(t *testing.T) { + testInitializedSimulators(t, func(t *testing.T, device *Device) { + t.Helper() + coin := messages.BTCCoin_BTC + accountKeypath := []uint32{49 + hardenedKeyStart, 0 + hardenedKeyStart, 0 + hardenedKeyStart} + + xpubStr, err := device.BTCXPub(coin, accountKeypath, messages.BTCPubRequest_XPUB, false) + require.NoError(t, err) + + xpub := parseXPub(t, xpubStr, 0, 10) + pubKey, err := xpub.ECPubKey() + require.NoError(t, err) + + sig, _, _, err := device.BTCSignMessage( + coin, + &messages.BTCScriptConfigWithKeypath{ + ScriptConfig: NewBTCScriptConfigSimple(messages.BTCScriptConfig_P2WPKH_P2SH), + Keypath: append(accountKeypath, 0, 10), + }, + []byte("message"), + ) + require.NoError(t, err) + sigHash := chainhash.DoubleHashB([]byte("\x18Bitcoin Signed Message:\n\x07message")) + require.True(t, parseECDSASignature(t, sig).Verify(sigHash, pubKey)) + }) +} + +func TestSimulatorBTCXPub(t *testing.T) { testConfigurations(t, func(t *testing.T, env *testEnv) { t.Helper() expected := "mocked-xpub" @@ -110,7 +197,7 @@ func TestBTCXPub(t *testing.T) { }) } -func TestBTCAddress(t *testing.T) { +func TestSimulatorBTCAddress(t *testing.T) { testConfigurations(t, func(t *testing.T, env *testEnv) { t.Helper() expected := "mocked-address" diff --git a/api/firmware/cardano_test.go b/api/firmware/cardano_test.go new file mode 100644 index 0000000..2c82bf5 --- /dev/null +++ b/api/firmware/cardano_test.go @@ -0,0 +1,73 @@ +// Copyright 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. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package firmware + +import ( + "encoding/hex" + "testing" + + "github.com/BitBoxSwiss/bitbox02-api-go/api/firmware/messages" + "github.com/stretchr/testify/require" +) + +func TestSimulatorCardanoXPubs(t *testing.T) { + testInitializedSimulators(t, func(t *testing.T, device *Device) { + t.Helper() + xpubs, err := device.CardanoXPubs( + [][]uint32{ + {1852 + hardenedKeyStart, 1815 + hardenedKeyStart, hardenedKeyStart}, + {1852 + hardenedKeyStart, 1815 + hardenedKeyStart, hardenedKeyStart + 1}, + }, + ) + require.NoError(t, err) + require.Len(t, xpubs, 2) + require.Equal(t, + "9fc9550e8379cb97c2d2557d89574207c6cf4d4ff62b37e377f2b3b3c284935b677f0fe5a4a6928c7b982c0c149f140c26c0930b73c2fe16feddfa21625e0316", + hex.EncodeToString(xpubs[0]), + ) + require.Equal(t, + "7ffd0bd7d54f1648ac59a357d3eb27b878c2f7c09739d3b7c7e6662d496dea16f10ef525258833d37db047cd530bf373ebcb283495aa4c768424a2af37cee661", + hex.EncodeToString(xpubs[1]), + ) + }) +} + +func TestSimulatorCardanoAddress(t *testing.T) { + testInitializedSimulators(t, func(t *testing.T, device *Device) { + t.Helper() + const account = uint32(1) + const rolePayment = uint32(0) // receive + const roleStake = uint32(2) // stake role must be 2 + const addressIdx = uint32(10) // address index + const stakeAddressIdx = uint32(0) // stake addr idx must be 0 + address, err := device.CardanoAddress( + messages.CardanoNetwork_CardanoMainnet, + &messages.CardanoScriptConfig{ + Config: &messages.CardanoScriptConfig_PkhSkh_{ + PkhSkh: &messages.CardanoScriptConfig_PkhSkh{ + KeypathPayment: []uint32{1852 + hardenedKeyStart, 1815 + hardenedKeyStart, account + hardenedKeyStart, rolePayment, addressIdx}, + KeypathStake: []uint32{1852 + hardenedKeyStart, 1815 + hardenedKeyStart, account + hardenedKeyStart, roleStake, stakeAddressIdx}, + }, + }, + }, + false, + ) + require.NoError(t, err) + require.Equal(t, + "addr1qxdq2ez52f5gtva3m77xgf5x4a7ap78mal43e5hhszyqehaaddssj2eta30yv9chr0sf4gu0jw77gag2g464yq0c70gqks5cr4", + address, + ) + }) +} diff --git a/api/firmware/device_test.go b/api/firmware/device_test.go index 6d4d0f5..4d2a855 100644 --- a/api/firmware/device_test.go +++ b/api/firmware/device_test.go @@ -1,4 +1,5 @@ // Copyright 2018-2019 Shift Cryptosecurity AG +// Copyright 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. @@ -16,19 +17,202 @@ package firmware import ( "crypto/rand" + "crypto/sha256" + "encoding/hex" + "encoding/json" "errors" "fmt" + "io" + "net" + "net/http" + "net/url" + "os" + "os/exec" + "path" + "path/filepath" + "runtime" + "sync" "testing" + "time" "github.com/BitBoxSwiss/bitbox02-api-go/api/common" "github.com/BitBoxSwiss/bitbox02-api-go/api/firmware/messages" "github.com/BitBoxSwiss/bitbox02-api-go/api/firmware/mocks" + "github.com/BitBoxSwiss/bitbox02-api-go/communication/u2fhid" "github.com/BitBoxSwiss/bitbox02-api-go/util/semver" "github.com/flynn/noise" "github.com/stretchr/testify/require" "google.golang.org/protobuf/proto" ) +func runSimulator(filename string) (func() error, *Device, error) { + cmd := exec.Command(filename) + if err := cmd.Start(); err != nil { + return nil, nil, err + } + var conn net.Conn + var err error + for i := 0; i < 200; i++ { + conn, err = net.Dial("tcp", "localhost:15423") + if err == nil { + break + } + time.Sleep(10 * time.Millisecond) + } + if err != nil { + return nil, nil, err + } + const bitboxCMD = 0x80 + 0x40 + 0x01 + + communication := u2fhid.NewCommunication(conn, bitboxCMD) + device := NewDevice(nil, nil, + &mocks.Config{}, communication, &mocks.Logger{}, + ) + return func() error { + if err := conn.Close(); err != nil { + return err + } + return cmd.Process.Kill() + }, device, nil +} + +// Download BitBox simulators based on testdata/simulators.json to testdata/simulators/*. +// Skips the download if the file already exists and has the corect hash. +func downloadSimulators() ([]string, error) { + type simulator struct { + URL string `json:"url"` + Sha256 string `json:"sha256"` + } + data, err := os.ReadFile("./testdata/simulators.json") + if err != nil { + return nil, err + } + var simulators []simulator + if err := json.Unmarshal(data, &simulators); err != nil { + return nil, err + } + + fileNotExistOrHashMismatch := func(filename, expectedHash string) (bool, error) { + file, err := os.Open(filename) + if os.IsNotExist(err) { + return true, nil + } + if err != nil { + return false, err + } + defer file.Close() + + hasher := sha256.New() + if _, err := io.Copy(hasher, file); err != nil { + return false, err + } + actualHash := hex.EncodeToString(hasher.Sum(nil)) + + return actualHash != expectedHash, nil + } + + downloadFile := func(url, filename string) error { + resp, err := http.Get(url) + if err != nil { + return err + } + defer resp.Body.Close() + + if resp.StatusCode != http.StatusOK { + return fmt.Errorf("bad status: %s", resp.Status) + } + + // Create the file + out, err := os.Create(filename) + if err != nil { + return err + } + defer out.Close() + + _, err = io.Copy(out, resp.Body) + return err + } + filenames := []string{} + for _, simulator := range simulators { + simUrl, err := url.Parse(simulator.URL) + if err != nil { + return nil, err + } + filename := filepath.Join("testdata", "simulators", path.Base(simUrl.Path)) + if err := os.MkdirAll(filepath.Dir(filename), 0755); err != nil { + return nil, err + } + doDownload, err := fileNotExistOrHashMismatch(filename, simulator.Sha256) + if err != nil { + return nil, err + } + if doDownload { + if err := downloadFile(simulator.URL, filename); err != nil { + return nil, err + } + if err := os.Chmod(filename, 0755); err != nil { + return nil, err + } + } + filenames = append(filenames, filename) + } + return filenames, nil +} + +var downloadSimulatorsOnce = sync.OnceValues(downloadSimulators) + +// Runs tests against a simulator which is not initialized (not paired, not seeded). +func testSimulators(t *testing.T, run func(*testing.T, *Device)) { + t.Helper() + if runtime.GOOS != "linux" || runtime.GOARCH != "amd64" { + t.Skip("Skipping simulator tests: not running on linux-amd64") + } + + simulatorFilenames, err := downloadSimulatorsOnce() + require.NoError(t, err) + + for _, simulatorFilename := range simulatorFilenames { + t.Run(filepath.Base(simulatorFilename), func(t *testing.T) { + teardown, device, err := runSimulator(simulatorFilename) + require.NoError(t, err) + defer func() { require.NoError(t, teardown()) }() + run(t, device) + }) + } +} + +// Runs tests against a simulator which is not initialized, but paired (not seeded). +func testSimulatorsAfterPairing(t *testing.T, run func(*testing.T, *Device)) { + t.Helper() + testSimulators(t, func(t *testing.T, device *Device) { + t.Helper() + require.NoError(t, device.Init()) + device.ChannelHashVerify(true) + run(t, device) + }) +} + +// Runs tests againt a simulator that is seeded with this mnemonic: boring mistake dish oyster truth +// pigeon viable emerge sort crash wire portion cannon couple enact box walk height pull today solid +// off enable tide +func testInitializedSimulators(t *testing.T, run func(*testing.T, *Device)) { + t.Helper() + testSimulatorsAfterPairing(t, func(t *testing.T, device *Device) { + t.Helper() + require.NoError(t, device.RestoreFromMnemonic()) + run(t, device) + }) +} + +func TestSimulatorRootFingerprint(t *testing.T) { + testInitializedSimulators(t, func(t *testing.T, device *Device) { + t.Helper() + fp, err := device.RootFingerprint() + require.NoError(t, err) + require.Equal(t, "4c00739d", hex.EncodeToString(fp)) + }) +} + // newDevice creates a device to test with, with init/pairing already processed. func newDevice( t *testing.T, @@ -296,6 +480,14 @@ func TestVersion(t *testing.T) { }) } +func TestSimulatorProduct(t *testing.T) { + testSimulators(t, func(t *testing.T, device *Device) { + t.Helper() + require.NoError(t, device.Init()) + require.Equal(t, common.ProductBitBox02Multi, device.Product()) + }) +} + func TestProduct(t *testing.T) { testConfigurations(t, func(t *testing.T, env *testEnv) { t.Helper() diff --git a/api/firmware/eth.go b/api/firmware/eth.go index 9da9a81..96b1267 100644 --- a/api/firmware/eth.go +++ b/api/firmware/eth.go @@ -253,7 +253,7 @@ func (device *Device) ETHSignEIP1559( } // ETHSignMessage signs an Ethereum message. The provided msg will be prefixed with "\x19Ethereum -// message\n" + len(msg) in the hardware, e.g. "\x19Ethereum\n5hello" (yes, the len prefix is the +// Signed Message\n" + len(msg) in the hardware, e.g. "\x19Ethereum Signed dMessage\n5hello" (yes, the len prefix is the // ascii representation with no fixed size or delimiter, WTF). // 27 is added to the recID to denote an uncompressed pubkey. func (device *Device) ETHSignMessage( diff --git a/api/firmware/eth_test.go b/api/firmware/eth_test.go index 693c338..77ffdba 100644 --- a/api/firmware/eth_test.go +++ b/api/firmware/eth_test.go @@ -19,8 +19,15 @@ import ( "github.com/BitBoxSwiss/bitbox02-api-go/api/firmware/messages" "github.com/stretchr/testify/require" + "golang.org/x/crypto/sha3" ) +func hashKeccak(b []byte) []byte { + h := sha3.NewLegacyKeccak256() + h.Write(b) + return h.Sum(nil) +} + func parseTypeNoErr(t *testing.T, typ string, types map[string]interface{}) *messages.ETHSignTypedMessageRequest_MemberType { t.Helper() parsed, err := parseType(typ, types) @@ -29,7 +36,6 @@ func parseTypeNoErr(t *testing.T, typ string, types map[string]interface{}) *mes } func TestParseType(t *testing.T) { - require.Equal(t, &messages.ETHSignTypedMessageRequest_MemberType{ Type: messages.ETHSignTypedMessageRequest_STRING, @@ -238,3 +244,152 @@ func TestEncodeValue(t *testing.T) { require.NoError(t, err) require.Equal(t, []byte("\x00\x00\x03\xe8"), encoded) } + +func TestSimulatorETHPub(t *testing.T) { + testInitializedSimulators(t, func(t *testing.T, device *Device) { + t.Helper() + chainID := uint64(1) + xpub, err := device.ETHPub( + chainID, + []uint32{ + 44 + hardenedKeyStart, + 60 + hardenedKeyStart, + 0 + hardenedKeyStart, + 0, + }, + messages.ETHPubRequest_XPUB, + false, + nil, + ) + require.NoError(t, err) + require.Equal(t, + "xpub6F2rrkQ947NAvxGQdZPcw1fMHdnJMxXCPtGKWdmf1aaumRkaCoJF72yFYhKRmkbat27bhDy79FWndkS3skRNLgbsuuJKqBoFyUcrp5ZgmC3", + xpub, + ) + + address, err := device.ETHPub( + chainID, + []uint32{ + 44 + hardenedKeyStart, + 60 + hardenedKeyStart, + 0 + hardenedKeyStart, + 0, + 1, + }, + messages.ETHPubRequest_ADDRESS, + false, + nil, + ) + require.NoError(t, err) + require.Equal(t, + "0x6A2A567cB891DeF8eA8C215C85f93d2f0F844ceB", + address, + ) + }) +} + +func TestSimulatorETHSignMessage(t *testing.T) { + testInitializedSimulators(t, func(t *testing.T, device *Device) { + t.Helper() + chainID := uint64(1) + xpubStr, err := device.ETHPub( + chainID, + []uint32{ + 44 + hardenedKeyStart, + 60 + hardenedKeyStart, + 0 + hardenedKeyStart, + 0, + }, + messages.ETHPubRequest_XPUB, + false, + nil, + ) + require.NoError(t, err) + + xpub := parseXPub(t, xpubStr, 10) + pubKey, err := xpub.ECPubKey() + require.NoError(t, err) + + sig, err := device.ETHSignMessage( + chainID, + []uint32{ + 44 + hardenedKeyStart, + 60 + hardenedKeyStart, + 0 + hardenedKeyStart, + 0, + 10, + }, + []byte("message"), + ) + require.NoError(t, err) + + sigHash := hashKeccak([]byte("\x19Ethereum Signed Message:\n7message")) + require.True(t, parseECDSASignature(t, sig[:64]).Verify(sigHash, pubKey)) + }) +} + +func TestSimulatorETHSignTypedMessage(t *testing.T) { + testInitializedSimulators(t, func(t *testing.T, device *Device) { + t.Helper() + msg := []byte(` +{ + "types": { + "EIP712Domain": [ + { "name": "name", "type": "string" }, + { "name": "version", "type": "string" }, + { "name": "chainId", "type": "uint256" }, + { "name": "verifyingContract", "type": "address" } + ], + "Attachment": [ + { "name": "contents", "type": "string" } + ], + "Person": [ + { "name": "name", "type": "string" }, + { "name": "wallet", "type": "address" }, + { "name": "age", "type": "uint8" } + ], + "Mail": [ + { "name": "from", "type": "Person" }, + { "name": "to", "type": "Person" }, + { "name": "contents", "type": "string" }, + { "name": "attachments", "type": "Attachment[]" } + ] + }, + "primaryType": "Mail", + "domain": { + "name": "Ether Mail", + "version": "1", + "chainId": 1, + "verifyingContract": "0xCcCCccccCCCCcCCCCCCcCcCccCcCCCcCcccccccC" + }, + "message": { + "from": { + "name": "Cow", + "wallet": "0xCD2a3d9F938E13CD947Ec05AbC7FE734Df8DD826", + "age": 20 + }, + "to": { + "name": "Bob", + "wallet": "0xbBbBBBBbbBBBbbbBbbBbbbbBBbBbbbbBbBbbBBbB", + "age": "0x1e" + }, + "contents": "Hello, Bob!", + "attachments": [{ "contents": "attachment1" }, { "contents": "attachment2" }] + } +}`) + + sig, err := device.ETHSignTypedMessage( + 1, + []uint32{ + 44 + hardenedKeyStart, + 60 + hardenedKeyStart, + 0 + hardenedKeyStart, + 0, + 10, + }, + msg, + ) + require.NoError(t, err) + require.Len(t, sig, 65) + }) +} diff --git a/api/firmware/mnemonic_test.go b/api/firmware/mnemonic_test.go new file mode 100644 index 0000000..530a588 --- /dev/null +++ b/api/firmware/mnemonic_test.go @@ -0,0 +1,50 @@ +// Copyright 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. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package firmware + +import ( + "testing" + + "github.com/stretchr/testify/require" +) + +func TestSimulatorShowMnemonic(t *testing.T) { + testInitializedSimulators(t, func(t *testing.T, device *Device) { + t.Helper() + require.NoError(t, device.ShowMnemonic()) + }) +} + +func TestSimulatorSetMnemonicPassphraseEnabled(t *testing.T) { + testInitializedSimulators(t, func(t *testing.T, device *Device) { + t.Helper() + info, err := device.DeviceInfo() + require.NoError(t, err) + require.False(t, info.MnemonicPassphraseEnabled) + + require.NoError(t, device.SetMnemonicPassphraseEnabled(true)) + + info, err = device.DeviceInfo() + require.NoError(t, err) + require.True(t, info.MnemonicPassphraseEnabled) + + require.NoError(t, device.SetMnemonicPassphraseEnabled(false)) + + info, err = device.DeviceInfo() + require.NoError(t, err) + require.False(t, info.MnemonicPassphraseEnabled) + }) + +} diff --git a/api/firmware/sdcard_test.go b/api/firmware/sdcard_test.go new file mode 100644 index 0000000..c8ae1f9 --- /dev/null +++ b/api/firmware/sdcard_test.go @@ -0,0 +1,42 @@ +// Copyright 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. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package firmware + +import ( + "testing" + + "github.com/BitBoxSwiss/bitbox02-api-go/api/firmware/messages" + "github.com/stretchr/testify/require" +) + +func TestSimulatorCheckSDCard(t *testing.T) { + testSimulatorsAfterPairing(t, func(t *testing.T, device *Device) { + t.Helper() + inserted, err := device.CheckSDCard() + require.NoError(t, err) + // Simulator always returns true. + require.True(t, inserted) + }) +} + +func TestSimutorInsertRemoveSDCard(t *testing.T) { + testSimulatorsAfterPairing(t, func(t *testing.T, device *Device) { + t.Helper() + require.NoError(t, + device.InsertRemoveSDCard(messages.InsertRemoveSDCardRequest_INSERT_CARD)) + require.NoError(t, + device.InsertRemoveSDCard(messages.InsertRemoveSDCardRequest_REMOVE_CARD)) + }) +} diff --git a/api/firmware/system_test.go b/api/firmware/system_test.go new file mode 100644 index 0000000..416fb60 --- /dev/null +++ b/api/firmware/system_test.go @@ -0,0 +1,52 @@ +// Copyright 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. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package firmware + +import ( + "fmt" + "testing" + + "github.com/stretchr/testify/require" +) + +func TestSimulatorDeviceName(t *testing.T) { + testSimulatorsAfterPairing(t, func(t *testing.T, device *Device) { + t.Helper() + info, err := device.DeviceInfo() + require.NoError(t, err) + require.Equal(t, "My BitBox", info.Name) + + // Name too long. + require.Error(t, device.SetDeviceName( + "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa")) + + require.NoError(t, device.SetDeviceName("new name")) + info, err = device.DeviceInfo() + require.NoError(t, err) + require.Equal(t, "new name", info.Name) + }) +} + +func TestSimulatorSetPassword(t *testing.T) { + for _, seedLen := range []int{16, 32} { + t.Run(fmt.Sprintf("seedLen=%d", seedLen), func(t *testing.T) { + testSimulatorsAfterPairing(t, func(t *testing.T, device *Device) { + t.Helper() + require.NoError(t, device.SetPassword(seedLen)) + require.Equal(t, StatusSeeded, device.Status()) + }) + }) + } +} diff --git a/api/firmware/testdata/.gitignore b/api/firmware/testdata/.gitignore new file mode 100644 index 0000000..c677f0d --- /dev/null +++ b/api/firmware/testdata/.gitignore @@ -0,0 +1 @@ +/simulators/ diff --git a/api/firmware/testdata/simulators.json b/api/firmware/testdata/simulators.json new file mode 100644 index 0000000..bc8c72f --- /dev/null +++ b/api/firmware/testdata/simulators.json @@ -0,0 +1,6 @@ +[ + { + "url": "https://github.com/BitBoxSwiss/bitbox02-firmware/releases/download/firmware%2Fv9.19.0/bitbox02-multi-v9.19.0-simulator1.0.0-linux-amd64", + "sha256": "e28be3fd6c7777624ad2574546ba125b7f134f095fa951acc8fb7295f3d33931" + } +] diff --git a/communication/usart/usart_internal_test.go b/communication/usart/usart_internal_test.go index 7963462..b5a50b9 100644 --- a/communication/usart/usart_internal_test.go +++ b/communication/usart/usart_internal_test.go @@ -16,14 +16,14 @@ package usart import ( "encoding/hex" - "io/ioutil" + "os" "testing" "github.com/stretchr/testify/require" ) func TestChecksum(t *testing.T) { - data, err := ioutil.ReadFile("testdata/chunk.bin") + data, err := os.ReadFile("testdata/chunk.bin") require.NoError(t, err) require.Equal(t, "4700", hex.EncodeToString(computeChecksum(data))) } diff --git a/go.mod b/go.mod index 6bdfe43..1fcd163 100644 --- a/go.mod +++ b/go.mod @@ -1,6 +1,6 @@ module github.com/BitBoxSwiss/bitbox02-api-go -go 1.13 +go 1.21 require ( github.com/benma/miniscript-go v0.0.0-20240226152043-f7c34099edf9 @@ -8,11 +8,20 @@ require ( github.com/btcsuite/btcd/btcec/v2 v2.3.2 github.com/btcsuite/btcd/btcutil v1.1.5 github.com/btcsuite/btcd/chaincfg/chainhash v1.1.0 - github.com/decred/dcrd/dcrec/secp256k1/v4 v4.2.0 // indirect github.com/flynn/noise v1.1.0 github.com/karalabe/usb v0.0.0-20191104083709-911d15fe12a9 github.com/pkg/errors v0.8.1 github.com/stretchr/testify v1.8.4 - golang.org/x/crypto v0.19.0 // indirect + golang.org/x/crypto v0.19.0 google.golang.org/protobuf v1.32.0 ) + +require ( + github.com/btcsuite/btclog v0.0.0-20170628155309-84c8d2346e9f // indirect + github.com/davecgh/go-spew v1.1.1 // indirect + github.com/decred/dcrd/crypto/blake256 v1.0.1 // indirect + github.com/decred/dcrd/dcrec/secp256k1/v4 v4.2.0 // indirect + github.com/pmezard/go-difflib v1.0.0 // indirect + golang.org/x/sys v0.17.0 // indirect + gopkg.in/yaml.v3 v3.0.1 // indirect +) diff --git a/go.sum b/go.sum index 64fef92..13f24c1 100644 --- a/go.sum +++ b/go.sum @@ -36,7 +36,6 @@ github.com/decred/dcrd/crypto/blake256 v1.0.0/go.mod h1:sQl2p6Y26YV+ZOcSTP6thNdn github.com/decred/dcrd/crypto/blake256 v1.0.1 h1:7PltbUIQB7u/FfZ39+DGa/ShuMyJ5ilcvdfma9wOH6Y= github.com/decred/dcrd/crypto/blake256 v1.0.1/go.mod h1:2OfgNZ5wDpcsFmHmCK5gZTPcCXqlm2ArzUIkw9czNJo= github.com/decred/dcrd/dcrec/secp256k1/v4 v4.0.1/go.mod h1:hyedUtir6IdtD/7lIxGeCxkaw7y45JueMRL4DIyJDKs= -github.com/decred/dcrd/dcrec/secp256k1/v4 v4.1.0/go.mod h1:DZGJHZMqrU4JJqFAWUS2UO1+lbSKsdiOoYi9Zzey7Fc= github.com/decred/dcrd/dcrec/secp256k1/v4 v4.2.0 h1:8UrgZ3GkP4i/CLijOJx79Yu+etlyjdBU4sfcs2WYQMs= github.com/decred/dcrd/dcrec/secp256k1/v4 v4.2.0/go.mod h1:v57UDF4pDQJcEfFUCRop3lJL149eHGSe9Jvczhzjo/0= github.com/decred/dcrd/lru v1.0.0/go.mod h1:mxKOwFd7lFjN2GZYsiz/ecgqR6kkYAl+0pz0tEMk218= @@ -51,7 +50,6 @@ github.com/golang/protobuf v1.4.0-rc.2/go.mod h1:LlEzMj4AhA7rCAGe4KMBDvJI+AwstrU github.com/golang/protobuf v1.4.0-rc.4.0.20200313231945-b860323f09d0/go.mod h1:WU3c8KckQ9AFe+yFwt9sWVRKCVIyN9cPHBJSNnbL67w= github.com/golang/protobuf v1.4.0/go.mod h1:jodUvKwWbYaEsadDk5Fwe5c77LiNKVO9IDvqG2KuDX0= github.com/golang/protobuf v1.4.2/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI= -github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk= github.com/golang/snappy v0.0.4/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= @@ -85,40 +83,23 @@ github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINE github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= -github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw= -github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo= github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= -github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= -github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU= github.com/stretchr/testify v1.8.4 h1:CcVxjf3Q8PM0mHUKJCdn+eZZtm5yQwehR5yeSVQQcUk= github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo= github.com/syndtr/goleveldb v1.0.1-0.20210819022825-2ae1ddf74ef7/go.mod h1:q4W45IWZaF22tdD+VEXcAWRA037jwmWEB5VWYORlTpc= -github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY= golang.org/x/crypto v0.0.0-20170930174604-9419663f5a44/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/crypto v0.0.0-20210322153248-0c34fe9e7dc2/go.mod h1:T9bdIzuCu7OtxOm1hfPfRQxPLYneinmdGuTeoZ9dtd4= -golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= -golang.org/x/crypto v0.5.0/go.mod h1:NK/OQwhpMQP3MwtdjgLlYHnH9ebylxKWv3e0fK+mkQU= golang.org/x/crypto v0.19.0 h1:ENy+Az/9Y1vSrlrvBSyna3PITt4tiZLf7sgCjZBX7Wo= golang.org/x/crypto v0.19.0/go.mod h1:Iy9bg/ha4yyC70EfRS8jz+B6ybOBKMaSxLj6P6oBDfU= -golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4= -golang.org/x/mod v0.8.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs= golang.org/x/net v0.0.0-20180719180050-a680a1efc54d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= -golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20200520004742-59133d7f0dd7/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= golang.org/x/net v0.0.0-20200813134508-3edf25e44fcc/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= -golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c= -golang.org/x/net v0.5.0/go.mod h1:DivGGAXEgPSlEBzxGzZI+ZLohi+xUj054jfeKui00ws= -golang.org/x/net v0.6.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs= -golang.org/x/net v0.10.0/go.mod h1:0qNGK6F8kojg2nk9dLZ2mShWaEBan6FAoqfSigmmuDg= golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= @@ -129,33 +110,13 @@ golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7w golang.org/x/sys v0.0.0-20200519105757-fe76b779f299/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200814200057-3d37ad5750ed/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.4.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.8.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.17.0 h1:25cE3gD+tdBA7lp7QfhuV+rJiE9YXTcS3VG1SqssI/Y= golang.org/x/sys v0.17.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= -golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= -golang.org/x/term v0.4.0/go.mod h1:9P2UbLfCdcvo3p/nzKvsmas4TnlujnuoV9hGgYzW1lQ= -golang.org/x/term v0.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k= -golang.org/x/term v0.8.0/go.mod h1:xPskH00ivmX89bAKVGSKKtLOWNx2+17Eiy94tnKShWo= -golang.org/x/term v0.17.0/go.mod h1:lLRBjIVuehSbZlaOtGMbcMncT+aqLLLmKrsjNrUguwk= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk= golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= -golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ= -golang.org/x/text v0.6.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= -golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= -golang.org/x/text v0.9.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8= -golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU= golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= -golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= -golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc= -golang.org/x/tools v0.6.0/go.mod h1:Xwgl3UAJ/d3gWutnCtw505GrjyAbvKui8lOU390QaIU= -golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1 h1:go1bK/D/BFZV2I8cIQd1NKEZ+0owSTG1fDTci4IqFcE= golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= @@ -165,7 +126,6 @@ google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQ google.golang.org/protobuf v1.20.1-0.20200309200217-e05f789c0967/go.mod h1:A+miEFZTKqfCUM6K7xSMQL9OKL/b6hQv+e19PK+JZNE= google.golang.org/protobuf v1.21.0/go.mod h1:47Nbq4nVaFHyn7ilMalzfO3qCViNmqZ2kzikPIcrTAo= google.golang.org/protobuf v1.23.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= -google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw= google.golang.org/protobuf v1.32.0 h1:pPC6BG5ex8PDFnkbrGU3EixyhKcQ2aDuBS36lqK/C7I= google.golang.org/protobuf v1.32.0/go.mod h1:c6P6GXX6sHbq/GpV6MGZEdwhWPcYBgnhAHhKbcUYpos= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=