-
Notifications
You must be signed in to change notification settings - Fork 1
Expand file tree
/
Copy pathhandler_adapter.go
More file actions
150 lines (115 loc) · 4.06 KB
/
Copy pathhandler_adapter.go
File metadata and controls
150 lines (115 loc) · 4.06 KB
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
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
package main
import (
"bytes"
"crypto/aes"
"encoding/hex"
"encoding/json"
"errors"
"io/ioutil"
"net/http"
"github.com/op/go-logging"
)
type Adapter struct {
log *logging.Logger
piotDevices *PiotDevices
password string
}
func NewAdapter(log *logging.Logger, piotDevices *PiotDevices, password string) *Adapter {
return &Adapter{log: log, piotDevices: piotDevices, password: password}
}
func (h *Adapter) ServeHTTP(w http.ResponseWriter, r *http.Request) {
const size = 16
h.log.Debugf("Incoming packet")
body, err := ioutil.ReadAll(r.Body)
if err != nil {
h.log.Errorf("Reading request body error: %s", err)
http.Error(w, err.Error(), http.StatusBadRequest)
return
}
bodyLen := len(body)
h.log.Debugf("Packet body length: %d", bodyLen)
// log message content in DEBUG mode
// get info of debug mode directly from logger
if h.log.IsEnabledFor(logging.DEBUG) {
reqStr := ioutil.NopCloser(bytes.NewBuffer(body))
h.log.Debugf("Request body: %s", reqStr)
r.Body = reqStr
}
// check http method, POST is required
if r.Method != http.MethodPost {
WriteErrorResponse(w, errors.New("only POST method is allowed"), http.StatusMethodNotAllowed)
return
}
// try to decode packet
var devicePacket PiotDevicePacket
if err := json.NewDecoder(r.Body).Decode(&devicePacket); err != nil {
h.log.Debugf("Raw data json decode failed, trying to decrypt")
if len(h.password) != 16 {
h.log.Error("Failed to decrypt, PIOT password not configured or doesn't have 16 chars")
WriteErrorResponse(w, errors.New("missing or wrong encryption configuration"), 500)
return
}
// body shall have length which is multiplication of cipher size (size constant)
if bodyLen%size != 0 {
h.log.Errorf("Invalid length of body for decryption %d", bodyLen)
WriteErrorResponse(w, errors.New("invalid length of body for decryption"), http.StatusBadRequest)
return
}
// json decode from raw data failed => try to decrypt first
cipher, _ := aes.NewCipher([]byte(h.password))
decrypted := make([]byte, bodyLen)
// decrypt by individual blocks
decryptedLen := 0
for bs, be := 0, size; bs < bodyLen; bs, be = bs+size, be+size {
cipher.Decrypt(decrypted[bs:be], body[bs:be])
// last block needs special attention due to padding that must be removed
// before json parsing
if bs+size == bodyLen {
// strip pkcs7 padding
stripped, err := pkcs7strip(decrypted[bs:be], size)
if err != nil {
h.log.Errorf("PKCS#7 padding stripping failed (%e)", err.Error())
WriteErrorResponse(w, errors.New("wrong PKCS#7 padding of encrypted content"), http.StatusBadRequest)
}
decryptedLen += len(stripped)
} else {
decryptedLen += size
}
}
h.log.Debugf("Decrypted message <%s>", decrypted[:decryptedLen])
h.log.Debugf("%s", hex.Dump(decrypted))
// try to decode decrypted data
if err := json.Unmarshal(decrypted[:decryptedLen], &devicePacket); err != nil {
h.log.Debugf("Decrypted data json decode failed (%s)", err.Error())
http.Error(w, err.Error(), http.StatusBadRequest)
return
}
}
h.log.Debugf("Packet decoded %v", devicePacket)
if err := h.piotDevices.ProcessPacket(devicePacket); err != nil {
http.Error(w, err.Error(), 500)
return
}
}
// pkcs7strip remove pkcs7 padding
func pkcs7strip(data []byte, blockSize int) ([]byte, error) {
length := len(data)
// no empty blocks can exist
if length == 0 {
return nil, errors.New("pkcs7: data is empty")
}
// all bytes are always filled (padding values are always non zero)
if length%blockSize != 0 {
return nil, errors.New("pkcs7: data is not block-aligned")
}
// get number of bytes used for padding from last block byte
padLen := int(data[length-1])
// generate sequence of bytes that should match end of the block
ref := bytes.Repeat([]byte{byte(padLen)}, padLen)
// check if padding is encoded correctly - it must be smaller than block size,
// non zero and match generated sequence
if padLen > blockSize || padLen == 0 || !bytes.HasSuffix(data, ref) {
return nil, errors.New("pkcs7: invalid padding")
}
return data[:length-padLen], nil
}