Skip to content

Commit 24396f9

Browse files
authoredNov 30, 2020
Flesh out armory transparency story (#169)
1 parent b6d5472 commit 24396f9

File tree

10 files changed

+196
-36
lines changed

10 files changed

+196
-36
lines changed
 
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,89 @@
1+
# Copyright 2020 Google LLC. All Rights Reserved.
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+
# build.sh is a utility to create ext4 filesystem images for use in the
16+
# firmware partition of SD Cards created for USB Armory devices.
17+
18+
#!/bin/bash
19+
usage() {
20+
echo "Usage: $0 -u <path_to_unikernel> -o <output_file>" 1>&2
21+
exit 1
22+
}
23+
24+
clean() {
25+
if [ ! -z "${MNT}" ]; then
26+
fusermount -u ${MNT}
27+
rmdir ${MNT}
28+
fi
29+
}
30+
31+
32+
while getopts ":u:o:f" opt; do
33+
case ${opt} in
34+
u )
35+
u=${OPTARG}
36+
;;
37+
o )
38+
o=${OPTARG}
39+
;;
40+
f )
41+
f=1
42+
;;
43+
* )
44+
usage
45+
;;
46+
esac
47+
done
48+
shift $((OPTIND - 1))
49+
50+
if [ -z "${u}" ] || [ -z "${o}" ]; then
51+
usage
52+
fi
53+
54+
if [ -f "${o}" ] && [ -z "${f}" ]; then
55+
echo "Output file ${o} already exists, use -f to force overwrite"
56+
if [ -z ${f} ]; then
57+
exit 2
58+
fi
59+
fi
60+
61+
trap clean EXIT
62+
63+
set -e
64+
MNT=$(mktemp --directory /tmp/build_image-XXXXX)
65+
66+
SIZE=$(stat --format='%s' ${u})
67+
OVERHEAD=2560000
68+
let BLOCKS=(${SIZE}+${OVERHEAD})/1024
69+
70+
mkfs.ext4 ${o} -q -b 1024 -L firmware ${BLOCKS}
71+
fuse2fs -o fakeroot ${o} ${MNT}
72+
73+
mkdir ${MNT}/boot
74+
cp ${u} ${MNT}/boot/unikernel
75+
h=$(sha256sum ${u} | awk '{print $1}')
76+
sed -- "s/@HASH@/${h}/" > ${MNT}/boot/armory-boot.conf <<EOF
77+
{
78+
"unikernel": [
79+
"/boot/unikernel",
80+
"@HASH@"
81+
]
82+
}
83+
EOF
84+
85+
fusermount -u ${MNT}
86+
unset MNT
87+
88+
echo "Created image in ${o}:"
89+
ls -lh ${o}

‎binary_transparency/firmware/devices/usb_armory/README.md

+67-12
Original file line numberDiff line numberDiff line change
@@ -39,8 +39,8 @@ The on-disk partition layout will be:
3939
index | name | size | format | notes
4040
------|------------|---------|--------|-----------------------------------------------
4141
1 | boot | 10M | raw | Must cover disk bytes 1024 onwards as we'll directly write the bootloader here.
42-
2 | proof | 512KB | ext4 | EXT4 filesytem for storing a serialised proof bundle
43-
3 | firmware | 64MB+ | ext4 | EXT4 filesystem containing the bootable firmware image, armory boot config, and DTD file.
42+
2 | proof | 512KB | ext4 | EXT4 filesystem for storing a serialised proof bundle
43+
3 | firmware | 64MB+ | ext4 | EXT4 filesystem containing the bootable firmware image, armory boot config, etc.
4444

4545
### Preparing the SD Card
4646

@@ -80,7 +80,7 @@ Model: Generic- Micro SD/M2 (scsi)
8080
Disk /dev/sdc: 15931539456B
8181
Sector size (logical/physical): 512B/512B
8282
Partition Table: msdos
83-
Disk Flags:
83+
Disk Flags:
8484

8585
Number Start End Size Type File system Flags
8686
1 512B 10240511B 10240000B primary lba
@@ -108,9 +108,9 @@ To compile the bootloader itself, run the following command in the `bootloader`
108108
directory:
109109

110110
```bash
111-
# Note that START_KERNEL corresponds to the offset of the firmware partition,
111+
# Note that START_KERNEL corresponds to the offset of the firmware partition,
112112
# and START_PROOF is the offset of the proof partition
113-
make CROSS_COMPILE=arm-none-eabi- TARGET=usbarmory imx BOOT=uSD START_KERNEL=10753536 START_PROOF=10240512 LEN_KERNEL=89246720
113+
make CROSS_COMPILE=arm-none-eabi- TARGET=usbarmory imx BOOT=uSD START_KERNEL=10753536 START_PROOF=10240512 LEN_KERNEL=89246720
114114
```
115115

116116
If successful, this will create a few files - the one we're interested in is
@@ -130,7 +130,8 @@ $ sudo dd if=armory-boot.imx of=/dev/myscard bs=512 seek=2 conv=fsync,notrunc
130130
Firmware images
131131
---------------
132132

133-
Currently, the bootloader can only chain to a Linux kernel.
133+
Currently, the bootloader can only chain to either a Linux kernel, or a
134+
bare-metal ELF unikernel (only tested with tamago-example thus far).
134135

135136
There are some invariants which must hold for this chain to work:
136137
1. The `firmware` partition MUST be located at the precise offset mentioned
@@ -140,18 +141,45 @@ There are some invariants which must hold for this chain to work:
140141
following contents:
141142
* `armory-boot.conf` - a JSON file which tells the bootloader which files
142143
to load
143-
* a valid ARM linux Kernel image
144-
* a valid DTB file
145-
Note that the `armory-boot.conf` file also contains SHA256 hashes of the
146-
kernel and DTB files, and these MUST be correct.
144+
* Either:
145+
* to boot a Linux Kernel:
146+
* a valid ARM linux Kernel image
147+
* a valid DTB file
148+
* to boot ELF unikernel:
149+
* a valid ARM bare-metal ELF binary/unikernel
147150

151+
Note that the `armory-boot.conf` file also contains SHA256 hashes of
152+
all files referenced, and these MUST be correct.
153+
154+
To aid in the creation of valid firmware images, use the
155+
`[cmd/usb_armory/image_builder/build.sh](/binary_transparency/firmware/cmd/usb_armory/image_builder/build.sh)`
156+
script, e.g.:
157+
158+
```bash
159+
$ ./cmd/usb_armory/image_builder/build.sh -u ./testdata/firmware/usb_armory/example/tamago-example -o /tmp/armory.ext4
160+
161+
/tmp/armory.ext4: Writing to the journal is not supported.
162+
Created image in /tmp/armory.ext4:
163+
-rw-rw-r-- 1 al al 13M Nov 30 10:39 /tmp/armory.ext4
164+
165+
```
166+
167+
For now, this image can be written to the target partition using `dd`, e.g.:
168+
169+
```bash
170+
$ sudo dd if=/tmp/armory.ext of=/dev/my_sdcard3 bs=1M conf=fsync
171+
```
172+
173+
TODO(al): consider updating `flash_tool` with support for writing these images.
174+
175+
### Linux
148176

149177
> :frog: The [Armory Debian Base Image](https://github.com/f-secure-foundry/usbarmory-debian-base_image/releases)
150178
> is a good source for the kernel (zImage) and dtb files.
151179
>
152180
> You can decompress and mount the image to access the files like so:
153181
> ```bash
154-
> # decompress image
182+
> # decompress image
155183
> $ xz -d usbarmory-mark-two-usd-debian_buster-base_image-20200714.raw.xz
156184
> # mount image with loopback:
157185
> # note the offset parameter below - the raw file is a complete disk image, this
@@ -170,7 +198,7 @@ There are some invariants which must hold for this chain to work:
170198
> -rwxr-xr-x 1 root root 6726952 Oct 20 17:13 zImage-5.4.72-0-usbarmory
171199
> ```
172200
173-
An example `armory-boot.conf` file is:
201+
An example `armory-boot.conf` file configured to boot a Linux kernel is:
174202
175203
```json
176204
{
@@ -188,6 +216,24 @@ An example `armory-boot.conf` file is:
188216
189217
TODO(al): Consider wrapping this up into a script.
190218

219+
### ELF unikernel
220+
221+
> :frog: A good sample unikernel is the
222+
> [tamago-example](https://github.com/f-secure-foundry/tamago-example)
223+
> application.
224+
225+
An example `armory-boot.conf` file configured to boot an ELF unikernel is:
226+
227+
```json
228+
{
229+
"unikernel": [
230+
"/boot/tamago-example",
231+
"aceb3514d5ba6ac591a7d5f2cad680e83a9f848d19763563da8024f003e927c7"
232+
]
233+
}
234+
```
235+
236+
191237
Booting
192238
-------
193239

@@ -211,3 +257,12 @@ Dentry cache hash table entries: 65536 (order: 6, 262144 bytes, linear)
211257
...
212258
```
213259

260+
261+
Firmware Measurement
262+
--------------------
263+
264+
The 'firmware measurement' hash for the USB Armory is defined to be the SHA256
265+
hash of the raw bytes of the `ext4` **filesystem image** stored in the
266+
'firmware' partition of the SD Card.
267+
268+
Note that this _may well_ be a different size than the partition itself.

‎binary_transparency/firmware/devices/usb_armory/bootloader/Makefile

+1-1
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,7 @@ endif
2525
APP := armory-boot
2626
GOENV := GO_EXTLINK_ENABLED=0 CGO_ENABLED=0 GOOS=tamago GOARM=7 GOARCH=arm
2727
TEXT_START := 0x90010000 # ramStart (defined in imx6/imx6ul/memory.go) + 0x10000
28-
GOFLAGS := -tags ${BUILD_TAGS} -ldflags "-s -w -T $(TEXT_START) -E _rt0_arm_tamago -R 0x1000 -X 'main.Build=${BUILD}' -X 'main.Revision=${REV}' -X 'main.Boot=${BOOT}' -X 'main.StartKernel=${START_KERNEL}' -X 'main.StartProof=${START_PROOF}' -X 'main.LenKernel=${LEN_KERNEL}' -X 'main.PublicKeyStr=${PUBLIC_KEY}'"
28+
GOFLAGS := -tags ${BUILD_TAGS} -ldflags "-s -w -T $(TEXT_START) -E _rt0_arm_tamago -R 0x1000 -X 'main.Build=${BUILD}' -X 'main.Revision=${REV}' -X 'main.Boot=${BOOT}' -X 'main.StartKernel=${START_KERNEL}' -X 'main.StartProof=${START_PROOF}' -X 'main.PublicKeyStr=${PUBLIC_KEY}'"
2929

3030
.PHONY: clean
3131

‎binary_transparency/firmware/devices/usb_armory/bootloader/boot.go

-1
Original file line numberDiff line numberDiff line change
@@ -64,7 +64,6 @@ func bootKernel(kernel []byte, dtb []byte, cmdline string) {
6464
// This function implements a _very_ simple ELF loader which is suitable for
6565
// loading bare-metal ELF files like those produced by TamaGo.
6666
func bootELFUnikernel(img []byte) {
67-
dma.Init(dmaStart, dmaSize)
6867
mem, _ := dma.Reserve(dmaSize, 0)
6968

7069
f, err := elf.NewFile(bytes.NewReader(img))

‎binary_transparency/firmware/devices/usb_armory/bootloader/ext4.go

+18
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,24 @@ type Partition struct {
2828
_offset int64
2929
}
3030

31+
// GetExt4FilesystemSize returns the size in bytes of the ext4 filesystem stored
32+
// in the partition.
33+
// Note that this may not be the same as the size of the disk partition which
34+
// contains the filesystem.
35+
func (d *Partition) GetExt4FilesystemSize() (uint64, error) {
36+
_, err := d.Seek(ext4.Superblock0Offset, io.SeekStart)
37+
if err != nil {
38+
return 0, err
39+
}
40+
41+
sb, err := ext4.NewSuperblockWithReader(d)
42+
if err != nil {
43+
return 0, err
44+
}
45+
46+
return uint64(sb.BlockSize()) * sb.BlockCount(), nil
47+
}
48+
3149
func (d *Partition) getBlockGroupDescriptor(inode int) (bgd *ext4.BlockGroupDescriptor, err error) {
3250
_, err = d.Seek(ext4.Superblock0Offset, io.SeekStart)
3351

‎binary_transparency/firmware/devices/usb_armory/bootloader/ft.go

+15-9
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ import (
66
"encoding/json"
77
"fmt"
88
"io"
9+
"log"
910
"time"
1011

1112
"github.com/f-secure-foundry/tamago/soc/imx6/dcp"
@@ -33,14 +34,20 @@ func loadBundle(p *Partition) (api.ProofBundle, error) {
3334
return bundle, nil
3435
}
3536

36-
// hashPartition calculates the SHA256 of the first numBytes of the partition.
37-
func hashPartition(numBytes int64, p *Partition) ([]byte, error) {
38-
fmt.Println("Reading partition at offset %d...", p.Offset)
37+
// hashPartition calculates the SHA256 of the whole partition.
38+
func hashPartition(p *Partition) ([]byte, error) {
39+
log.Printf("Reading partition at offset %d...\n", p.Offset)
40+
numBytes, err := p.GetExtFilesystemSize()
41+
if err != nil {
42+
return nil, fmt.Errorf("failed to get partition size: %w", err)
43+
}
44+
log.Printf("Partition size %d bytes\n", numBytes)
45+
3946
if _, err := p.Seek(0, io.SeekStart); err != nil {
4047
return nil, fmt.Errorf("failed to seek: %w", err)
4148
}
4249

43-
bs := int64(1<<20)
50+
bs := uint64(1 << 16)
4451
rc := make(chan []byte, 10)
4552
hc := make(chan []byte)
4653

@@ -50,7 +57,7 @@ func hashPartition(numBytes int64, p *Partition) ([]byte, error) {
5057
if err != nil {
5158
panic(fmt.Sprintf("Failed to created hasher: %q", err))
5259
}
53-
for b := range(rc) {
60+
for b := range rc {
5461
if _, err := h.Write(b); err != nil {
5562
panic(fmt.Errorf("failed to hash: %w", err))
5663
}
@@ -72,14 +79,13 @@ func hashPartition(numBytes int64, p *Partition) ([]byte, error) {
7279
if err != nil {
7380
return nil, fmt.Errorf("failed to read: %w", err)
7481
}
75-
rc<-b[:bc]
82+
rc <- b[:bc]
7683

77-
numBytes -= int64(bc)
84+
numBytes -= uint64(bc)
7885
}
7986
close(rc)
8087

81-
fmt.Printf("Finished reading, hashing in %s\n", time.Now().Sub(start))
88+
log.Printf("Finished reading, hashing in %s\n", time.Now().Sub(start))
8289
hash := <-hc
8390
return hash[:], nil
8491
}
85-

‎binary_transparency/firmware/devices/usb_armory/bootloader/main.go

+3-12
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,6 @@ package main
1212

1313
import (
1414
"fmt"
15-
"log"
1615
"strconv"
1716

1817
"github.com/f-secure-foundry/tamago/board/f-secure/usbarmory/mark-two"
@@ -26,12 +25,9 @@ var Revision string
2625

2726
var Boot string
2827
var StartKernel string
29-
var LenKernel string
3028
var StartProof string
3129

3230
func init() {
33-
log.SetFlags(0)
34-
3531
if err := imx6.SetARMFreq(900); err != nil {
3632
panic(fmt.Sprintf("WARNING: error setting ARM frequency: %v\n", err))
3733
}
@@ -63,15 +59,10 @@ func main() {
6359
if err != nil {
6460
panic(fmt.Sprintf("invalid kernel partition start offset: %v\n", err))
6561
}
66-
kernelLen, err := strconv.ParseInt(LenKernel, 10, 64)
67-
if err != nil {
68-
panic(fmt.Sprintf("invalid kernel partition length : %v\n", err))
69-
}
7062
proofOffset, err := strconv.ParseInt(StartProof, 10, 64)
7163
if err != nil {
7264
panic(fmt.Sprintf("invalid proof partition start offset: %v\n", err))
7365
}
74-
7566
partition := &Partition{
7667
Card: card,
7768
Offset: kernelOffset,
@@ -80,7 +71,7 @@ func main() {
8071
panic(fmt.Sprintf("invalid configuration: %v\n", err))
8172
}
8273

83-
h, err := hashPartition(kernelLen, partition)
74+
h, err := hashPartition(partition)
8475
if err != nil {
8576
panic(fmt.Sprintf("failed to hash kernelPart: %w\n", err))
8677
}
@@ -110,7 +101,7 @@ func main() {
110101

111102
switch {
112103
case len(conf.Kernel) > 0:
113-
fmt.Println("Loaded kernel config")
104+
fmt.Printf("Loaded kernel config\n")
114105
kernel, err := partition.ReadAll(conf.Kernel[0])
115106
if err != nil {
116107
panic(fmt.Sprintf("invalid kernel path: %v\n", err))
@@ -140,7 +131,7 @@ func main() {
140131
bootKernel(kernel, dtb, conf.CmdLine)
141132

142133
case len(conf.Unikernel) > 0:
143-
fmt.Println("Loaded unikernel config")
134+
fmt.Printf("Loaded unikernel config\n")
144135
unikernel, err := partition.ReadAll(conf.Unikernel[0])
145136
if err != nil {
146137
panic(fmt.Sprintf("invalid unikernel path: %v\n", err))
Binary file not shown.

‎go.mod

+1-1
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ require (
1212
github.com/dsoprea/go-ext4 v0.0.0-20190528173430-c13b09fc0ff8
1313
github.com/dsoprea/go-logging v0.0.0-20200710184922-b02d349568dd // indirect
1414
github.com/ethereum/go-ethereum v1.9.24
15-
github.com/f-secure-foundry/tamago v0.0.0-20201127101424-e0d521d1ec51
15+
github.com/f-secure-foundry/tamago v0.0.0-20201127230944-9b6478583a99
1616
github.com/fatih/color v1.9.0 // indirect
1717
github.com/go-sql-driver/mysql v1.5.0
1818
github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b

‎go.sum

+2
Original file line numberDiff line numberDiff line change
@@ -219,6 +219,8 @@ github.com/f-secure-foundry/tamago v0.0.0-20201126222218-ecc3aa53ae88 h1:YbymNPH
219219
github.com/f-secure-foundry/tamago v0.0.0-20201126222218-ecc3aa53ae88/go.mod h1:+xmhdYxGfaNhWUZ/F1mbwFu38H73slg/k/oPjOL7tc8=
220220
github.com/f-secure-foundry/tamago v0.0.0-20201127101424-e0d521d1ec51 h1:pewB+4yHr4ivS1KAfQx3jQtld96ceV2ORqpi7xZ3s/Y=
221221
github.com/f-secure-foundry/tamago v0.0.0-20201127101424-e0d521d1ec51/go.mod h1:+xmhdYxGfaNhWUZ/F1mbwFu38H73slg/k/oPjOL7tc8=
222+
github.com/f-secure-foundry/tamago v0.0.0-20201127230944-9b6478583a99 h1:kMeONmx7TxN2Wgn6aURmObm2htgfey6luj3wJiGkYpM=
223+
github.com/f-secure-foundry/tamago v0.0.0-20201127230944-9b6478583a99/go.mod h1:+xmhdYxGfaNhWUZ/F1mbwFu38H73slg/k/oPjOL7tc8=
222224
github.com/fatih/color v1.3.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5KwzbycvMj4=
223225
github.com/fatih/color v1.7.0 h1:DkWD4oS2D8LGGgTQ6IvwJJXSL5Vp2ffcQg58nFV38Ys=
224226
github.com/fatih/color v1.7.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5KwzbycvMj4=

0 commit comments

Comments
 (0)
Please sign in to comment.