/
advertisement.go
61 lines (53 loc) · 1.42 KB
/
advertisement.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
package scanner
import (
"crypto/aes"
"crypto/cipher"
"encoding/binary"
"fmt"
"github.com/lazy-electron-consulting/victron-bluetooth/internal"
)
type Advertisement struct {
Addr string
Mode byte
Model uint16
iv []byte
keyPrefix byte
encryptedPayload []byte
}
// Decrypt decrypts the advert payload using the key.
func (a Advertisement) Decrypt(key []byte) ([]byte, error) {
if key[0] != a.keyPrefix {
return nil, fmt.Errorf("key mismatch, expected %x got %x", a.keyPrefix, key[0])
}
b, err := aes.NewCipher(key)
if err != nil {
return nil, fmt.Errorf("could not create aes: %w", err)
}
s := cipher.NewOFB(b, internal.AppendZero(a.iv, 16))
encrypted := internal.AppendZero(a.encryptedPayload, 16)
decrypted := make([]byte, len(encrypted))
s.XORKeyStream(decrypted, encrypted)
return decrypted, nil
}
func readAdvertisement(addr string, data []byte) (Advertisement, error) {
type layout struct {
_ byte // header
Mode byte
Model uint16
Readout uint8
IV [2]byte
KeyPrefix byte
}
l, err := internal.Decode[layout](data)
if err != nil {
return Advertisement{}, fmt.Errorf("unable to decode: %w", err)
}
return Advertisement{
Addr: addr,
Mode: l.Mode,
Model: l.Model,
iv: l.IV[:],
keyPrefix: l.KeyPrefix,
encryptedPayload: data[binary.Size(l):],
}, nil
}