9
9
package main
10
10
11
11
import (
12
+ "crypto/elliptic"
13
+ "crypto/sha256"
12
14
"encoding/base64"
13
- "errors"
15
+ "encoding/binary"
16
+ "io"
17
+ "machine"
18
+ "math/big"
19
+ "os"
14
20
"time"
15
21
22
+ "golang.org/x/crypto/hkdf"
16
23
"tinygo.org/x/bluetooth"
24
+ "tinygo.org/x/tinyfs/littlefs"
17
25
)
18
26
19
- var adapter = bluetooth .DefaultAdapter
27
+ var (
28
+ adapter = bluetooth .DefaultAdapter
29
+ lfs = littlefs .New (machine .Flash )
30
+ )
31
+
32
+ func getIndex () uint64 {
33
+ f , err := lfs .Open ("/haystack" )
34
+ if err != nil {
35
+ return 0
36
+ }
37
+ defer f .Close ()
38
+
39
+ var buf [8 ]byte
40
+ _ , err = io .ReadFull (f , buf [:])
41
+ must ("read index from file" , err )
42
+ return binary .LittleEndian .Uint64 (buf [:])
43
+ }
44
+
45
+ func writeIndex (i uint64 ) error {
46
+ f , err := lfs .OpenFile ("/haystack" , os .O_CREATE )
47
+ if err != nil {
48
+ return err
49
+ }
50
+ defer f .Close ()
51
+
52
+ var buf [8 ]byte
53
+ binary .LittleEndian .PutUint64 (buf [:], i )
54
+ _ , err = f .Write (buf [:])
55
+ return err
56
+ }
20
57
21
58
func main () {
22
59
// wait for USB serial to be available
23
60
time .Sleep (2 * time .Second )
24
61
25
- key , err := getKeyData ()
26
- if err != nil {
27
- fail ("failed to get key data: " + err .Error ())
62
+ config := littlefs.Config {
63
+ CacheSize : 64 ,
64
+ LookaheadSize : 32 ,
65
+ BlockCycles : 512 ,
66
+ }
67
+ lfs .Configure (& config )
68
+ if err := lfs .Mount (); err != nil {
69
+ must ("format littlefs" , lfs .Format ())
70
+ must ("mount littlefs" , lfs .Mount ())
28
71
}
29
- println ("key is" , AdvertisingKey , "(" , len (key ), "bytes)" )
72
+ println ("littlefs mounted" )
73
+
74
+ // Get and increment index. Ideally this would be done at the end of 15
75
+ // minutes. However writing seems to fail once advertising has started.
76
+ derivIndex := getIndex ()
77
+ derivIndex ++
78
+ must ("write new index to file" , writeIndex (derivIndex ))
79
+ println ("derivation secret is" , DerivationSecret )
80
+ println ("derivation index is" , derivIndex )
81
+
82
+ priv , pub , err := getKeyData (derivIndex )
83
+ must ("get key data" , err )
84
+ println ("private key is" , base64 .StdEncoding .EncodeToString (priv ))
85
+ println ("public key is" , base64 .StdEncoding .EncodeToString (pub ))
30
86
31
87
opts := bluetooth.AdvertisementOptions {
32
88
AdvertisementType : bluetooth .AdvertisingTypeNonConnInd ,
33
89
Interval : bluetooth .NewDuration (1285000 * time .Microsecond ), // 1285ms
34
- ManufacturerData : []bluetooth.ManufacturerDataElement {findMyData (key )},
90
+ ManufacturerData : []bluetooth.ManufacturerDataElement {findMyData (pub )},
35
91
}
36
92
37
93
must ("enable BLE stack" , adapter .Enable ())
38
94
39
95
// Set the address to the first 6 bytes of the public key.
40
- adapter .SetRandomAddress (bluetooth.MAC {key [5 ], key [4 ], key [3 ], key [2 ], key [1 ], key [0 ] | 0xC0 })
96
+ adapter .SetRandomAddress (bluetooth.MAC {pub [5 ], pub [4 ], pub [3 ], pub [2 ], pub [1 ], pub [0 ] | 0xC0 })
41
97
42
98
println ("configure advertising..." )
43
99
adv := adapter .DefaultAdvertisement ()
@@ -46,24 +102,48 @@ func main() {
46
102
println ("start advertising..." )
47
103
must ("start adv" , adv .Start ())
48
104
105
+ boot := time .Now ()
49
106
address , _ := adapter .Address ()
50
- for {
51
- println ("FindMy device using" , address .MAC .String ())
107
+ for uptime := 0 ; ; uptime ++ {
108
+ if uptime % 100 == 0 {
109
+ println ("FindMy device using" , address .MAC .String (), "uptime" , uptime )
110
+ }
52
111
time .Sleep (time .Second )
112
+ if time .Since (boot ) > 15 * time .Minute {
113
+ machine .CPUReset ()
114
+ }
53
115
}
54
116
}
55
117
56
- // getKeyData returns the public key data from the base64 encoded string.
57
- func getKeyData () ([]byte , error ) {
58
- val , err := base64 .StdEncoding .DecodeString (AdvertisingKey )
118
+ const keySize = 28
119
+
120
+ var curve = elliptic .P224 ()
121
+
122
+ func getKeyData (i uint64 ) ([]byte , []byte , error ) {
123
+ secret , err := base64 .StdEncoding .DecodeString (DerivationSecret )
59
124
if err != nil {
60
- return nil , err
61
- }
62
- if len (val ) != 28 {
63
- return nil , errors .New ("public key must be 28 bytes long" )
125
+ return nil , nil , err
64
126
}
65
127
66
- return val , nil
128
+ info := make ([]byte , 8 )
129
+ binary .LittleEndian .PutUint64 (info , i )
130
+ r := hkdf .New (sha256 .New , secret , nil , info )
131
+ for {
132
+ priv := make ([]byte , keySize )
133
+ if _ , err := io .ReadFull (r , priv ); err != nil {
134
+ return nil , nil , err
135
+ }
136
+
137
+ privInt := new (big.Int ).SetBytes (priv )
138
+ n := curve .Params ().N
139
+ if privInt .Sign () > 0 && privInt .Cmp (n ) < 0 {
140
+ xInt , _ := curve .ScalarBaseMult (priv )
141
+ xBytes := xInt .Bytes ()
142
+ x := make ([]byte , keySize )
143
+ copy (x [keySize - len (xBytes ):], xBytes )
144
+ return priv , x , nil
145
+ }
146
+ }
67
147
}
68
148
69
149
const (
0 commit comments