forked from IntelRealSense/RealSenseID
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathSecureSession.cc
345 lines (296 loc) · 11.5 KB
/
SecureSession.cc
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
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
// License: Apache 2.0. See LICENSE file in root directory.
// Copyright(c) 2020-2021 Intel Corporation. All Rights Reserved.
#include "SecureSession.h"
#include "PacketSender.h"
#include "Logger.h"
#include "Randomizer.h"
#include <stdexcept>
#include <string>
#include <cassert>
static const char* LOG_TAG = "SecureSession";
static const int MAX_SEQ_NUMBER_DELTA = 20;
namespace RealSenseID
{
namespace PacketManager
{
SecureSession::SecureSession(SignCallback sign_callback, VerifyCallback verify_callback) :
_sign_callback(sign_callback), _verify_callback(verify_callback)
{
}
SecureSession::~SecureSession()
{
try
{
LOG_DEBUG(LOG_TAG, "Close session");
auto ignored = HandleCancelFlag(); //cancel if requested but not handled yet
(void)ignored;
}
catch (...)
{
}
}
SerialStatus SecureSession::Pair(SerialConnection* serial_conn, const char* ecdsaHostPubKey,
const char* ecdsaHostPubKeySig, char* ecdsaDevicePubKey)
{
LOG_INFO(LOG_TAG, "Pairing start");
return PairImpl(serial_conn, ecdsaHostPubKey, ecdsaHostPubKeySig, ecdsaDevicePubKey);
}
SerialStatus SecureSession::Unpair(SerialConnection* serial_conn)
{
// Default public key
const unsigned char hostPubKey[ECC_P256_KEY_SIZE_BYTES] = {
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff};
unsigned char hostPubKeySig[ECC_P256_KEY_SIZE_BYTES];
bool res = _sign_callback(hostPubKey, ECC_P256_KEY_SIZE_BYTES, hostPubKeySig);
if(!res)
{
LOG_ERROR(LOG_TAG, "Sign callback failed");
return SerialStatus::SecurityError;
}
char devicePubKey[ECC_P256_KEY_SIZE_BYTES];
return PairImpl(serial_conn, (char*)hostPubKey, (char*)hostPubKeySig, devicePubKey);
}
SerialStatus SecureSession::Start(SerialConnection* serial_conn)
{
LOG_DEBUG(LOG_TAG, "Start session");
_is_open = false;
_cancel_required = false;
if (serial_conn == nullptr)
{
throw std::runtime_error("SecureSession: serial connection is null");
}
_serial = serial_conn;
_last_sent_seq_number = 0;
_last_recv_seq_number = 0;
// Generate ecdh keys and get public key with signature
MbedtlsWrapper::SignCallback sign_clbk = [this](const unsigned char* buffer, const unsigned int buffer_len,
unsigned char* out_sig) {
return this->_sign_callback(buffer, buffer_len, out_sig);
};
// Generate and send our public key to device
unsigned char* signed_pubkey = _crypto_wrapper.GetSignedEcdhPubkey(sign_clbk);
if (!signed_pubkey)
{
LOG_ERROR(LOG_TAG, "Failed to generate signed ECDH public key");
return SerialStatus::SecurityError;
}
auto signed_pubkey_size = _crypto_wrapper.GetSignedEcdhPubkeySize();
DataPacket packet {MsgId::HostEcdhKey, (char*)signed_pubkey, signed_pubkey_size};
PacketSender sender {_serial};
auto status = sender.SendBinary(packet);
if (status != SerialStatus::Ok)
{
LOG_ERROR(LOG_TAG, "Failed to send ecdh data packet");
return status;
}
// Read device's key and generate shared secret
status = sender.Recv(packet);
if (status != SerialStatus::Ok)
{
LOG_ERROR(LOG_TAG, "Failed to recv device key response");
return status;
}
if (packet.header.id != MsgId::DeviceEcdhKey)
{
LOG_ERROR(LOG_TAG, "Mutual authentication failed");
return SerialStatus::SecurityError;
}
assert(IsDataPacket(packet));
// Verify the received key
MbedtlsWrapper::VerifyCallback verify_clbk = [this](const unsigned char* buffer, const unsigned int buffer_len,
const unsigned char* sig, const unsigned int sig_len) {
return _verify_callback(buffer, buffer_len, sig, sig_len);
};
auto* data_to_verify = reinterpret_cast<const unsigned char*>(packet.Data().data);
if (!_crypto_wrapper.VerifyEcdhSignedKey(data_to_verify, verify_clbk))
{
LOG_ERROR(LOG_TAG, "Verify key callback failed");
return SerialStatus::SecurityError;
}
_is_open = true;
return SerialStatus::Ok;
}
bool SecureSession::IsOpen()
{
return _is_open;
}
// Encrypt and send packet to the serial connection
SerialStatus SecureSession::SendPacket(SerialPacket& packet)
{
return SendPacketImpl(packet);
}
// Wait for any packet until timeout.
// Decrypt the packet.
// Fill the given packet with the decrypted received packet packet.
SerialStatus SecureSession::RecvPacket(SerialPacket& packet)
{
return RecvPacketImpl(packet);
}
// Receive packet, decrypt and try to convert to FaPacket
SerialStatus SecureSession::RecvFaPacket(FaPacket& packet)
{
auto status = RecvPacketImpl(packet);
if (status != SerialStatus::Ok)
{
return status;
}
return IsFaPacket(packet) ? SerialStatus::Ok : SerialStatus::RecvUnexpectedPacket;
}
// Receive packet, decrypt and try to convert to DataPacket
SerialStatus SecureSession::RecvDataPacket(DataPacket& packet)
{
auto status = RecvPacketImpl(packet);
if (status != SerialStatus::Ok)
{
return status;
}
return IsDataPacket(packet) ? SerialStatus::Ok : SerialStatus::RecvUnexpectedPacket;
}
RealSenseID::PacketManager::SerialStatus SecureSession::PairImpl(SerialConnection* serial_conn,
const char* ecdsaHostPubKey,
const char* ecdsaHostPubKeySig,
char* ecdsaDevicePubKey)
{
unsigned char ecdsaSignedHostPubKey[SIGNED_PUBKEY_SIZE];
::memset(ecdsaSignedHostPubKey, 0, sizeof(ecdsaSignedHostPubKey));
::memcpy(ecdsaSignedHostPubKey, ecdsaHostPubKey, ECC_P256_KEY_SIZE_BYTES);
::memcpy(ecdsaSignedHostPubKey + ECC_P256_KEY_SIZE_BYTES, ecdsaHostPubKeySig, ECC_P256_SIG_SIZE_BYTES);
PacketManager::DataPacket packet {PacketManager::MsgId::HostEcdsaKey, (char*)ecdsaSignedHostPubKey,
sizeof(ecdsaSignedHostPubKey)};
PacketManager::PacketSender sender {serial_conn};
auto status = sender.SendBinary(packet);
if (status != PacketManager::SerialStatus::Ok)
{
LOG_ERROR(LOG_TAG, "Failed to send ecdsa public key");
return status;
}
status = sender.Recv(packet);
if (status != PacketManager::SerialStatus::Ok)
{
LOG_ERROR(LOG_TAG, "Failed to recv device ecdsa public key");
return status;
}
if (packet.header.id != PacketManager::MsgId::DeviceEcdsaKey)
{
LOG_ERROR(LOG_TAG, "Mutual authentication failed");
return SerialStatus::SecurityError;
}
assert(IsDataPacket(packet));
::memcpy(ecdsaDevicePubKey, packet.Data().data, ECC_P256_KEY_SIZE_BYTES);
DEBUG_SERIAL(LOG_TAG, "Device Pubkey", ecdsaDevicePubKey, ECC_P256_KEY_SIZE_BYTES);
LOG_INFO(LOG_TAG, "Pairing Ok");
return SerialStatus::Ok;
}
SerialStatus SecureSession::SendPacketImpl(SerialPacket& packet)
{
// increment and set sequence number in the packet
packet.payload.sequence_number = ++_last_sent_seq_number;
// encrypt packet except for sync bytes and msg id
char* packet_ptr = (char*)&packet;
char* payload_to_encrypt = ((char*)&(packet.payload));
unsigned char temp_encrypted_data[sizeof(SerialPacket::payload)] = {0};
// randomize iv for encryption/decryption
Randomizer::Instance().GenerateRandom(packet.header.iv, sizeof(packet.header.iv));
auto ok = _crypto_wrapper.Encrypt(packet.header.iv, (unsigned char*)payload_to_encrypt, temp_encrypted_data,
packet.header.payload_size);
if (!ok)
{
LOG_ERROR(LOG_TAG, "Failed encrypting packet");
return SerialStatus::SecurityError;
}
// copy back encrypted data in the the packet payload
::memcpy(payload_to_encrypt, (char*)temp_encrypted_data, packet.header.payload_size);
int content_size = sizeof(packet.header) + packet.header.payload_size;
ok = _crypto_wrapper.CalcHmac((unsigned char*)packet_ptr, content_size, (unsigned char*)packet.hmac);
if (!ok)
{
LOG_ERROR(LOG_TAG, "Failed to calc HMAC");
return SerialStatus::SecurityError;
}
assert(_serial != nullptr);
PacketSender sender {_serial};
return sender.SendBinary(packet);
}
// new sequence number should advance by max of MAX_SEQ_NUMBER_DELTA from last number
static bool ValidateSeqNumber(uint32_t last_recv_number, uint32_t seq_number)
{
return (last_recv_number < seq_number && seq_number <= last_recv_number + MAX_SEQ_NUMBER_DELTA);
}
SerialStatus SecureSession::RecvPacketImpl(SerialPacket& packet)
{
assert(_serial != nullptr);
PacketSender sender {_serial};
// Handle cancel flag
auto status = HandleCancelFlag();
if (status != SerialStatus::Ok)
{
return status;
}
status = sender.Recv(packet);
if (status != SerialStatus::Ok)
{
return status;
}
char* packet_ptr = (char*)&packet;
// verify hmac of the received packet
int content_size = sizeof(packet.header) + packet.header.payload_size;
char hmac[HMAC_256_SIZE_BYTES];
auto ok = _crypto_wrapper.CalcHmac((unsigned char*)packet_ptr, content_size, (unsigned char*)hmac);
if (!ok)
{
LOG_ERROR(LOG_TAG, "Failed to calc HMAC");
return SerialStatus::SecurityError;
}
static_assert(sizeof(packet.hmac) == sizeof(hmac), "HMAC size mismatch");
if (::memcmp(packet.hmac, hmac, HMAC_256_SIZE_BYTES))
{
LOG_ERROR(LOG_TAG, "HMAC not the same. Packet not valid");
return SerialStatus::SecurityError;
}
// decrypt payload
char* payload_to_decrypt = ((char*)&(packet.payload));
unsigned char temp_decrypted_data[sizeof(SerialPacket::payload)] = {0};
ok = _crypto_wrapper.Decrypt(packet.header.iv, (unsigned char*)payload_to_decrypt, temp_decrypted_data,
packet.header.payload_size);
if (!ok)
{
LOG_ERROR(LOG_TAG, "Failed decrypting packet");
return SerialStatus::SecurityError;
}
// copy back encrypted data in the the packet payload
::memcpy(payload_to_decrypt, temp_decrypted_data, packet.header.payload_size);
// validate sequence number
auto current_seq = packet.payload.sequence_number;
if (!ValidateSeqNumber(_last_recv_seq_number, current_seq))
{
LOG_ERROR(LOG_TAG, "Invalid sequence number. Last: %zu, Current: %zu", _last_recv_seq_number, current_seq);
return SerialStatus::SecurityError;
}
_last_recv_seq_number = current_seq;
return SerialStatus::Ok;
}
void SecureSession::Cancel()
{
LOG_DEBUG(LOG_TAG, "Cancel requested.");
_cancel_required = true;
}
SerialStatus SecureSession::HandleCancelFlag()
{
if (!_cancel_required)
{
return SerialStatus::Ok;
}
_cancel_required = false;
if (!_serial)
{
LOG_WARNING(LOG_TAG, "Cannot send cancel, no serial connection");
return SerialStatus::SendFailed;
}
LOG_DEBUG(LOG_TAG, "Sending cancel..");
return _serial->SendBytes(Commands::face_cancel, ::strlen(Commands::face_cancel));
}
} // namespace PacketManager
} // namespace RealSenseID