aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMike Crute <mike@crute.us>2022-08-04 17:43:40 -0700
committerMike Crute <mike@crute.us>2022-08-04 17:43:40 -0700
commitf7b39e7e79e75396ccb0967b6c791f2e0264acef (patch)
treecc1be8e539b3f98a6bdfa95a5d51881d7b245ee7
parent11b6cf3ce57b4e1706c8410ad7ccd499902fc4de (diff)
downloadgo-inform-f7b39e7e79e75396ccb0967b6c791f2e0264acef.tar.bz2
go-inform-f7b39e7e79e75396ccb0967b6c791f2e0264acef.tar.xz
go-inform-f7b39e7e79e75396ccb0967b6c791f2e0264acef.zip
Support AES GCM and snappy compression
-rw-r--r--go.mod5
-rw-r--r--go.sum2
-rw-r--r--inform/codec.go16
-rw-r--r--inform/crypto.go50
-rw-r--r--inform/inform.go56
5 files changed, 119 insertions, 10 deletions
diff --git a/go.mod b/go.mod
new file mode 100644
index 0000000..6adbd45
--- /dev/null
+++ b/go.mod
@@ -0,0 +1,5 @@
1module code.crute.me/mcrute/go-inform
2
3go 1.18
4
5require github.com/golang/snappy v0.0.4
diff --git a/go.sum b/go.sum
new file mode 100644
index 0000000..74eae48
--- /dev/null
+++ b/go.sum
@@ -0,0 +1,2 @@
1github.com/golang/snappy v0.0.4 h1:yAGX7huGHXlcLOEtBnF4w7FQwA26wojNCwOYAEhLjQM=
2github.com/golang/snappy v0.0.4/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q=
diff --git a/inform/codec.go b/inform/codec.go
index e5c9296..8147986 100644
--- a/inform/codec.go
+++ b/inform/codec.go
@@ -5,6 +5,8 @@ import (
5 "encoding/binary" 5 "encoding/binary"
6 "errors" 6 "errors"
7 "io" 7 "io"
8
9 "github.com/golang/snappy"
8) 10)
9 11
10type Codec struct { 12type Codec struct {
@@ -33,6 +35,7 @@ func (c *Codec) Unmarshal(fp io.Reader) (*InformWrapper, error) {
33 35
34 var dataLen int32 36 var dataLen int32
35 binary.Read(fp, binary.BigEndian, &dataLen) 37 binary.Read(fp, binary.BigEndian, &dataLen)
38 w.DataLength = dataLen
36 39
37 p := make([]byte, dataLen) 40 p := make([]byte, dataLen)
38 io.ReadFull(fp, p) 41 io.ReadFull(fp, p)
@@ -42,12 +45,21 @@ func (c *Codec) Unmarshal(fp io.Reader) (*InformWrapper, error) {
42 return nil, errors.New("No key found") 45 return nil, errors.New("No key found")
43 } 46 }
44 47
45 u, err := Decrypt(p, iv, key) 48 u, err := Decrypt(p, iv, key, w)
46 if err != nil { 49 if err != nil {
47 return nil, err 50 return nil, err
48 } 51 }
49 52
50 w.Payload = u 53 if w.IsSnappyCompressed() {
54 w.Payload, err = snappy.Decode(nil, u)
55 if err != nil {
56 return nil, err
57 }
58 } else if w.IsZlibCompressed() {
59 return nil, errors.New("payload is zlib compressed, not supported")
60 } else {
61 w.Payload = u
62 }
51 63
52 return w, nil 64 return w, nil
53} 65}
diff --git a/inform/crypto.go b/inform/crypto.go
index 90118c1..4e82741 100644
--- a/inform/crypto.go
+++ b/inform/crypto.go
@@ -5,6 +5,7 @@ import (
5 "crypto/aes" 5 "crypto/aes"
6 "crypto/cipher" 6 "crypto/cipher"
7 "crypto/rand" 7 "crypto/rand"
8 "encoding/binary"
8 "encoding/hex" 9 "encoding/hex"
9 "errors" 10 "errors"
10) 11)
@@ -68,7 +69,54 @@ func Encrypt(payload []byte, key string) ([]byte, []byte, error) {
68 return ct, iv, nil 69 return ct, iv, nil
69} 70}
70 71
71func Decrypt(payload, iv []byte, key string) ([]byte, error) { 72func Decrypt(payload, iv []byte, key string, w *InformWrapper) ([]byte, error) {
73 if !w.IsEncrypted() {
74 return nil, errors.New("payload is not encrypted")
75 }
76
77 if w.IsGCMEncrypted() {
78 return decryptGCM(payload, iv, key, w)
79 } else {
80 return decryptCBC(payload, iv, key)
81 }
82
83 return nil, nil
84}
85
86func buildAuthData(w *InformWrapper, iv []byte) []byte {
87 ad := &bytes.Buffer{}
88 binary.Write(ad, binary.BigEndian, int32(PROTOCOL_MAGIC))
89 binary.Write(ad, binary.BigEndian, int32(w.Version))
90 ad.Write(w.MacAddr)
91 binary.Write(ad, binary.BigEndian, int16(w.Flags))
92 ad.Write(iv)
93 binary.Write(ad, binary.BigEndian, int32(w.DataVersion))
94 binary.Write(ad, binary.BigEndian, int32(w.DataLength))
95 return ad.Bytes()
96}
97
98func decryptGCM(payload, iv []byte, key string, w *InformWrapper) ([]byte, error) {
99 block, err := decodeHexKey(key)
100 if err != nil {
101 return nil, err
102 }
103
104 mode, err := cipher.NewGCMWithNonceSize(block, 16)
105 if err != nil {
106 return nil, err
107 }
108
109 _, err = mode.Open(payload[:0], iv, payload, buildAuthData(w, iv))
110 if err != nil {
111 return nil, err
112 }
113
114 // The last block always seems to be garbage, maybe it's padding or
115 // something else. I have not looked carefully at it.
116 return payload[:len(payload)-aes.BlockSize], nil
117}
118
119func decryptCBC(payload, iv []byte, key string) ([]byte, error) {
72 b := make([]byte, len(payload)) 120 b := make([]byte, len(payload))
73 121
74 block, err := decodeHexKey(key) 122 block, err := decodeHexKey(key)
diff --git a/inform/inform.go b/inform/inform.go
index ac3b57d..59f969c 100644
--- a/inform/inform.go
+++ b/inform/inform.go
@@ -11,8 +11,10 @@ const (
11 INFORM_VERSION int32 = 0 11 INFORM_VERSION int32 = 0
12 DATA_VERSION int32 = 1 12 DATA_VERSION int32 = 1
13 13
14 ENCRYPTED_FLAG = 1 14 ENCRYPTED_FLAG = 1
15 COMPRESSED_FLAG = 2 15 ZLIB_COMPRESSED_FLAG = 2
16 SNAPPY_COMPRESSED_FLAG = 4
17 AES_GCM_FLAG = 8
16) 18)
17 19
18// Wrapper around an inform message, serializes directly into the wire 20// Wrapper around an inform message, serializes directly into the wire
@@ -22,6 +24,7 @@ type InformWrapper struct {
22 MacAddr []byte 24 MacAddr []byte
23 Flags int16 25 Flags int16
24 DataVersion int32 26 DataVersion int32
27 DataLength int32
25 Payload []byte 28 Payload []byte
26} 29}
27 30
@@ -86,7 +89,18 @@ func (i *InformWrapper) String() string {
86 fmt.Fprintf(b, "Mac Addr: \t%s\n", i.FormattedMac()) 89 fmt.Fprintf(b, "Mac Addr: \t%s\n", i.FormattedMac())
87 fmt.Fprintf(b, "Flags: \t%d\n", i.Flags) 90 fmt.Fprintf(b, "Flags: \t%d\n", i.Flags)
88 fmt.Fprintf(b, " Encrypted: \t%t\n", i.IsEncrypted()) 91 fmt.Fprintf(b, " Encrypted: \t%t\n", i.IsEncrypted())
89 fmt.Fprintf(b, " Compressed: \t%t\n", i.IsCompressed()) 92 fmt.Fprintf(b, " Compressed: \t%t", i.IsCompressed())
93 if i.IsCompressed() {
94 if i.IsZlibCompressed() {
95 fmt.Fprintf(b, " (zlib)\n")
96 } else if i.IsSnappyCompressed() {
97 fmt.Fprintf(b, " (snappy)\n")
98 } else {
99 fmt.Fprintf(b, " (unknown)\n")
100 }
101 } else {
102 fmt.Fprintf(b, "\n")
103 }
90 fmt.Fprintf(b, "Data Version: \t%d\n", i.DataVersion) 104 fmt.Fprintf(b, "Data Version: \t%d\n", i.DataVersion)
91 fmt.Fprintf(b, "Payload: \t%q\n", i.Payload) 105 fmt.Fprintf(b, "Payload: \t%q\n", i.Payload)
92 106
@@ -105,14 +119,42 @@ func (i *InformWrapper) SetEncrypted(e bool) {
105 } 119 }
106} 120}
107 121
122func (i *InformWrapper) IsGCMEncrypted() bool {
123 return i.Flags&AES_GCM_FLAG != 0
124}
125
126func (i *InformWrapper) SetGCMEncrypted(e bool) {
127 if e {
128 i.Flags |= AES_GCM_FLAG
129 } else {
130 i.Flags &= AES_GCM_FLAG
131 }
132}
133
108func (i *InformWrapper) IsCompressed() bool { 134func (i *InformWrapper) IsCompressed() bool {
109 return i.Flags&COMPRESSED_FLAG != 0 135 return i.IsZlibCompressed() || i.IsSnappyCompressed()
136}
137
138func (i *InformWrapper) IsZlibCompressed() bool {
139 return i.Flags&ZLIB_COMPRESSED_FLAG != 0
140}
141
142func (i *InformWrapper) IsSnappyCompressed() bool {
143 return i.Flags&SNAPPY_COMPRESSED_FLAG != 0
144}
145
146func (i *InformWrapper) SetSnappyCompressed(c bool) {
147 if c {
148 i.Flags |= SNAPPY_COMPRESSED_FLAG
149 } else {
150 i.Flags &= SNAPPY_COMPRESSED_FLAG
151 }
110} 152}
111 153
112func (i *InformWrapper) SetCompressed(c bool) { 154func (i *InformWrapper) SetZlibCompressed(c bool) {
113 if c { 155 if c {
114 i.Flags |= COMPRESSED_FLAG 156 i.Flags |= ZLIB_COMPRESSED_FLAG
115 } else { 157 } else {
116 i.Flags &= COMPRESSED_FLAG 158 i.Flags &= ZLIB_COMPRESSED_FLAG
117 } 159 }
118} 160}