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