1 /*
2 * Copyright (C) 2023 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17 #include "location/lbs/contexthub/nanoapps/nearby/presence_decoder_v1.h"
18
19 #include <cinttypes>
20 #include <cstdint>
21 #include <cstdio>
22 #include <cstring>
23
24 #include "location/lbs/contexthub/nanoapps/nearby/byte_array.h"
25 #include "location/lbs/contexthub/nanoapps/nearby/crypto.h"
26 #include "location/lbs/contexthub/nanoapps/nearby/proto/ble_filter.nanopb.h"
27 #include "third_party/contexthub/chre/util/include/chre/util/nanoapp/log.h"
28 #include "third_party/contexthub/chre/util/include/chre/util/optional.h"
29 // #include "third_party/nearby/internal/platform/byte_array.h"
30
31 #define LOG_TAG "[NEARBY][PRESENCE_DECODER_V1]"
32
33 namespace nearby {
34 // The Presence v1 advertisement is defined in the format below:
35 // Header (1 byte) | Section header (1 byte) | salt (1+2 bytes) | Identity +
36 // filter (2+16 bytes) | repeated Data Element fields (various bytes), ending
37 // with MIC 16 bytes The header contains: version (3 bits) | 5 bit reserved for
38 // future use (RFU)
Decode(const ByteArray & encoded_data,const Crypto & crypto,const ByteArray & key,const ByteArray & metadata_encryption_key_tag)39 bool PresenceDecoderV1::Decode(const ByteArray &encoded_data,
40 const Crypto &crypto, const ByteArray &key,
41 const ByteArray &metadata_encryption_key_tag) {
42 LOGI("Start V1 Decoding");
43
44 // 1 + 1 + 1 + 2 + 2 + 16
45 constexpr size_t kMinAdvertisementLength = 23;
46 constexpr size_t kHeaderIndex = 0;
47 // Section header index is 1
48 constexpr size_t kSaltIndex = 2;
49 constexpr size_t kIdentityIndex = 5;
50 constexpr size_t kDataElementIndex = 23;
51 constexpr size_t kIdentityHeaderLength = 2;
52 constexpr size_t kMicLength = 16;
53
54 constexpr uint8_t kVersionMask = 0b11100000;
55 constexpr uint8_t kVersion = 1;
56
57 chre::Optional<DataElementHeaderV1> de_header;
58 uint8_t *const data = encoded_data.data;
59 const size_t data_size = encoded_data.length;
60
61 if (data_size < kMinAdvertisementLength) {
62 LOGE(
63 "Encoded advertisement does not have sufficient bytes to include "
64 "de_header, salt, and identity");
65 return false;
66 }
67 if ((data[kHeaderIndex] & kVersionMask) >> 5 != kVersion) {
68 LOGE("Advertisement version is not v1");
69 return false;
70 }
71
72 // Decodes salt.
73 de_header =
74 DataElementHeaderV1::Decode(&data[kSaltIndex], data_size - kSaltIndex);
75 if (de_header.has_value() &&
76 de_header->type == DataElementHeaderV1::kSaltType &&
77 de_header->length == DataElementHeaderV1::kSaltLength) {
78 salt[0] = data[kSaltIndex + 1];
79 salt[1] = data[kSaltIndex + 2];
80 } else {
81 LOGE("Advertisement has no valid salt.");
82 return false;
83 }
84
85 de_header = DataElementHeaderV1::Decode(&data[kIdentityIndex],
86 data_size - kIdentityIndex);
87 #ifdef LOG_INCLUDE_SENSITIVE_INFO
88 size_t identity_data_index = kIdentityIndex + kIdentityHeaderLength;
89 LOGD_SENSITIVE_INFO("encrypted identity:");
90 for (size_t i = identity_data_index;
91 i < (identity_data_index + DataElementHeaderV1::kIdentityLength); i++) {
92 LOGD_SENSITIVE_INFO("%" PRIi8, data[i]);
93 }
94 LOGD_SENSITIVE_INFO("metadata encryption key tag:");
95 for (size_t i = 0; i < metadata_encryption_key_tag.length; i++) {
96 LOGD_SENSITIVE_INFO("%" PRIi8, metadata_encryption_key_tag.data[i]);
97 }
98 LOGD_SENSITIVE_INFO("SALT [ %" PRIi8 ", %" PRIi8 "]", salt[0], salt[1]);
99 LOGD_SENSITIVE_INFO("authenticity key:");
100 for (size_t i = 0; i < key.length; i++) {
101 LOGD_SENSITIVE_INFO("%" PRIi8, key.data[i]);
102 }
103 #endif
104 if (!de_header.has_value()) {
105 LOGE("Advertisement has wrong format.");
106 return false;
107 }
108 if (de_header->type < DataElementHeaderV1::kPrivateIdentityType ||
109 de_header->type > DataElementHeaderV1::kProvisionIdentityType ||
110 de_header->length != DataElementHeaderV1::kIdentityLength) {
111 LOGE("Advertisement has no identity.");
112 return false;
113 }
114 if (data_size < kDataElementIndex + kMicLength) {
115 LOGE("Presence advertisement has wrong format.");
116 return false;
117 }
118 size_t cipher_text_index = kIdentityIndex + kIdentityHeaderLength;
119 size_t cipher_text_length = data_size - cipher_text_index - kMicLength;
120 ByteArray cipher_text(&data[cipher_text_index], cipher_text_length);
121
122 // Decodes Data Elements including identity
123 ByteArray decrypted_byte_array(decryption_output_buffer, cipher_text_length);
124 if (!crypto.decrypt(cipher_text,
125 ByteArray(salt, DataElementHeaderV1::kSaltLength), key,
126 decrypted_byte_array)) {
127 LOGE("Fail to decrypt data elements.");
128 return false;
129 }
130 memcpy(identity, decrypted_byte_array.data,
131 DataElementHeaderV1::kIdentityLength);
132 if (!crypto.verify(ByteArray(identity, sizeof(identity)), key,
133 metadata_encryption_key_tag)) {
134 LOGW("Metadata encryption key not matched.");
135 return false;
136 }
137
138 #ifdef LOG_INCLUDE_SENSITIVE_INFO
139 LOGD_SENSITIVE_INFO("decrypted identity:");
140 for (size_t i = 0; i < sizeof(identity); i++) {
141 LOGD_SENSITIVE_INFO("%" PRIi8, identity[i]);
142 }
143 #endif
144
145 if (data_size == kMinAdvertisementLength) {
146 LOGD("Presence advertisement has no data elements.");
147 return true;
148 }
149 #ifdef LOG_INCLUDE_SENSITIVE_INFO
150 LOGD_SENSITIVE_INFO(
151 "Data Elements length %d and encrypted bytes(including identity without "
152 "header):",
153 (int)decrypted_byte_array.length);
154 LOGD_SENSITIVE_INFO("Salt bytes: %" PRIi8 " %" PRIu8, salt[0], salt[1]);
155 LOGD_SENSITIVE_INFO("authenticity key:");
156 for (size_t i = 0; i < key.length; i++) {
157 LOGD_SENSITIVE_INFO("%" PRIi8, key.data[i]);
158 }
159 #endif
160
161 #ifdef LOG_INCLUDE_SENSITIVE_INFO
162 LOGD_SENSITIVE_INFO("decrypted data elements bytes:");
163 for (size_t i = 0; i < decrypted_byte_array.length; i++) {
164 LOGD_SENSITIVE_INFO("%" PRIi8, decrypted_byte_array.data[i]);
165 }
166 #endif
167 if (!DecodeDataElements(
168 &decrypted_byte_array.data[DataElementHeaderV1::kIdentityLength],
169 decrypted_byte_array.length - DataElementHeaderV1::kIdentityLength)) {
170 LOGE("Advertisement has invalid data elements.");
171 return false;
172 }
173
174 decoded = true;
175 return true;
176 }
177
DecodeDataElements(uint8_t data[],size_t data_size)178 bool PresenceDecoderV1::DecodeDataElements(uint8_t data[], size_t data_size) {
179 chre::Optional<DataElementHeaderV1> de_header;
180 num_actions = 0;
181 for (size_t index = 0; index < data_size;) {
182 de_header = DataElementHeaderV1::Decode(&data[index], data_size - index);
183 if (de_header.has_value()) {
184 switch (de_header->type) {
185 case DataElementHeaderV1::kActionType:
186 if (de_header->length != DataElementHeaderV1::kActionLength) {
187 LOGE("Advertisement has incorrect action length");
188 return false;
189 }
190 actions[num_actions] = data[index + de_header->header_length];
191 num_actions++;
192 break;
193 case DataElementHeaderV1::kTxPowerType:
194 if (de_header->length != DataElementHeaderV1::kTxPowerLength) {
195 LOGE("Advertisement has incorrect TX power length");
196 return false;
197 }
198 tx_power = ByteArray(&data[index + de_header->header_length],
199 de_header->length);
200 break;
201 case DataElementHeaderV1::kModelIdType:
202 if (de_header->length != DataElementHeaderV1::kModelIdLength) {
203 LOGE("Advertisement has incorrect model ID length");
204 return false;
205 }
206 model_id = ByteArray(&data[index + de_header->header_length],
207 de_header->length);
208 break;
209 case DataElementHeaderV1::kConnectionStatusType:
210 if (de_header->length !=
211 DataElementHeaderV1::kConnectionStatusLength) {
212 LOGE("Advertisement has incorrect connection status length");
213 return false;
214 }
215 connection_status = ByteArray(&data[index + de_header->header_length],
216 de_header->length);
217 break;
218 case DataElementHeaderV1::kBatteryStatusType:
219 if (de_header->length != DataElementHeaderV1::kBatteryStatusLength) {
220 LOGE("Advertisement has incorrect battery status length");
221 return false;
222 }
223 battery_status = ByteArray(&data[index + de_header->header_length],
224 de_header->length);
225 break;
226 default:
227 if (IsValidExtDataElementsType(de_header->type)) {
228 extended_des.push_back(DataElement(
229 static_cast<nearby_DataElement_ElementType>(de_header->type),
230 ByteArray(&data[index + de_header->header_length],
231 de_header->length)));
232 } else {
233 LOGD("Invalid DE type(%" PRIi64 ") is included", de_header->type);
234 }
235 break;
236 }
237 }
238 index = index + de_header->header_length + de_header->length;
239 }
240 return true;
241 }
242
IsValidExtDataElementsType(uint64_t type)243 bool PresenceDecoderV1::IsValidExtDataElementsType(uint64_t type) {
244 #ifdef ENABLE_TEST_DE
245 return (type >= nearby_DataElement_ElementType_DE_TEST_BEGIN &&
246 type <= nearby_DataElement_ElementType_DE_TEST_END);
247 #endif
248 return false;
249 }
250
Decode(const uint8_t data[],const size_t data_size)251 chre::Optional<DataElementHeaderV1> DataElementHeaderV1::Decode(
252 const uint8_t data[], const size_t data_size) {
253 // The bytes to define Data Element type should be less than 8.
254 constexpr size_t kTypeMaxByteLength = 8;
255 constexpr uint8_t kExtendBitMask = 0b10000000;
256 constexpr uint8_t kNoneExtendBitsMask = 0b01111111;
257 constexpr uint8_t kLengthBitsMask = 0b01110000;
258 constexpr uint8_t kTypeBitsMask = 0b00001111;
259
260 DataElementHeaderV1 header;
261
262 if (data_size == 0) {
263 LOGE("Decode Data Element header from zero byte.");
264 return chre::Optional<DataElementHeaderV1>();
265 }
266
267 // Single byte header
268 if ((data[0] & kExtendBitMask) == 0) {
269 header.length = (data[0] & kLengthBitsMask) >> 4;
270 header.type = (data[0] & kTypeBitsMask);
271 header.header_length = 1;
272 LOGD("Return single byte header with length: %" PRIu8 " and type: %" PRIu64,
273 header.length, header.type);
274 return header;
275 }
276
277 // multi-byte header
278 header.length = data[0] & kNoneExtendBitsMask;
279 header.type = 0;
280 size_t i = 1;
281 while (true) {
282 if (i > kTypeMaxByteLength) {
283 LOGE("Type exceeds the maximum byte length: %zu", kTypeMaxByteLength);
284 return chre::Optional<DataElementHeaderV1>();
285 }
286 if (i >= data_size) {
287 LOGE("Extended byte exceeds the data size.");
288 return chre::Optional<DataElementHeaderV1>();
289 }
290 header.type = (header.type << 7) | (data[i] & kNoneExtendBitsMask);
291 if ((data[i] & kExtendBitMask) == 0) {
292 break;
293 } else {
294 i++;
295 }
296 }
297 header.header_length = static_cast<uint8_t>(i + 1);
298 LOGD("Return multi byte header with length: %" PRIu8 " and type: %" PRIu64,
299 header.length, header.type);
300 return header;
301 }
302
303 } // namespace nearby
304