Skip to content

Commit 36006cb

Browse files
committed
feature: add flash subcommand and move existing functionality to keys subcommand
Signed-off-by: deadprogram <[email protected]>
1 parent 2397c12 commit 36006cb

File tree

4 files changed

+113
-22
lines changed

4 files changed

+113
-22
lines changed

README.md

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -72,18 +72,18 @@ go install github.com/hybridgroup/go-haystack
7272
1. Generate keys for a device
7373

7474
```shell
75-
go-haystack -name=DEVICENAME
75+
go-haystack keys DEVICENAME
7676
```
7777

7878
The keys will be saved in a file named `DEVICENAME.keys` and the configuration file for Haystack will be saved in `DEVICENAME.json`. Replace "DEVICENAME" with whatever you want to name the actual device.
7979

8080

81-
2. Flash the hardware with the target name of your device and those keys
81+
2. Flash the hardware with the TinyGo target and the name of your device.
8282

8383
For example:
8484

8585
```shell
86-
./flash.sh nano-rp2040 DEVICENAME.keys
86+
go-haystack flash nano-rp2040 DEVICENAME
8787
```
8888

8989
This will use TinyGo to compile the firmware using your keys, and then flash it to the device. See [https://tinygo.org/getting-started/overview/](https://tinygo.org/getting-started/overview/) for more information about TinyGo.

keys.go

Lines changed: 8 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -6,22 +6,25 @@ import (
66
"crypto/rand"
77
"crypto/sha256"
88
"encoding/base64"
9+
"errors"
910
"strings"
1011
)
1112

12-
func generateKey() (string, string, string) {
13+
var errInvalidHash = errors.New("Hash contains '/'")
14+
15+
func generateKey() (string, string, string, error) {
1316
// Generate ECDSA private key using P-224 curve
1417
pk, err := ecdsa.GenerateKey(elliptic.P224(), rand.Reader)
1518
if err != nil {
16-
panic(err)
19+
return "", "", "", err
1720
}
1821

1922
// Extract the raw private key bytes
2023
privateKeyBytes := pk.D.Bytes()
2124

2225
// Ensure the private key is 28 bytes long (P-224 curve)
2326
if len(privateKeyBytes) != 28 {
24-
panic("Private key is not 28 bytes long")
27+
return "", "", "", errors.New("Private key is not 28 bytes long")
2528
}
2629

2730
// Encode the raw private key to Base64
@@ -39,8 +42,8 @@ func generateKey() (string, string, string) {
3942

4043
// make sure not '/' in the base64 string
4144
if strings.Contains(hashBase64, "/") {
42-
panic("key generation failed because there was a / in the b64 of the hashed pubkey. try again.")
45+
return "", "", "", errInvalidHash
4346
}
4447

45-
return privateKeyBase64, publicKeyBase64, hashBase64
48+
return privateKeyBase64, publicKeyBase64, hashBase64, nil
4649
}

main.go

Lines changed: 92 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,21 +1,55 @@
11
package main
22

33
import (
4+
"errors"
45
"flag"
56
"fmt"
7+
"os"
8+
"os/exec"
9+
"path/filepath"
10+
"strings"
611
)
712

813
func main() {
9-
nameFlag := flag.String("name", "", "name of the device to be added")
10-
verboseFlag := flag.Bool("verbose", false, "enable verbose mode")
11-
14+
verboseFlag := flag.Bool("v", false, "enable verbose mode")
1215
flag.Parse()
13-
if *nameFlag == "" {
14-
fmt.Println("Please provide a device name using the -name flag")
16+
17+
args := flag.Args()
18+
if len(args) < 1 {
19+
fmt.Println("either 'keys' or 'flash' subcommand is required.", len(args))
1520
return
1621
}
1722

18-
priv, pub, hash := generateKey()
23+
switch args[0] {
24+
case "keys":
25+
if len(args) < 2 {
26+
fmt.Println("Please provide a device name")
27+
return
28+
}
29+
if err := generateKeys(args[1], verboseFlag); err != nil {
30+
fmt.Println("failed to generate keys:", err)
31+
}
32+
case "flash":
33+
if len(args) < 3 {
34+
fmt.Println("Please provide a device target and name")
35+
return
36+
}
37+
if err := flashDevice(args[1], args[2], verboseFlag); err != nil {
38+
fmt.Println("failed to flash device:", err)
39+
}
40+
default:
41+
fmt.Println("either 'keys' or 'flash' subcommand is required.")
42+
return
43+
}
44+
}
45+
46+
func generateKeys(name string, verboseFlag *bool) error {
47+
// TODO: check if overwriting keys
48+
49+
priv, pub, hash, err := generateKey()
50+
if err != nil {
51+
return err
52+
}
1953

2054
// Print the keys and hash
2155
if *verboseFlag {
@@ -25,8 +59,58 @@ func main() {
2559
}
2660

2761
// save keys file
28-
saveKeys(*nameFlag, priv, pub, hash)
62+
if err := saveKeys(name, priv, pub, hash); err != nil {
63+
return err
64+
}
2965

3066
// save device file
31-
saveDevice(*nameFlag, priv)
67+
return saveDevice(name, priv)
68+
}
69+
70+
func flashDevice(name string, target string, verboseFlag *bool) error {
71+
key, err := readKey(name)
72+
if err != nil {
73+
return err
74+
}
75+
76+
pwd := os.Getenv("PWD")
77+
pth := filepath.Join(pwd, "firmware")
78+
if err := os.Chdir(pth); err != nil {
79+
panic(err)
80+
}
81+
defer os.Chdir(pwd)
82+
83+
keyVal := fmt.Sprintf("-X main.AdvertisingKey='%s'", key)
84+
if *verboseFlag {
85+
fmt.Println("tinygo", "flash", "-target", target, "-ldflags", keyVal, ".")
86+
}
87+
88+
cmd := exec.Command("tinygo", "flash", "-target", target, "-ldflags", keyVal, ".")
89+
cmd.Stdout = os.Stdout
90+
cmd.Stderr = os.Stderr
91+
return cmd.Run()
92+
}
93+
94+
func readKey(name string) (string, error) {
95+
f, err := os.Open(name + ".keys")
96+
if err != nil {
97+
return "", err
98+
}
99+
defer f.Close()
100+
101+
b := make([]byte, 1024)
102+
n, err := f.Read(b)
103+
if err != nil {
104+
return "", err
105+
}
106+
107+
lines := strings.Split(string(b[:n]), "\n")
108+
for _, line := range lines {
109+
if strings.Contains(line, "Advertisement key") {
110+
s := strings.Split(line, ":")
111+
return strings.TrimLeft(s[1], " "), nil
112+
}
113+
}
114+
115+
return "", errors.New("key not found")
32116
}

save.go

Lines changed: 10 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -8,17 +8,19 @@ import (
88
"text/template"
99
)
1010

11-
func saveKeys(name string, priv string, pub string, hash string) {
11+
func saveKeys(name string, priv string, pub string, hash string) error {
1212
f, err := os.Create(name + ".keys")
1313
if err != nil {
14-
panic(err)
14+
return err
1515
}
1616

1717
defer f.Close()
1818

1919
f.Write([]byte(fmt.Sprintf("Private key: %s\n", priv)))
2020
f.Write([]byte(fmt.Sprintf("Advertisement key: %s\n", pub)))
2121
f.Write([]byte(fmt.Sprintf("Hashed adv key: %s\n", hash)))
22+
23+
return nil
2224
}
2325

2426
const deviceTemplate = `[
@@ -42,15 +44,15 @@ const deviceTemplate = `[
4244
]
4345
`
4446

45-
func saveDevice(name string, priv string) {
47+
func saveDevice(name string, priv string) error {
4648
t, err := template.New("device").Parse(deviceTemplate)
4749
if err != nil {
48-
panic(err)
50+
return err
4951
}
5052

5153
f, err := os.Create(name + ".json")
5254
if err != nil {
53-
panic(err)
55+
return err
5456
}
5557

5658
defer f.Close()
@@ -61,8 +63,10 @@ func saveDevice(name string, priv string) {
6163
"PrivateKey": priv,
6264
})
6365
if err != nil {
64-
panic(err)
66+
return err
6567
}
68+
69+
return nil
6670
}
6771

6872
// Returns an int >= min, < max

0 commit comments

Comments
 (0)