From 994c9b756004c49f6e7e4738b5677dbb2cb30562 Mon Sep 17 00:00:00 2001 From: Marko Bencun Date: Wed, 3 Jul 2024 11:57:51 +0200 Subject: [PATCH 1/4] go: update to Go 1.21 So we can use sync.OnceValues --- api/bootloader/device_test.go | 8 ++--- api/bootloader/util_test.go | 8 ++--- communication/usart/usart_internal_test.go | 4 +-- go.mod | 15 ++++++-- go.sum | 40 ---------------------- 5 files changed, 22 insertions(+), 53 deletions(-) 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/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= From 2fc73dd8250378d03cfbced469ae79d1c92c4ec2 Mon Sep 17 00:00:00 2001 From: Marko Bencun Date: Mon, 3 Jun 2024 12:25:06 +0200 Subject: [PATCH 2/4] firmware/tests: test against bitbox02 simulators The simulators are downloaded base on the simulators.json file if the file does not exist (or the hash is wrong). This allows us to exercise the library much more easily without needing to mock everything, as the simulator behaves like the real BitBox02 for the most part. We could automatically download all releases by using the GitHub releases API (https://api.github.com/repos/BitBoxSwiss/bitbox02-firmware/releases), but the downside is that there would be network requests each time you run the unit tests. For now we simply keep the list manually. The simulators are only released for linux-amd64 for now, so we skip tests if we are not running in this environment. --- .github/workflows/ci.yml | 2 +- api/firmware/backup_test.go | 59 ++++++++ api/firmware/bip85_test.go | 38 +++++ api/firmware/btc_test.go | 91 +++++++++++- api/firmware/cardano_test.go | 73 ++++++++++ api/firmware/device_test.go | 192 ++++++++++++++++++++++++++ api/firmware/eth.go | 2 +- api/firmware/eth_test.go | 157 ++++++++++++++++++++- api/firmware/mnemonic_test.go | 50 +++++++ api/firmware/sdcard_test.go | 42 ++++++ api/firmware/system_test.go | 52 +++++++ api/firmware/testdata/.gitignore | 1 + api/firmware/testdata/simulators.json | 6 + 13 files changed, 760 insertions(+), 5 deletions(-) create mode 100644 api/firmware/backup_test.go create mode 100644 api/firmware/bip85_test.go create mode 100644 api/firmware/cardano_test.go create mode 100644 api/firmware/mnemonic_test.go create mode 100644 api/firmware/sdcard_test.go create mode 100644 api/firmware/system_test.go create mode 100644 api/firmware/testdata/.gitignore create mode 100644 api/firmware/testdata/simulators.json diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index f7e7594..70d2539 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -54,7 +54,7 @@ jobs: - name: Test run: | go version - go test ./... + go test ./... -v build: runs-on: ubuntu-22.04 steps: 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" + } +] From a6fa5be220bead511a7cc3164ba2b24ffcb8d05c Mon Sep 17 00:00:00 2001 From: Marko Bencun Date: Wed, 3 Jul 2024 12:14:22 +0200 Subject: [PATCH 3/4] .github: bump actions to v4 v3 being deprecated. --- .github/workflows/ci.yml | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 70d2539..d2c051d 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 @@ -48,7 +48,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: Test @@ -59,9 +59,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 @@ -70,7 +70,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 From c71efc6001186dd6ebb75a38e0bf7637aa2715f5 Mon Sep 17 00:00:00 2001 From: Marko Bencun Date: Wed, 3 Jul 2024 12:33:08 +0200 Subject: [PATCH 4/4] .github/workflows: enable simulators cache --- .github/workflows/ci.yml | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index d2c051d..c10f241 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -47,6 +47,12 @@ 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@v4 with: