Skip to content

Commit 796a86f

Browse files
committed
Merge branch 'firmwarehash'
2 parents c9753ac + c5e4f77 commit 796a86f

File tree

3 files changed

+129
-21
lines changed

3 files changed

+129
-21
lines changed

api/bootloader/device.go

Lines changed: 6 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,6 @@ package bootloader
1717

1818
import (
1919
"bytes"
20-
"crypto/sha256"
2120
"encoding/binary"
2221
"io"
2322
"math"
@@ -234,18 +233,12 @@ func (device *Device) flashUnsignedFirmware(firmware []byte, progressCallback fu
234233
// format is invalid, or the firmware magic does not match the expected magic according to the
235234
// device product.
236235
func (device *Device) parseSignedFirmware(firmware []byte) ([]byte, []byte, error) {
237-
if len(firmware) <= magicLen+sigDataLen {
238-
return nil, nil, errp.New("firmware too small")
239-
}
240-
magic, firmware := firmware[:magicLen], firmware[magicLen:]
241-
sigData, firmware := firmware[:sigDataLen], firmware[sigDataLen:]
242-
243-
expectedMagic, ok := sigDataMagic[device.product]
244-
if !ok {
245-
return nil, nil, errp.New("unrecognized product")
236+
product, sigData, firmware, err := ParseSignedFirmware(firmware)
237+
if err != nil {
238+
return nil, nil, err
246239
}
247-
if binary.BigEndian.Uint32(magic) != expectedMagic {
248-
return nil, nil, errp.New("invalid signing pubkeys data magic")
240+
if product != device.product {
241+
return nil, nil, errp.New("signed firmware binary does not match device product")
249242
}
250243
return sigData, firmware, nil
251244
}
@@ -309,17 +302,9 @@ func (device *Device) Erased() (bool, error) {
309302
if err != nil {
310303
return false, err
311304
}
312-
firmwareVersionLE := make([]byte, 4)
313-
binary.LittleEndian.PutUint32(firmwareVersionLE, firmwareVersion)
314305

315-
emptyFirmware := bytes.Repeat([]byte{0xFF}, maxFirmwareSize)
306+
emptyFirmwareHash := HashFirmware(firmwareVersion, []byte{})
316307

317-
doubleHash := func(b []byte) []byte {
318-
first := sha256.Sum256(b)
319-
second := sha256.Sum256(first[:])
320-
return second[:]
321-
}
322-
emptyFirmwareHash := doubleHash(append(firmwareVersionLE, emptyFirmware...))
323308
firmwareHash, _, err := device.GetHashes(false, false)
324309
if err != nil {
325310
return false, err

api/bootloader/util.go

Lines changed: 65 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,65 @@
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 bootloader
16+
17+
import (
18+
"bytes"
19+
"crypto/sha256"
20+
"encoding/binary"
21+
22+
"github.com/digitalbitbox/bitbox02-api-go/api/common"
23+
"github.com/digitalbitbox/bitbox02-api-go/util/errp"
24+
)
25+
26+
// HashFirmware returns the hash of `<firmware version><firmware>`, as computed by the bootloader to
27+
// check the firmware signature.
28+
func HashFirmware(firmwareVersion uint32, unsignedFirmware []byte) []byte {
29+
doubleHash := func(b []byte) []byte {
30+
first := sha256.Sum256(b)
31+
second := sha256.Sum256(first[:])
32+
return second[:]
33+
}
34+
firmwareVersionLE := make([]byte, 4)
35+
binary.LittleEndian.PutUint32(firmwareVersionLE, firmwareVersion)
36+
37+
padded := bytes.Repeat([]byte{0xFF}, maxFirmwareSize)
38+
copy(padded, unsignedFirmware)
39+
return doubleHash(append(firmwareVersionLE, padded...))
40+
}
41+
42+
// ParseSignedFirmware parses a signed firmware file and returns (sigdata, firmware). Errors if the
43+
// format is invalid, or the firmware magic does not match the expected magic according to the
44+
// device product.
45+
func ParseSignedFirmware(firmware []byte) (common.Product, []byte, []byte, error) {
46+
if len(firmware) <= magicLen+sigDataLen {
47+
return "", nil, nil, errp.New("firmware too small")
48+
}
49+
magic, firmware := firmware[:magicLen], firmware[magicLen:]
50+
sigData, firmware := firmware[:sigDataLen], firmware[sigDataLen:]
51+
52+
var product common.Product
53+
magicInt := binary.BigEndian.Uint32(magic)
54+
for p, productMagic := range sigDataMagic {
55+
if magicInt == productMagic {
56+
product = p
57+
break
58+
}
59+
}
60+
if product == "" {
61+
return "", nil, nil, errp.Newf("unrecognized magic")
62+
}
63+
64+
return product, sigData, firmware, nil
65+
}

api/bootloader/util_test.go

Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,58 @@
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 bootloader
16+
17+
import (
18+
"encoding/hex"
19+
"io/ioutil"
20+
"testing"
21+
22+
"github.com/digitalbitbox/bitbox02-api-go/api/common"
23+
"github.com/stretchr/testify/require"
24+
)
25+
26+
func TestHashFirmware(t *testing.T) {
27+
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")
28+
require.Equal(t, emptyHash, HashFirmware(5, []byte{}))
29+
30+
unsignedFirmware, err := ioutil.ReadFile("testdata/firmware-btc.v4.2.2.bin")
31+
require.NoError(t, err)
32+
require.Equal(t,
33+
[]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"),
34+
HashFirmware(7, unsignedFirmware),
35+
)
36+
}
37+
38+
func TestParseSignedFirmmare(t *testing.T) {
39+
unsignedFirmware, err := ioutil.ReadFile("testdata/firmware-btc.v4.2.2.bin")
40+
require.NoError(t, err)
41+
42+
signedFirmware, err := ioutil.ReadFile("testdata/firmware-btc.v4.2.2.signed.bin")
43+
require.NoError(t, err)
44+
45+
product, sigData, firmware, err := ParseSignedFirmware(signedFirmware)
46+
require.NoError(t, err)
47+
48+
expectedSigData := "0000000027a8678099f9f52b142a0692b320d6053d3f7c637273a236654ce4e5346efaffb35034df24eca2e500bbd24b84ba79799d4d7ad5492516b5122587d41a63d9d7c2565124b98a5d9da8bfab7e566371c936b1435d7980d4c09bc31b84431c2e3c6b62829093f478be5356657aa525d6a5fc793acd2641f9bd2d3587dea6a33ad7c6789655ce072bf02908b5d795a87b6789cac63e98bf7849d740d47fb62f3fef88c6db3cb260c53302eb133a89b3529e2f8ae20e99ed0fe3d32cd30db880ffbc47be63edb71c681a3a0d45716746db7704c915d617fcf1c895ca949bb3adcc9a666c73dd373cdf9d4ccf9ff102bde32307f29ecdbf981b3553af7ba3509ff565000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000003d8054281b0f6733469f58e0406cba24fefcea8704cd6e8d990bd98fa33d2b0a0942dcafc81f912216ca86cab2000b6de96f1567d5209ab6167278dc585b011d070000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000017a44e602e19792e468a110b997f74b4149a4aca55e98a8b94f219739886c0227f6267ff582f2c1293f71f5afcb5ba6065ebeb454aa142f389f2bb91da62e4281f557a14e9974a3df39c9451b88766c4de7d1fcb8173fcdef82e316e8a8fd4822947a103aeee373e7c687228fadbd5b7ae3032886da057d53338abd889bff301"
49+
require.Equal(t, expectedSigData, hex.EncodeToString(sigData))
50+
require.Equal(t, common.ProductBitBox02BTCOnly, product)
51+
require.Equal(t, unsignedFirmware, firmware)
52+
53+
// Test invalid magic
54+
signedFirmware[0] = 0
55+
_, _, _, err = ParseSignedFirmware(signedFirmware)
56+
require.Error(t, err)
57+
58+
}

0 commit comments

Comments
 (0)