• 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_filter.h"
18 
19 #include <inttypes.h>
20 
21 #include <cstddef>
22 
23 #include "location/lbs/contexthub/nanoapps/nearby/presence_decoder_v1.h"
24 #include "location/lbs/contexthub/nanoapps/nearby/presence_service_data.h"
25 #include "location/lbs/contexthub/nanoapps/nearby/proto/ble_filter.nanopb.h"
26 #include "third_party/contexthub/chre/util/include/chre/util/macros.h"
27 #include "third_party/contexthub/chre/util/include/chre/util/nanoapp/log.h"
28 
29 #define LOG_TAG "[NEARBY][PRESENCE_FILTER]"
30 
31 namespace nearby {
32 
33 constexpr int kAuthenticityKeyLength = 32;
34 constexpr int kMetaDataEncryptionTagLength = 32;
35 
addDataElementToResult(nearby_DataElement_ElementType de_type,const ByteArray & de_value,nearby_BleFilterResult * result)36 static bool addDataElementToResult(nearby_DataElement_ElementType de_type,
37                                    const ByteArray &de_value,
38                                    nearby_BleFilterResult *result) {
39   size_t de_index = result->data_element_count;
40   if (de_index >= ARRAY_SIZE(result->data_element)) {
41     LOGE("Data Elements(%u) exceed the maximum count: %zu", de_type, de_index);
42     return false;
43   }
44   if (de_value.length > ARRAY_SIZE(result->data_element[de_index].value)) {
45     LOGE("Data Element(%u) exceeds the maximum length: %zu", de_type,
46          de_value.length);
47     return false;
48   }
49   result->data_element_count++;
50   result->data_element[de_index].has_key = true;
51   result->data_element[de_index].key = de_type;
52   result->data_element[de_index].has_value = true;
53   result->data_element[de_index].has_value_length = true;
54   result->data_element[de_index].value_length =
55       static_cast<uint32_t>(de_value.length);
56   LOGD_SENSITIVE_INFO(
57       "AddDataElementToResult de_index: %zu de_type: %d de_value.length: %zu",
58       de_index, de_type, de_value.length);
59   memcpy(result->data_element[de_index].value, de_value.data, de_value.length);
60   return true;
61 }
62 
MatchFastPairInitial(const nearby_BleFilter & filter,const PresenceServiceData & service_data,nearby_BleFilterResult * result)63 bool MatchFastPairInitial(const nearby_BleFilter &filter,
64                           const PresenceServiceData &service_data,
65                           nearby_BleFilterResult *result) {
66   if (!service_data.has_fp_model_id) {
67     return false;
68   }
69   bool has_initial_pairing_filter = false;
70   for (int i = 0; i < filter.data_element_count; i++) {
71     if (filter.data_element[i].has_key &&
72         filter.data_element[i].key ==
73             nearby_DataElement_ElementType_DE_FAST_PAIR_ACCOUNT_KEY &&
74         filter.data_element[i].has_value &&
75         filter.data_element[i].has_value_length &&
76         filter.data_element[i].value_length == kFpAccountKeyLength) {
77       uint32_t value_byte_summary = 0;
78       for (int j = 0; j < kFpAccountKeyLength; j++) {
79         value_byte_summary += filter.data_element[i].value[j];
80       }
81       if (value_byte_summary == 0) {
82         has_initial_pairing_filter = true;
83         break;
84       }
85     }
86   }
87   if (has_initial_pairing_filter) {
88     size_t de_index = result->data_element_count;
89     result->data_element_count++;
90     result->data_element[de_index].has_key = true;
91     result->data_element[de_index].key =
92         nearby_DataElement_ElementType_DE_FAST_PAIR_ACCOUNT_KEY;
93     // value bytes have already been initialized to zero by default.
94     result->data_element[de_index].has_value = true;
95     result->data_element[de_index].has_value_length = true;
96     result->data_element[de_index].value_length = kFpAccountKeyLength;
97     result->has_result_type = true;
98     result->result_type = nearby_BleFilterResult_ResultType_RESULT_FAST_PAIR;
99     return true;
100   }
101   return false;
102 }
103 
MatchPresenceV0(const nearby_BleFilter & filter,const BleScanRecord & scan_record,nearby_BleFilterResult * result)104 bool MatchPresenceV0(const nearby_BleFilter &filter,
105                      const BleScanRecord &scan_record,
106                      nearby_BleFilterResult *result) {
107   chre::Optional<PresenceServiceData> presence_service_data;
108   for (const auto &ble_service_data : scan_record.service_data) {
109     if (ble_service_data.uuid == PresenceServiceData::kUuid) {
110       presence_service_data = PresenceServiceData::Parse(
111           ble_service_data.data, ble_service_data.length);
112       if (ble_service_data.length <= sizeof(result->ble_service_data)) {
113         result->has_ble_service_data = true;
114         memcpy(result->ble_service_data, ble_service_data.data,
115                ble_service_data.length);
116       } else {
117         LOGI("Received the BLE advertisement with length larger than %zu",
118              sizeof(result->ble_service_data));
119       }
120       break;
121     }
122   }
123   if (!presence_service_data.has_value()) {
124     LOGI("[MatchPresenceV0] presence_service_data is empty.");
125     return false;
126   }
127 
128   if (MatchFastPairInitial(filter, presence_service_data.value(), result)) {
129     LOGD("MatchFastPairInitial succeeded");
130     return true;
131   } else {
132     LOGD("[MatchPresenceV0] filter Presence");
133     if (filter.has_intent) {
134       if (!presence_service_data.has_value()) {
135         return false;
136       }
137       if (presence_service_data->first_intent.has_value() &&
138           filter.intent == presence_service_data->first_intent.value()) {
139         return true;
140       } else if (presence_service_data->second_intent.has_value() &&
141                  filter.intent ==
142                      presence_service_data->second_intent.value()) {
143         return true;
144       } else {
145         return false;
146       }
147     }
148     return false;
149   }
150 }
151 
MatchExtendedDE(const nearby_BleFilter & filter,const chre::DynamicVector<DataElement> & extended_des,nearby_BleFilterResult * result)152 static bool MatchExtendedDE(
153     const nearby_BleFilter &filter,
154     const chre::DynamicVector<DataElement> &extended_des,
155     nearby_BleFilterResult *result) {
156   for (int i = 0; i < filter.data_element_count; i++) {
157     // If filter is valid, at least one DE should match with this filter.
158     // Otherwise, returns failure
159     if (filter.data_element[i].has_key && filter.data_element[i].has_value &&
160         filter.data_element[i].has_value_length) {
161       bool is_matched = false;
162       for (const auto &ext_de : extended_des) {
163         if (ext_de.key == filter.data_element[i].key &&
164             ext_de.value.length == filter.data_element[i].value_length &&
165             memcmp(ext_de.value.data, filter.data_element[i].value,
166                    filter.data_element[i].value_length) == 0) {
167           is_matched = true;
168           break;
169         }
170       }
171       if (!is_matched) {
172         LOGD("Match Presence V1 Data Element failed with %" PRIi32 " type.",
173              filter.data_element[i].key);
174         return false;
175       }
176     }
177   }
178   // Passed all filters. Adds all DEs into results.
179   for (const auto &ext_de : extended_des) {
180     if (!addDataElementToResult(ext_de.key, ext_de.value, result)) {
181       return false;
182     }
183   }
184   return true;
185 }
186 
MatchPresenceV1(const nearby_BleFilter & filter,const BleScanRecord & scan_record,const Crypto & crypto,nearby_BleFilterResult * result)187 bool MatchPresenceV1(const nearby_BleFilter &filter,
188                      const BleScanRecord &scan_record, const Crypto &crypto,
189                      nearby_BleFilterResult *result) {
190   LOGD_SENSITIVE_INFO("Filter Presence V1 with %" PRIu16 " certificates",
191                       filter.certificate_count);
192   PresenceDecoderV1 decoder;
193   for (const auto &ble_service_data : scan_record.service_data) {
194     if (ble_service_data.uuid == PresenceServiceData::kUuid) {
195       for (int cert_index = 0; cert_index < filter.certificate_count;
196            cert_index++) {
197         ByteArray authenticity_key(
198             const_cast<uint8_t *>(
199                 filter.certificate[cert_index].authenticity_key),
200             kAuthenticityKeyLength);
201         LOGD_SENSITIVE_INFO("certificate metadata encryption key tag:");
202         for (size_t i = 0; i < kMetaDataEncryptionTagLength; i++) {
203           LOGD_SENSITIVE_INFO(
204               "%" PRIi8,
205               filter.certificate[cert_index].metadata_encryption_key_tag[i]);
206         }
207         ByteArray metadata_encryption_key_tag(
208             const_cast<uint8_t *>(
209                 filter.certificate[cert_index].metadata_encryption_key_tag),
210             kMetaDataEncryptionTagLength);
211         if (decoder.Decode(
212                 ByteArray(const_cast<uint8_t *>(ble_service_data.data),
213                           ble_service_data.length),
214                 crypto, authenticity_key, metadata_encryption_key_tag)) {
215           result->has_public_credential = true;
216           result->public_credential.has_encrypted_metadata_tag = true;
217           for (size_t i = 0; i < kMetaDataEncryptionTagLength; i++) {
218             result->public_credential.encrypted_metadata_tag[i] =
219                 metadata_encryption_key_tag.data[i];
220           }
221           result->public_credential.has_authenticity_key = true;
222           for (size_t i = 0; i < kAuthenticityKeyLength; i++) {
223             result->public_credential.authenticity_key[i] =
224                 authenticity_key.data[i];
225           }
226           // TODO(b/244786064): remove unused fields.
227           result->public_credential.has_secret_id = true;
228           result->public_credential.has_encrypted_metadata = true;
229           result->public_credential.has_public_key = true;
230           LOGD("Succeeded to decode Presence advertisement v1.");
231           break;
232         }
233       }
234     }
235   }
236 
237   if (!decoder.decoded) {
238     LOGD("Decode Presence V1 failed.");
239     return false;
240   }
241 
242   if (filter.has_intent) {
243     bool action_matched = false;
244     for (size_t i = 0; i < decoder.num_actions; i++) {
245       LOGD("Match filter action %" PRIu32 " with advertisement action %" PRIu8,
246            filter.intent, decoder.actions[i]);
247       if (filter.intent == decoder.actions[i]) {
248         result->has_intent = true;
249         result->intent = filter.intent;
250         action_matched = true;
251         break;
252       }
253     }
254     if (!action_matched) {
255       return false;
256     }
257   }
258 
259   if (decoder.connection_status.data != nullptr) {
260     if (!addDataElementToResult(
261             nearby_DataElement_ElementType_DE_CONNECTION_STATUS,
262             decoder.connection_status, result)) {
263       return false;
264     }
265   }
266 
267   if (decoder.battery_status.data != nullptr) {
268     if (!addDataElementToResult(
269             nearby_DataElement_ElementType_DE_BATTERY_STATUS,
270             decoder.battery_status, result)) {
271       return false;
272     }
273   }
274 
275   if (!MatchExtendedDE(filter, decoder.extended_des, result)) {
276     return false;
277   }
278 
279   result->has_result_type = true;
280   result->result_type = nearby_BleFilterResult_ResultType_RESULT_PRESENCE;
281   return true;
282 }
283 
284 }  // namespace nearby
285