Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 4 additions & 2 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -94,10 +94,12 @@ testapp-probe:

# Simple ed25519 verifier app
VERIFIEROBJS=verifier/main.o verifier/verify.o verifier/app_proto.o \
verifier/update.o
verifier/update.o verifier/pubkey.o verifier/util.o

verifier/app.elf: $(VERIFIEROBJS)
$(CC) $(CFLAGS) $(VERIFIEROBJS) $(LDFLAGS) -I $(LIBDIR) -o $@
$(VERIFIEROBJS): $(INCLUDE)/tkey/tk1_mem.h verifier/bv_nad.h
$(VERIFIEROBJS): $(INCLUDE)/tkey/tk1_mem.h verifier/bv_nad.h verifier/app_proto.h \
verifier/pubkey.h verifier/update.h verifier/util.h verifier/verify.h

TESTAPPOBJS=testapp/main.o testapp/app_proto.o
testapp/app_a.elf: $(TESTAPPOBJS) testapp/app_a.c
Expand Down
38 changes: 30 additions & 8 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,8 @@ It currently supports:
when installation has finished, then it verifies and starts the app
in slot 1.

- Installing a vendor pubkey on flash.
- Installing a vendor pubkey on flash. This needs to be signed by the
existing vendor private key.

- Running both verifier and a verified device app sent from client.

Expand Down Expand Up @@ -126,10 +127,14 @@ able to talk to the firmware/apps when using QEMU.
*NB*: use `-no-expect-close` when running `tkey-mgt` against QEMU. The
connection behaves differently compared to real hardware.

#### boot

Command `boot` does a verified boot of the device app specified with
`-app`. It assumes a TKey running an app that supports the reset
command.

#### install

Command `install` installs the device app specified with `-app` in
slot 1. It assumes you are running an app that supports the reset
command and that a verifier is present in slot 0. See above about
Expand All @@ -152,19 +157,36 @@ corresponding to this public key you can use for testing:
9b62773323ef41a11834824194e55164d325eb9cdcc10ddda7d10ade4fbd8f6d
```

NOTE WELL: For real use signing of device apps [the tkey-sign
tool](https://github.com/tillitis/tkey-sign-cli) with BLAKE2s support
will most likely be used instead of `sign-tool`.

#### install-pubkey

Command `install-pubkey` installs the pubkey specified with `-pub`,
replacing any installed pubkey. During the installion the user is
replacing any installed pubkey. During the installation the user is
asked to confirm by touching the TKey touch sensor three times.

A pubkey file can be created with:
A *binary* version of the public key needs to be signed by the current
vendor private key. Typical series of commands:

```
$ ./sign-tool -p pubkey -s path-to-private-key
# Create a private key, key.private. See dev-seed for format (64 bytes in hex).

# Get the public key in binary form:
./sign-tool -P key.bin -s key.private

# Sign the binary key with old private key
./sign-tool -m key.bin -s dev-seed

# Get the public key in signify form:
./sign-tool -p key.pub -s key.private

# Install the new public key on TKey
./tkey-mgt -cmd install-pubkey -pub key.pub -sig key.bin.sig
```

NOTE WELL: For real use signing of device apps [the tkey-sign
tool](https://github.com/tillitis/tkey-sign-cli) with BLAKE2s support
will most likely be used instead of `sign-tool`.
Remember to use `-no-expect-close` if you're running against qemu.

## Chained Reset

Expand Down Expand Up @@ -200,7 +222,7 @@ The protocol has the following requests and responses:
| `CMD_UPDATE_APP_INIT` | Initialize app installation | 128 B | 0x03 | 32 bit LE app size, 32 B app digest, 64 B signature | `CMD_UPDATE_APP_INIT` |
| `CMD_UPDATE_APP_CHUNK` | Store a chunk of an app on flash | 128 B | 0x04 | 127 B app data | `CMD_UPDATE_APP_CHUNK` |
| `CMD_GET_PUBKEY` | Get the public key installed on flash | 1 B | 0x05 | none | `CMD_GET_PUBKEY` |
| `CMD_STORE_PUBKEY` | Store public key on flash | 128 B | 0x06 | 32 B public key | `CMD_STORE_PUBKEY` |
| `CMD_STORE_PUBKEY` | Store public key on flash | 128 B | 0x06 | 32 B public key, 64 B signature | `CMD_STORE_PUBKEY` |
| `CMD_SET_PUBKEY` | Set pubkey used by `CMD_VERIFY` | 128 B | 0x07 | 32 B public key | `CMD_SET_PUBKEY` |
| `CMD_ERASE_AREAS` | Erase all app storage areas | 1 B | 0x08 | none | `CMD_ERASE_AREAS` |
| `CMD_RESET` | Reset TKey | 4 B | 0xfe | 1 B reset type, 1 B next app data | none |
Expand Down
24 changes: 18 additions & 6 deletions cmd/sign-tool/sign-tool.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,10 +17,13 @@ import (
)

func usage() {
_, _ = fmt.Fprintf(flag.CommandLine.Output(), "%s [-m|-p] FILE -s seckey\n\n", os.Args[0])
_, _ = fmt.Fprintf(flag.CommandLine.Output(), "Sign message in FILE and write the result to file.sig.\n")
_, _ = fmt.Fprintf(flag.CommandLine.Output(), "Or, write pubkey generated from seckey to FILE.\n")
_, _ = fmt.Fprintf(flag.CommandLine.Output(), "Signatures and pubkeys are produced by Ed25519-signing the Blake2s digest of message.\n\n")
_, _ = fmt.Fprintf(flag.CommandLine.Output(), "%s signs a BLAKE2s digest of the contents of a file or exports the public key.\n\n", os.Args[0])
_, _ = fmt.Fprintf(flag.CommandLine.Output(), "Sign message in FILE and write the result to FILE.sig (default):\n")
_, _ = fmt.Fprintf(flag.CommandLine.Output(), "%s -m FILE -s seckey [-o SIGFILE]\n\n", os.Args[0])

_, _ = fmt.Fprintf(flag.CommandLine.Output(), "Write pubkey (in binary form with -P) generated from seckey to FILE.\n")
_, _ = fmt.Fprintf(flag.CommandLine.Output(), "%s -p|P FILE -s seckey\n\n", os.Args[0])

flag.PrintDefaults()
}

Expand All @@ -40,12 +43,13 @@ func main() {
messagePath := flag.String("m", "", "File containing message to sign")
sigPath := flag.String("o", "", "File to write signature to. Default: <message-file>.sig")
pubkeyPath := flag.String("p", "", "File to write pubkey to")
binPubkeyPath := flag.String("P", "", "File to write pubkey to")
Comment thread
mchack-work marked this conversation as resolved.
seedPath := flag.String("s", "", "File containing private key seed in hex")
flag.Usage = usage

flag.Parse()

noFileArgs := *messagePath == "" && *pubkeyPath == ""
noFileArgs := *messagePath == "" && *pubkeyPath == "" && *binPubkeyPath == ""
tooManyFileArgs := *messagePath != "" && *pubkeyPath != ""
if noFileArgs || tooManyFileArgs {
flag.Usage()
Expand Down Expand Up @@ -99,7 +103,7 @@ func main() {

copy(sig.Sig[:], rawSig[:])

path := *messagePath+".sig"
path := *messagePath + ".sig"
if *sigPath != "" {
path = *sigPath
}
Expand All @@ -121,5 +125,13 @@ func main() {
fmt.Printf("Couldn't store pubkey: %v\n", err)
os.Exit(1)
}
} else if *binPubkeyPath != "" {
// Write only the public key part as a binary file

err = sigfile.WriteBinary(*binPubkeyPath, privateKey.Public().(ed25519.PublicKey), true)
if err != nil {
fmt.Printf("Couldn't store pubkey: %v\n", err)
os.Exit(1)
}
}
}
8 changes: 6 additions & 2 deletions cmd/tkey-mgt/proto.go
Original file line number Diff line number Diff line change
Expand Up @@ -160,15 +160,19 @@ func getPubkey(tk *tkeyclient.TillitisKey) ([ed25519.PublicKeySize]byte, error)
return pubkey, nil
}

func storePubkey(tk *tkeyclient.TillitisKey, pubkey [32]byte) error {
func storePubkey(tk *tkeyclient.TillitisKey, pubkey [32]byte, sig [64]byte) error {
id := 0x01

tx, err := tkeyclient.NewFrameBuf(cmdStorePubkey, id)
if err != nil {
return err
}

copy(tx[2:], pubkey[:])
offset := 2
copy(tx[offset:], pubkey[:])
offset += 32 // length of pubkey
copy(tx[offset:], sig[:])
offset += 64 // length of sig

tkeyclient.Dump("store pubkey tx", tx)

Expand Down
14 changes: 10 additions & 4 deletions cmd/tkey-mgt/tkey-mgt.go
Original file line number Diff line number Diff line change
Expand Up @@ -172,7 +172,7 @@ func startVerifier(tk *tkeyclient.TillitisKey, pubKey [ed25519.PublicKeySize]byt
return nil
}

func installPubkey(tk *tkeyclient.TillitisKey, pubkey [32]byte) error {
func installPubkey(tk *tkeyclient.TillitisKey, pubkey [32]byte, sig [64]byte) error {
err := reset(tk, fwResetTypeStartFlash0, verifierResetDstCmdMode)
if err != nil {
return err
Expand All @@ -195,10 +195,10 @@ func installPubkey(tk *tkeyclient.TillitisKey, pubkey [32]byte) error {
}

fmt.Printf("Your TKey will begin to blink yellow.\n")
fmt.Printf("Confirm the pubkey update by touching the TKey touch sensor three times.\n")
fmt.Printf("Confirm the pubkey update by tapping the TKey touch sensor.\n")
fmt.Printf("If you want to abort then wait for the process to timeout.\n")

err = storePubkey(tk, pubkey)
err = storePubkey(tk, pubkey, sig)
if err != nil {
return err
}
Expand Down Expand Up @@ -372,7 +372,13 @@ func main() {
os.Exit(1)
}

if err := installPubkey(tk, appPub.Key); err != nil {
appSig, err := sigfile.ReadSig(*sigPath)
if err != nil {
fmt.Printf("couldn't read file: %v\n", err)
os.Exit(1)
}

if err := installPubkey(tk, appPub.Key, appSig.Sig); err != nil {
fmt.Printf("couldn't set pubkey: %v\n", err)
exit(1)
}
Expand Down
33 changes: 32 additions & 1 deletion sigfile/sigfile.go
Original file line number Diff line number Diff line change
Expand Up @@ -97,7 +97,7 @@ func WriteBase64(filename string, data any, comment string, overwrite bool) erro
f, err = os.OpenFile(filename, os.O_RDWR|os.O_CREATE|os.O_EXCL, 0o666)
if err != nil {
if os.IsExist(err) && overwrite {
f, err = os.OpenFile(filename, os.O_RDWR|os.O_CREATE, 0o666)
f, err = os.OpenFile(filename, os.O_RDWR|os.O_CREATE|os.O_TRUNC, 0o666)
if err != nil {
return fmt.Errorf("%w", err)
}
Expand All @@ -119,3 +119,34 @@ func WriteBase64(filename string, data any, comment string, overwrite bool) erro

return nil
}

func WriteBinary(filename string, data any, overwrite bool) error {
var buf bytes.Buffer

err := binary.Write(&buf, binary.BigEndian, data)
if err != nil {
return fmt.Errorf("%w", err)
}

var f *os.File

f, err = os.OpenFile(filename, os.O_RDWR|os.O_CREATE|os.O_EXCL, 0o666)
if err != nil {
if os.IsExist(err) && overwrite {
f, err = os.OpenFile(filename, os.O_RDWR|os.O_CREATE|os.O_TRUNC, 0o666)
if err != nil {
return fmt.Errorf("%w", err)
}
} else {
return fmt.Errorf("%w", err)
}
}
defer func() { _ = f.Close() }()

_, err = f.Write([]byte(buf.Bytes()))
if err != nil {
return fmt.Errorf("%w", err)
}

return nil
}
6 changes: 6 additions & 0 deletions verifier/app_proto.h
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,12 @@
#include <tkey/lib.h>
#include <tkey/proto.h>

// Incoming packet from client
struct packet {
struct frame_header hdr; // Framing Protocol header
uint8_t cmd[CMDLEN_MAXBYTES]; // Application level protocol
};

enum appcmd {
CMD_VERIFY = 0x01,
CMD_UPDATE_APP_INIT = 0x03,
Expand Down
60 changes: 5 additions & 55 deletions verifier/main.c
Original file line number Diff line number Diff line change
Expand Up @@ -13,11 +13,12 @@
#include <tkey/syscall.h>
#include <tkey/timer.h>
#include <tkey/tk1_mem.h>
#include <tkey/touch.h>

#include "app_proto.h"
#include "bv_nad.h"
#include "pubkey.h"
#include "update.h"
#include "util.h"
#include "verify.h"

// clang-format off
Expand All @@ -30,16 +31,6 @@ static volatile uint32_t *ver = (volatile uint32_t *) TK1_MMIO_TK1_VERSION;
// clang-format on

#define CHUNK_PAYLOAD_LEN (CMDLEN_MAXBYTES - 1)
#define PRESENCE_TIMEOUT_S 20
#define PRESENCE_REPEAT_DELAY_S 1

#define APP_LED_COLOR (LED_RED | LED_GREEN)

// Incoming packet from client
struct packet {
struct frame_header hdr; // Framing Protocol header
uint8_t cmd[CMDLEN_MAXBYTES]; // Application level protocol
};

// read_command takes a frame header and a command to fill in after
// parsing. It returns 0 on success.
Expand Down Expand Up @@ -179,14 +170,6 @@ void reset(uint32_t type, enum bv_nad reset_dst)
sys_reset(&rst, 1);
}

static void signal_issue()
{
for (uint8_t i = 0; i < 3; i++) {
led_set(i % 2 ? APP_LED_COLOR : LED_BLACK);
timer_wait(1);
}
}

static enum state verify_flash(uint8_t app_digest[32],
uint8_t app_signature[64], uint8_t pubkey[32])
{
Expand Down Expand Up @@ -246,20 +229,6 @@ static void wait_for_app_chunk(struct context *ctx)
}
}

bool user_is_present(void)
{
for (uint8_t i = 0; i < 3; i++) {
bool present = touch_wait(APP_LED_COLOR, PRESENCE_TIMEOUT_S);
if (!present) {
return false;
}
led_set(LED_BLACK);
timer_wait(PRESENCE_REPEAT_DELAY_S);
}

return true;
}

enum state wait_for_command(enum state state, struct context *ctx)
{
struct packet pkt = {0};
Expand All @@ -282,7 +251,7 @@ enum state wait_for_command(enum state state, struct context *ctx)
assert(1 == 2);
}

if (!user_is_present()) {
if (!user_is_present(3)) {
rsp[0] = STATUS_BAD;
appreply(pkt.hdr, CMD_ERASE_AREAS, rsp);
break;
Expand Down Expand Up @@ -327,26 +296,7 @@ enum state wait_for_command(enum state state, struct context *ctx)
break;

case CMD_STORE_PUBKEY:
if (pkt.hdr.len != 128) {
// Bad length
assert(1 == 2);
}

if (!user_is_present()) {
rsp[0] = STATUS_BAD;
appreply(pkt.hdr, CMD_STORE_PUBKEY, rsp);
break;
}

if (sys_preload_set_pubkey(&pkt.cmd[1]) != 0) {
rsp[0] = STATUS_BAD;
appreply(pkt.hdr, CMD_STORE_PUBKEY, rsp);
assert(1 == 2);
}

rsp[0] = STATUS_OK;
appreply(pkt.hdr, CMD_STORE_PUBKEY, rsp);

store_pubkey(pkt);
break;

case CMD_SET_PUBKEY:
Expand Down Expand Up @@ -407,7 +357,7 @@ enum state wait_for_command(enum state state, struct context *ctx)
assert(1 == 2);
}

if (!user_is_present()) {
if (!user_is_present(3)) {
rsp[0] = STATUS_BAD;
appreply(pkt.hdr, CMD_UPDATE_APP_INIT, rsp);
break;
Expand Down
Loading
Loading