• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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