Project import generated by Copybara.
GitOrigin-RevId: 5028dd6db990e351e6ce4b988267b60f3ac785d7
Change-Id: I981c26f33d52d775acde707d87df1c10a602e26f
diff --git a/nearby/Cargo.lock b/nearby/Cargo.lock
index 7441c1b..657ac34 100644
--- a/nearby/Cargo.lock
+++ b/nearby/Cargo.lock
@@ -279,6 +279,7 @@
"globset",
"log",
"owo-colors",
+ "regex",
"semver",
"serde_json",
"shell-escape",
@@ -1022,7 +1023,6 @@
version = "0.1.0"
dependencies = [
"criterion",
- "crypto_provider",
"lazy_static",
"lock_adapter",
]
@@ -1338,9 +1338,9 @@
[[package]]
name = "memchr"
-version = "2.5.0"
+version = "2.6.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "2dffe52ecf27772e601905b7522cb4ef790d2cc203488bbd0e2fe85fcb74566d"
+checksum = "f665ee40bc4a3c5590afb1e9677db74a508659dfd71e126420da8274909a0167"
[[package]]
name = "memoffset"
@@ -1861,9 +1861,9 @@
[[package]]
name = "regex"
-version = "1.9.1"
+version = "1.10.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "b2eae68fc220f7cf2532e4494aded17545fce192d59cd996e0fe7887f4ceb575"
+checksum = "380b951a9c5e80ddfd6136919eef32310721aa4aacd4889a8d39124b026ab343"
dependencies = [
"aho-corasick",
"memchr",
@@ -1873,9 +1873,9 @@
[[package]]
name = "regex-automata"
-version = "0.3.4"
+version = "0.4.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "b7b6d6190b7594385f61bd3911cd1be99dfddcfc365a4160cc2ab5bff4aed294"
+checksum = "5f804c7828047e88b2d32e2d7fe5a105da8ee3264f01902f796c8e067dc2483f"
dependencies = [
"aho-corasick",
"memchr",
@@ -1884,9 +1884,9 @@
[[package]]
name = "regex-syntax"
-version = "0.7.4"
+version = "0.8.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "e5ea92a5b6195c6ef2a0295ea818b312502c6fc94dde986c5553242e18fd4ce2"
+checksum = "c08c74e62047bb2de4ff487b251e4a92e24f48745648451635cec7d591162d9f"
[[package]]
name = "reword"
diff --git a/nearby/Cargo.toml b/nearby/Cargo.toml
index 1416e24..df92f64 100644
--- a/nearby/Cargo.toml
+++ b/nearby/Cargo.toml
@@ -171,6 +171,7 @@
which = "4.4.0"
file-header = "0.1.2"
serde_json.workspace = true
+regex = "1.10.2"
[dev-dependencies]
tempfile.workspace = true
diff --git a/nearby/presence/np_adv/Cargo.toml b/nearby/presence/np_adv/Cargo.toml
index 5123737..97e12f5 100644
--- a/nearby/presence/np_adv/Cargo.toml
+++ b/nearby/presence/np_adv/Cargo.toml
@@ -14,7 +14,7 @@
crypto_provider.workspace = true
strum.workspace = true
strum_macros.workspace = true
-nom = { version = "7.1.3", default-features = false, features = ["alloc"] }
+nom = { version = "7.1.3", default-features = false }
lazy_static.workspace = true
sink.workspace = true
tinyvec.workspace = true
diff --git a/nearby/presence/np_adv/src/array_vec.rs b/nearby/presence/np_adv/src/array_vec.rs
new file mode 100644
index 0000000..a92ff5b
--- /dev/null
+++ b/nearby/presence/np_adv/src/array_vec.rs
@@ -0,0 +1,203 @@
+// Copyright 2023 Google LLC
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+//! Provides an [`ArrayVecOption`] wrapper implementation over [`ArrayVec`],
+//! which stores all the elements in an `Option` in order to satisfy
+//! `ArrayVec`'s requirement that the elements must implement `Default`.
+
+use tinyvec::{ArrayVec, ArrayVecIterator};
+#[cfg(any(test, feature = "alloc"))]
+extern crate alloc;
+#[cfg(any(test, feature = "alloc"))]
+use alloc::vec::Vec;
+
+/// A wrapper of [`ArrayVec`] that stores it values as [`Option`], in order to
+/// satisfy `ArrayVec`'s requirement that the elements must implement `Default`.
+/// The implementation guarantees that any items in the wrapped `ArrayVec`
+/// within `0..len` is `Some`, and therefore will not panic when unwrapped.
+#[derive(Debug, Clone, PartialEq, Eq)]
+pub struct ArrayVecOption<A, const N: usize>(ArrayVec<[Option<A>; N]>);
+
+// Cannot derive due to https://github.com/rust-lang/rust/issues/74462
+impl<A, const N: usize> Default for ArrayVecOption<A, N> {
+ fn default() -> Self {
+ Self(Default::default())
+ }
+}
+
+/// Iterator type returned by `ArrayVecOption.iter()`, which can be used as
+/// `impl Iterator<Item = &A>`
+pub type ArrayVecOptionRefIter<'a, A> =
+ core::iter::Map<core::slice::Iter<'a, Option<A>>, fn(&'a Option<A>) -> &'a A>;
+
+/// Iterator type returned by `ArrayVecOption.into_iter()`, which can be used as
+/// `impl Iterator<Item = A>`
+pub type ArrayVecOptionIntoIter<A, const N: usize> =
+ core::iter::Map<ArrayVecIterator<[Option<A>; N]>, fn(Option<A>) -> A>;
+
+impl<A, const N: usize> ArrayVecOption<A, N> {
+ /// Returns an iterator over this vec.
+ pub fn iter(&self) -> ArrayVecOptionRefIter<A> {
+ self.0
+ .iter()
+ .map(|v| v.as_ref().expect("ArrayVecOption value before .len() should not be None"))
+ }
+
+ /// The length of the vec (in elements).
+ pub fn len(&self) -> usize {
+ self.0.len()
+ }
+
+ /// Checks if the length is 0.
+ pub fn is_empty(&self) -> bool {
+ self.0.is_empty()
+ }
+
+ /// Returns the first element of the vec, or `None` if it is empty.
+ pub fn first(&self) -> Option<&A> {
+ self.iter().next()
+ }
+
+ /// Place an element onto the end of the vec.
+ ///
+ /// # Panics
+ /// * If the length of the vec would overflow the capacity.
+ pub fn push(&mut self, value: A) {
+ self.0.push(Some(value))
+ }
+
+ /// Returns a reference to an element at the given index.
+ pub fn get(&self, index: usize) -> Option<&A> {
+ self.0.get(index).and_then(|opt| opt.as_ref())
+ }
+
+ /// Sorts the slice with a key extraction function, but might not preserve
+ /// the order of equal elements.
+ ///
+ /// This sort is unstable (i.e., may reorder equal elements), in-place
+ /// (i.e., does not allocate), and *O*(*m* \* *n* \* log(*n*)) worst-case,
+ /// where the key function is *O*(*m*).
+ pub fn sort_unstable_by_key<K: Ord>(&mut self, f: impl Fn(&A) -> K) {
+ self.0.sort_unstable_by_key(|a| f(a.as_ref().unwrap()))
+ }
+
+ /// Remove an element, swapping the end of the vec into its place.
+ pub fn swap_remove(&mut self, index: usize) -> A {
+ self.0.swap_remove(index).unwrap()
+ }
+
+ /// Converts this vector into a regular `Vec`, unwrapping all of the
+ /// `Option` in the process.
+ #[cfg(any(test, feature = "alloc"))]
+ pub fn into_vec(self) -> Vec<A> {
+ self.into_iter().collect()
+ }
+}
+
+impl<A, const N: usize> IntoIterator for ArrayVecOption<A, N> {
+ type Item = A;
+ type IntoIter = ArrayVecOptionIntoIter<A, N>;
+ fn into_iter(self) -> Self::IntoIter {
+ self.0
+ .into_iter()
+ .map(|v| v.expect("ArrayVecOption value before .len() should not be None"))
+ }
+}
+
+// Implement `FromIterator` to enable `iter.collect::<ArrayVecOption<_>>()`
+impl<A, const N: usize> FromIterator<A> for ArrayVecOption<A, N> {
+ fn from_iter<T: IntoIterator<Item = A>>(iter: T) -> Self {
+ Self(iter.into_iter().map(Some).collect())
+ }
+}
+
+impl<A, const N: usize> core::ops::Index<usize> for ArrayVecOption<A, N> {
+ type Output = A;
+ fn index(&self, index: usize) -> &Self::Output {
+ self.0[index].as_ref().unwrap()
+ }
+}
+
+#[cfg(test)]
+mod test {
+ extern crate std;
+ use super::ArrayVecOption;
+ use std::vec;
+
+ #[test]
+ fn test_default_array_vec() {
+ let vec = ArrayVecOption::<u32, 5>::default();
+ assert_eq!(0, vec.len());
+ assert_eq!(None, vec.iter().next());
+ assert!(vec.is_empty());
+ assert_eq!(None, vec.first());
+ assert_eq!(None, vec.get(0));
+ assert_eq!(vec![0_u32; 0], vec.into_vec());
+ }
+
+ #[test]
+ fn test_array_vec_with_contents() {
+ let mut vec = ArrayVecOption::<u32, 5>::default();
+ vec.push(1);
+ vec.push(2);
+ vec.push(3);
+ vec.push(4);
+ vec.push(5);
+ assert_eq!(5, vec.len());
+ let mut iter = vec.iter();
+ assert_eq!(Some(&1_u32), iter.next());
+ assert_eq!(Some(&2_u32), iter.next());
+ assert_eq!(Some(&3_u32), iter.next());
+ assert_eq!(Some(&4_u32), iter.next());
+ assert_eq!(Some(&5_u32), iter.next());
+ assert_eq!(None, iter.next());
+ assert!(!vec.is_empty());
+ assert_eq!(Some(&1_u32), vec.first());
+ assert_eq!(Some(&5_u32), vec.get(4));
+ assert_eq!(vec![1_u32, 2, 3, 4, 5], vec.clone().into_vec());
+
+ vec.swap_remove(2);
+ assert_eq!(vec![1_u32, 2, 5, 4], vec.clone().into_vec());
+ }
+
+ #[test]
+ #[should_panic]
+ fn test_array_vec_push_overflow() {
+ let mut vec = ArrayVecOption::<u32, 5>::default();
+ vec.push(1);
+ vec.push(2);
+ vec.push(3);
+ vec.push(4);
+ vec.push(5);
+ vec.push(6);
+ }
+
+ #[test]
+ fn test_sort() {
+ let mut vec = ArrayVecOption::<u32, 5>::default();
+ vec.push(3);
+ vec.push(1);
+ vec.push(4);
+ vec.push(1);
+ vec.push(2);
+ vec.sort_unstable_by_key(|k| *k);
+ assert_eq!(vec![1_u32, 1, 2, 3, 4], vec.clone().into_vec());
+ }
+
+ #[test]
+ fn test_collect() {
+ let vec: ArrayVecOption<u32, 5> = [5_u32, 4, 3, 2, 1].into_iter().collect();
+ assert_eq!(vec![5_u32, 4, 3, 2, 1], vec.clone().into_vec());
+ }
+}
diff --git a/nearby/presence/np_adv/src/credential/book.rs b/nearby/presence/np_adv/src/credential/book.rs
index 9e6361d..902d55a 100644
--- a/nearby/presence/np_adv/src/credential/book.rs
+++ b/nearby/presence/np_adv/src/credential/book.rs
@@ -35,6 +35,8 @@
use crypto_provider::CryptoProvider;
#[cfg(feature = "alloc")]
+extern crate alloc;
+#[cfg(feature = "alloc")]
use alloc::vec::Vec;
/// A collection of credentials to try when attempting to deserialize
diff --git a/nearby/presence/np_adv/src/credential/tests.rs b/nearby/presence/np_adv/src/credential/tests.rs
index 297fdb5..8b12bfc 100644
--- a/nearby/presence/np_adv/src/credential/tests.rs
+++ b/nearby/presence/np_adv/src/credential/tests.rs
@@ -13,6 +13,8 @@
//! Tests of functionality related to credentials, credential-views, and credential suppliers.
+extern crate alloc;
+
use crate::credential::{
book::{
init_cache_from_source, CachedCredentialSource, PossiblyCachedDiscoveryCryptoMaterialKind,
diff --git a/nearby/presence/np_adv/src/deser_v0_tests.rs b/nearby/presence/np_adv/src/deser_v0_tests.rs
index 2fb6f38..93c3b97 100644
--- a/nearby/presence/np_adv/src/deser_v0_tests.rs
+++ b/nearby/presence/np_adv/src/deser_v0_tests.rs
@@ -38,7 +38,7 @@
HasIdentityMatch, PlaintextIdentityMode, PublicIdentity, V0AdvertisementContents,
};
use array_view::ArrayView;
-use core::{borrow::BorrowMut, marker::PhantomData};
+use core::marker::PhantomData;
use crypto_provider::CryptoProvider;
use crypto_provider_default::CryptoProviderImpl;
use ldt_np_adv::LegacySalt;
@@ -61,10 +61,10 @@
})
.collect::<Vec<_>>();
- let mut arena = deserialization_arena!();
+ let arena = deserialization_arena!();
let cred_book =
CredentialBookBuilder::build_cached_slice_book::<0, 0, CryptoProviderImpl>(&creds, &[]);
- let contents = deser_v0::<_, CryptoProviderImpl>(&mut arena, adv.as_slice(), &cred_book);
+ let contents = deser_v0::<_, CryptoProviderImpl>(arena, adv.as_slice(), &cred_book);
assert_adv_equals(&adv_config, &contents);
}
@@ -90,12 +90,12 @@
})
.collect::<Vec<_>>();
- let mut arena = deserialization_arena!();
+ let arena = deserialization_arena!();
let cred_book = CredentialBookBuilder::build_cached_slice_book::<0, 0, CryptoProviderImpl>(
&credentials,
&[],
);
- let contents = deser_v0::<_, CryptoProviderImpl>(&mut arena, adv.as_slice(), &cred_book);
+ let contents = deser_v0::<_, CryptoProviderImpl>(arena, adv.as_slice(), &cred_book);
match adv_config.identity {
// we ended up generating plaintext, so it's fine
@@ -118,10 +118,10 @@
let creds = Vec::<MatchableCredential<V0, EmptyMatchedCredential>>::new();
- let mut arena = deserialization_arena!();
+ let arena = deserialization_arena!();
let cred_book =
CredentialBookBuilder::build_cached_slice_book::<0, 0, CryptoProviderImpl>(&creds, &[]);
- let contents = deser_v0::<_, CryptoProviderImpl>(&mut arena, adv.as_slice(), &cred_book);
+ let contents = deser_v0::<_, CryptoProviderImpl>(arena, adv.as_slice(), &cred_book);
match adv_config.identity {
// we ended up generating plaintext, so it's fine
@@ -150,8 +150,8 @@
assert_eq!(adv_config.plaintext_mode.unwrap(), p.identity());
assert_eq!(
- vec![&PlainDataElement::Actions(de)],
- p.data_elements().collect::<Vec<_>>()
+ vec![PlainDataElement::Actions(de)],
+ p.data_elements().collect::<Result<Vec<_>, _>>().unwrap()
)
}
_ => panic!("should be a plaintext adv"),
@@ -166,8 +166,8 @@
let de = ActionsDataElement::from(action_bits);
assert_eq!(
- vec![&PlainDataElement::Actions(de)],
- wmc.contents().data_elements().collect::<Vec<_>>()
+ vec![PlainDataElement::Actions(de)],
+ wmc.contents().data_elements().collect::<Result<Vec<_>, _>>().unwrap()
);
assert_eq!(
adv_config.identity.unwrap().identity_type,
@@ -184,10 +184,10 @@
}
fn deser_v0<'adv, B, P>(
- arena: impl BorrowMut<DeserializationArena<'adv>>,
+ arena: DeserializationArena<'adv>,
adv: &'adv [u8],
cred_book: &'adv B,
-) -> V0AdvertisementContents<B::Matched>
+) -> V0AdvertisementContents<'adv, B::Matched>
where
B: CredentialBook<'adv>,
P: CryptoProvider,
diff --git a/nearby/presence/np_adv/src/deser_v1_tests.rs b/nearby/presence/np_adv/src/deser_v1_tests.rs
index 80f646e..903ed3d 100644
--- a/nearby/presence/np_adv/src/deser_v1_tests.rs
+++ b/nearby/presence/np_adv/src/deser_v1_tests.rs
@@ -68,12 +68,12 @@
})
.collect::<Vec<_>>();
- let mut arena = deserialization_arena!();
+ let arena = deserialization_arena!();
// check if the section is empty or there is more than one public section
let cred_book =
CredentialBookBuilder::build_cached_slice_book::<0, 0, CryptoProviderImpl>(&[], &creds);
if section_configs.is_empty() {
- let v1_error = deser_v1_error::<_, CryptoProviderImpl>(&mut arena, &adv, &cred_book);
+ let v1_error = deser_v1_error::<_, CryptoProviderImpl>(arena, &adv, &cred_book);
assert_eq!(
v1_error,
AdvDeserializationError::ParseError {
@@ -82,7 +82,7 @@
}
); //assert a adv deserialization error
} else {
- let v1_contents = deser_v1::<_, CryptoProviderImpl>(&mut arena, &adv, &cred_book);
+ let v1_contents = deser_v1::<_, CryptoProviderImpl>(arena, &adv, &cred_book);
assert_eq!(0, v1_contents.invalid_sections_count());
assert_eq!(section_configs.len(), v1_contents.sections.len());
for (section_config, section) in section_configs.iter().zip(v1_contents.sections.iter())
@@ -111,12 +111,12 @@
match_data: EmptyMatchedCredential,
})
.collect::<Vec<_>>();
- let mut arena = deserialization_arena!();
+ let arena = deserialization_arena!();
// check if the section header would be 0 or if the section is empty
let cred_book =
CredentialBookBuilder::build_cached_slice_book::<0, 0, CryptoProviderImpl>(&[], &creds);
if section_configs.is_empty() {
- let v1_error = deser_v1_error::<_, CryptoProviderImpl>(&mut arena, &adv, &cred_book);
+ let v1_error = deser_v1_error::<_, CryptoProviderImpl>(arena, &adv, &cred_book);
assert_eq!(
v1_error,
AdvDeserializationError::ParseError {
@@ -125,7 +125,7 @@
}
); //assert a adv deserialization error
} else {
- let v1_contents = deser_v1::<_, CryptoProviderImpl>(&mut arena, &adv, &cred_book);
+ let v1_contents = deser_v1::<_, CryptoProviderImpl>(arena, &adv, &cred_book);
assert_eq!(0, v1_contents.invalid_sections_count());
assert_eq!(section_configs.len(), v1_contents.sections.len());
for (section_config, section) in section_configs.iter().zip(v1_contents.sections.iter())
@@ -161,12 +161,12 @@
})
.collect::<Vec<_>>();
- let mut arena = deserialization_arena!();
+ let arena = deserialization_arena!();
// check if the section header would be 0
let cred_book =
CredentialBookBuilder::build_cached_slice_book::<0, 0, CryptoProviderImpl>(&[], &creds);
if section_configs.is_empty() {
- let v1_error = deser_v1_error::<_, CryptoProviderImpl>(&mut arena, &adv, &cred_book);
+ let v1_error = deser_v1_error::<_, CryptoProviderImpl>(arena, &adv, &cred_book);
assert_eq!(
v1_error,
AdvDeserializationError::ParseError {
@@ -175,7 +175,7 @@
}
); //assert a adv deserialization error
} else {
- let v1_contents = deser_v1::<_, CryptoProviderImpl>(&mut arena, &adv, &cred_book);
+ let v1_contents = deser_v1::<_, CryptoProviderImpl>(arena, &adv, &cred_book);
// all encrypted identity sections are invalid
let encrypted_section_count =
section_configs.iter().filter(|sc| sc.identity.is_some()).count();
@@ -206,7 +206,7 @@
&mut adv_builder,
);
let adv = adv_builder.into_advertisement();
- let mut arena = deserialization_arena!();
+ let arena = deserialization_arena!();
// check if the section header would be 0
let cred_book = CredentialBookBuilder::<EmptyMatchedCredential>::build_cached_slice_book::<
0,
@@ -214,7 +214,7 @@
CryptoProviderImpl,
>(&[], &[]);
if section_configs.is_empty() {
- let v1_error = deser_v1_error::<_, CryptoProviderImpl>(&mut arena, &adv, &cred_book);
+ let v1_error = deser_v1_error::<_, CryptoProviderImpl>(arena, &adv, &cred_book);
assert_eq!(
v1_error,
AdvDeserializationError::ParseError {
@@ -223,7 +223,7 @@
}
); //assert a adv deserialization error
} else {
- let v1_contents = deser_v1::<_, CryptoProviderImpl>(&mut arena, &adv, &cred_book);
+ let v1_contents = deser_v1::<_, CryptoProviderImpl>(arena, &adv, &cred_book);
// all encrypted identity sections are invalid
let encrypted_section_count =
section_configs.iter().filter(|sc| sc.identity.is_some()).count();
@@ -276,14 +276,14 @@
})
.collect::<Vec<_>>();
- let mut arena = deserialization_arena!();
+ let arena = deserialization_arena!();
let cred_book =
CredentialBookBuilder::build_cached_slice_book::<0, 0, CryptoProviderImpl>(&[], &creds);
// check if the section header would be 0
if section_configs.is_empty() {
- let v1_error = deser_v1_error::<_, CryptoProviderImpl>(&mut arena, &adv, &cred_book);
+ let v1_error = deser_v1_error::<_, CryptoProviderImpl>(arena, &adv, &cred_book);
assert_eq!(
v1_error,
AdvDeserializationError::ParseError {
@@ -292,7 +292,7 @@
}
); //assert a adv deserialization error
} else {
- let v1_contents = deser_v1::<_, CryptoProviderImpl>(&mut arena, &adv, &cred_book);
+ let v1_contents = deser_v1::<_, CryptoProviderImpl>(arena, &adv, &cred_book);
let affected_sections: Vec<_> = section_configs
.iter()
@@ -367,7 +367,7 @@
}
fn deser_v1_error<'a, B, P>(
- arena: &'a mut DeserializationArena<'a>,
+ arena: DeserializationArena<'a>,
adv: &'a EncodedAdvertisement,
cred_book: &'a B,
) -> AdvDeserializationError
@@ -383,7 +383,7 @@
}
fn deser_v1<'adv, B, P>(
- arena: &'adv mut DeserializationArena<'adv>,
+ arena: DeserializationArena<'adv>,
adv: &'adv EncodedAdvertisement,
cred_book: &'adv B,
) -> V1AdvertisementContents<'adv, B::Matched>
diff --git a/nearby/presence/np_adv/src/deserialization_arena.rs b/nearby/presence/np_adv/src/deserialization_arena.rs
index 6b5f651..9b58cbf 100644
--- a/nearby/presence/np_adv/src/deserialization_arena.rs
+++ b/nearby/presence/np_adv/src/deserialization_arena.rs
@@ -27,24 +27,19 @@
// Reference: https://doc.rust-lang.org/reference/destructors.html#temporary-lifetime-extension
() => {
$crate::deserialization_arena::DeserializationArena {
- slice: &mut $crate::deserialization_arena::DeserializationArena::buffer(),
+ buffer: &mut $crate::deserialization_arena::DeserializationArena::new_buffer(),
}
};
}
/// A simple allocator that simply keeps splitting the given slice on allocation. Use the
/// [`deserialization_arena!`][crate::deserialization_arena!] macro to create an instance.
-pub struct DeserializationArena<'a> {
- #[doc(hidden)] // Exposed for use by `deserialization_arena!` only.
- pub slice: &'a mut [u8],
+pub(crate) struct DeserializationArenaAllocator<'a> {
+ #[doc(hidden)]
+ slice: &'a mut [u8],
}
-impl<'a> DeserializationArena<'a> {
- #[doc(hidden)] // Exposed for use by `deserialization_arena!` only.
- pub fn buffer() -> [u8; BLE_ADV_SVC_CONTENT_LEN] {
- [0; BLE_ADV_SVC_CONTENT_LEN]
- }
-
+impl<'a> DeserializationArenaAllocator<'a> {
/// Allocates `len` bytes from the slice given upon construction. In the expected use case, the
/// allocated slice should be written to with actual data, overwriting what's contained in
/// there. While reading from the allocated slice without first writing to it is safe in the
@@ -59,17 +54,36 @@
}
let (allocated, remaining) = core::mem::take(&mut self.slice).split_at_mut(len.into());
self.slice = remaining;
- // Note: the returned data is logically garbage, but in practice it's all zeroes assuming
- // `deserialization_arena!` was used to create this allocator.
+ // Note: the returned data is logically garbage. While it's deterministic (not UB),
+ // semantically this should be treated as a write only slice.
Ok(allocated)
}
}
+/// A simple allocator that simply keeps splitting the given slice on allocation. Use the
+/// [`deserialization_arena!`][crate::deserialization_arena!] macro to create an instance.
+pub struct DeserializationArena<'a> {
+ #[doc(hidden)] // Exposed for use by `deserialization_arena!` only.
+ pub buffer: &'a mut [u8; BLE_ADV_SVC_CONTENT_LEN],
+}
+
+impl<'a> DeserializationArena<'a> {
+ #[doc(hidden)] // Exposed for use by `deserialization_arena!` only.
+ pub fn new_buffer() -> [u8; BLE_ADV_SVC_CONTENT_LEN] {
+ [0; BLE_ADV_SVC_CONTENT_LEN]
+ }
+
+ /// Convert this arena into an allocator that can start allocating.
+ pub(crate) fn into_allocator(self) -> DeserializationArenaAllocator<'a> {
+ DeserializationArenaAllocator { slice: self.buffer }
+ }
+}
+
/// Error indicating that the arena has ran out of space, and deserialization cannot proceed. This
/// should never happen if the arena is created with [`crate::deserialization_arena!`], since the
/// total size of decrypted sections should be less than the size of the incoming BLE advertisement.
#[derive(Debug, PartialEq, Eq)]
-pub struct ArenaOutOfSpace;
+pub(crate) struct ArenaOutOfSpace;
#[cfg(test)]
mod test {
@@ -77,26 +91,29 @@
#[test]
fn test_creation() {
- assert_eq!(BLE_ADV_SVC_CONTENT_LEN, deserialization_arena!().slice.len());
+ assert_eq!(BLE_ADV_SVC_CONTENT_LEN, deserialization_arena!().buffer.len());
}
#[test]
fn test_allocation() {
- let mut arena = deserialization_arena!();
- assert_eq!(Ok(&mut [0_u8; 100][..]), arena.allocate(100));
- assert_eq!(BLE_ADV_SVC_CONTENT_LEN - 100, arena.slice.len());
+ let arena = deserialization_arena!();
+ let mut allocator = arena.into_allocator();
+ assert_eq!(Ok(&mut [0_u8; 100][..]), allocator.allocate(100));
+ assert_eq!(BLE_ADV_SVC_CONTENT_LEN - 100, allocator.slice.len());
}
#[test]
fn test_allocation_overflow() {
- let mut arena = deserialization_arena!();
- assert_eq!(Err(ArenaOutOfSpace), arena.allocate(u8::MAX));
+ let arena = deserialization_arena!();
+ let mut allocator = arena.into_allocator();
+ assert_eq!(Err(ArenaOutOfSpace), allocator.allocate(u8::MAX));
}
#[test]
fn test_allocation_twice_overflow() {
- let mut arena = deserialization_arena!();
- assert_eq!(Ok(&mut [0_u8; 128][..]), arena.allocate(128));
- assert_eq!(Err(ArenaOutOfSpace), arena.allocate(128));
+ let arena = deserialization_arena!();
+ let mut allocator = arena.into_allocator();
+ assert_eq!(Ok(&mut [0_u8; 128][..]), allocator.allocate(128));
+ assert_eq!(Err(ArenaOutOfSpace), allocator.allocate(128));
}
}
diff --git a/nearby/presence/np_adv/src/extended/deserialize/encrypted_section/mic_decrypt_tests.rs b/nearby/presence/np_adv/src/extended/deserialize/encrypted_section/mic_decrypt_tests.rs
index dfab7ce..9bd54b8 100644
--- a/nearby/presence/np_adv/src/extended/deserialize/encrypted_section/mic_decrypt_tests.rs
+++ b/nearby/presence/np_adv/src/extended/deserialize/encrypted_section/mic_decrypt_tests.rs
@@ -99,10 +99,11 @@
let verification_material =
discovery_credential.unsigned_verification_material::<CryptoProviderImpl>();
- let mut arena = deserialization_arena!();
+ let arena = deserialization_arena!();
+ let mut allocator = arena.into_allocator();
let section = contents
.try_resolve_identity_and_deserialize::<CryptoProviderImpl>(
- &mut arena,
+ &mut allocator,
&identity_resolution_material,
&verification_material,
)
@@ -179,10 +180,11 @@
&identity_resolution_material.into_raw_resolution_material(),
)
.unwrap();
- let mut arena = deserialization_arena!();
+ let arena = deserialization_arena!();
+ let mut allocator = arena.into_allocator();
let decrypted = contents
.contents
- .decrypt_ciphertext::<CryptoProviderImpl>(&mut arena, identity_match)
+ .decrypt_ciphertext::<CryptoProviderImpl>(&mut allocator, identity_match)
.unwrap();
let mut expected = Vec::new();
@@ -366,7 +368,7 @@
error,
contents
.try_resolve_identity_and_deserialize::<C>(
- &mut deserialization_arena!(),
+ &mut deserialization_arena!().into_allocator(),
&identity_resolution_material,
&verification_material,
)
@@ -447,7 +449,7 @@
discovery_credential.unsigned_verification_material::<CryptoProviderImpl>();
match contents.try_resolve_identity_and_deserialize::<CryptoProviderImpl>(
- &mut deserialization_arena!(),
+ &mut deserialization_arena!().into_allocator(),
&identity_resolution_material,
&verification_material,
) {
diff --git a/nearby/presence/np_adv/src/extended/deserialize/encrypted_section/mod.rs b/nearby/presence/np_adv/src/extended/deserialize/encrypted_section/mod.rs
index 0afeb08..2236630 100644
--- a/nearby/presence/np_adv/src/extended/deserialize/encrypted_section/mod.rs
+++ b/nearby/presence/np_adv/src/extended/deserialize/encrypted_section/mod.rs
@@ -14,7 +14,7 @@
use crate::{
credential::v1::*,
- deserialization_arena::DeserializationArena,
+ deserialization_arena::DeserializationArenaAllocator,
extended::{
deserialize::{
DecryptedSection, EncryptedIdentityMetadata, EncryptionInfo, SectionContents,
@@ -27,6 +27,8 @@
};
#[cfg(any(feature = "devtools", test))]
+extern crate alloc;
+#[cfg(any(feature = "devtools", test))]
use alloc::vec::Vec;
#[cfg(feature = "devtools")]
use array_view::ArrayView;
@@ -189,7 +191,7 @@
/// and returns the raw bytes of the decrypted plaintext.
pub(crate) fn decrypt_ciphertext<C: CryptoProvider>(
&self,
- arena: &mut DeserializationArena<'a>,
+ arena: &mut DeserializationArenaAllocator<'a>,
mut identity_match: IdentityMatch<C>,
) -> Result<RawDecryptedSection<'a>, ArenaOutOfSpace> {
// Fill decrypt_buf with the ciphertext after the metadata key
@@ -213,12 +215,12 @@
#[cfg(feature = "devtools")]
pub(crate) fn try_resolve_identity_and_decrypt<P: CryptoProvider>(
&self,
- arena: &mut DeserializationArena<'a>,
+ allocator: &mut DeserializationArenaAllocator<'a>,
identity_resolution_material: &SectionIdentityResolutionMaterial,
) -> Option<Result<ArrayView<u8, NP_ADV_MAX_SECTION_LEN>, ArenaOutOfSpace>> {
let identity_resolution_contents = self.compute_identity_resolution_contents::<P>();
identity_resolution_contents.try_match(identity_resolution_material).map(|identity_match| {
- let decrypted_section = self.decrypt_ciphertext::<P>(arena, identity_match)?;
+ let decrypted_section = self.decrypt_ciphertext::<P>(allocator, identity_match)?;
Ok(decrypted_section.to_raw_bytes())
})
}
@@ -235,7 +237,7 @@
/// with some paired verification material for the matched identity.
pub(crate) fn try_deserialize<P>(
&self,
- arena: &mut DeserializationArena<'a>,
+ arena: &mut DeserializationArenaAllocator<'a>,
identity_match: IdentityMatch<P>,
verification_material: &SignedSectionVerificationMaterial,
) -> Result<DecryptedSection<'a>, DeserializationError<SignatureVerificationError>>
@@ -294,11 +296,11 @@
#[cfg(feature = "devtools")]
pub(crate) fn try_resolve_identity_and_decrypt<P: CryptoProvider>(
&self,
- arena: &mut DeserializationArena<'a>,
+ allocator: &mut DeserializationArenaAllocator<'a>,
identity_resolution_material: &SignedSectionIdentityResolutionMaterial,
) -> Option<Result<ArrayView<u8, NP_ADV_MAX_SECTION_LEN>, ArenaOutOfSpace>> {
self.contents.try_resolve_identity_and_decrypt::<P>(
- arena,
+ allocator,
identity_resolution_material.as_raw_resolution_material(),
)
}
@@ -307,7 +309,7 @@
#[cfg(test)]
pub(crate) fn try_resolve_identity_and_deserialize<P: CryptoProvider>(
&self,
- arena: &mut DeserializationArena<'a>,
+ allocator: &mut DeserializationArenaAllocator<'a>,
identity_resolution_material: &SignedSectionIdentityResolutionMaterial,
verification_material: &SignedSectionVerificationMaterial,
) -> Result<
@@ -320,7 +322,7 @@
.try_match::<P>(identity_resolution_material.as_raw_resolution_material())
{
Some(identity_match) => self
- .try_deserialize(arena, identity_match, verification_material)
+ .try_deserialize(allocator, identity_match, verification_material)
.map_err(|e| e.into()),
None => Err(IdentityResolutionOrDeserializationError::IdentityMatchingError),
}
@@ -413,14 +415,14 @@
/// Returns an error if the credential is incorrect or if the section data is malformed.
pub(crate) fn try_deserialize<P>(
&self,
- arena: &mut DeserializationArena<'a>,
+ allocator: &mut DeserializationArenaAllocator<'a>,
identity_match: IdentityMatch<P>,
verification_material: &UnsignedSectionVerificationMaterial,
) -> Result<DecryptedSection<'a>, DeserializationError<MicVerificationError>>
where
P: CryptoProvider,
{
- let raw_decrypted = self.contents.decrypt_ciphertext(arena, identity_match)?;
+ let raw_decrypted = self.contents.decrypt_ciphertext(allocator, identity_match)?;
let metadata_key = raw_decrypted.metadata_key_plaintext;
let nonce = raw_decrypted.nonce;
let remaining_des = raw_decrypted.plaintext_contents;
@@ -455,11 +457,11 @@
#[cfg(feature = "devtools")]
pub(crate) fn try_resolve_identity_and_decrypt<P: CryptoProvider>(
&self,
- arena: &mut DeserializationArena<'a>,
+ allocator: &mut DeserializationArenaAllocator<'a>,
identity_resolution_material: &UnsignedSectionIdentityResolutionMaterial,
) -> Option<Result<ArrayView<u8, NP_ADV_MAX_SECTION_LEN>, ArenaOutOfSpace>> {
self.contents.try_resolve_identity_and_decrypt::<P>(
- arena,
+ allocator,
identity_resolution_material.as_raw_resolution_material(),
)
}
@@ -468,7 +470,7 @@
#[cfg(test)]
pub(crate) fn try_resolve_identity_and_deserialize<P: CryptoProvider>(
&self,
- arena: &mut DeserializationArena<'a>,
+ allocator: &mut DeserializationArenaAllocator<'a>,
identity_resolution_material: &UnsignedSectionIdentityResolutionMaterial,
verification_material: &UnsignedSectionVerificationMaterial,
) -> Result<DecryptedSection, IdentityResolutionOrDeserializationError<MicVerificationError>>
@@ -479,7 +481,7 @@
.try_match::<P>(identity_resolution_material.as_raw_resolution_material())
{
Some(identity_match) => self
- .try_deserialize(arena, identity_match, verification_material)
+ .try_deserialize(allocator, identity_match, verification_material)
.map_err(|e| e.into()),
None => Err(IdentityResolutionOrDeserializationError::IdentityMatchingError),
}
diff --git a/nearby/presence/np_adv/src/extended/deserialize/encrypted_section/signature_decrypt_tests.rs b/nearby/presence/np_adv/src/extended/deserialize/encrypted_section/signature_decrypt_tests.rs
index 10fc46b..2370aeb 100644
--- a/nearby/presence/np_adv/src/extended/deserialize/encrypted_section/signature_decrypt_tests.rs
+++ b/nearby/presence/np_adv/src/extended/deserialize/encrypted_section/signature_decrypt_tests.rs
@@ -143,8 +143,10 @@
)
.unwrap();
- let mut arena = deserialization_arena!();
- let decrypted = contents.contents.decrypt_ciphertext(&mut arena, identity_match).unwrap();
+ let arena = deserialization_arena!();
+ let mut allocator = arena.into_allocator();
+ let decrypted =
+ contents.contents.decrypt_ciphertext(&mut allocator, identity_match).unwrap();
let mut expected = Vec::new();
expected.extend_from_slice(txpower_de.de_header().serialize().as_slice());
@@ -186,10 +188,11 @@
let signed_verification_material =
crypto_material.signed_verification_material::<CryptoProviderImpl>();
- let mut arena = deserialization_arena!();
+ let arena = deserialization_arena!();
+ let mut allocator = arena.into_allocator();
let section = contents
.try_resolve_identity_and_deserialize::<CryptoProviderImpl>(
- &mut arena,
+ &mut allocator,
&signed_identity_resolution_material,
&signed_verification_material,
)
@@ -424,7 +427,7 @@
error,
contents
.try_resolve_identity_and_deserialize::<C>(
- &mut deserialization_arena!(),
+ &mut deserialization_arena!().into_allocator(),
&signed_identity_resolution_material,
&signed_verification_material,
)
@@ -533,7 +536,7 @@
crypto_material.signed_verification_material::<CryptoProviderImpl>();
match contents.try_resolve_identity_and_deserialize::<CryptoProviderImpl>(
- &mut deserialization_arena!(),
+ &mut deserialization_arena!().into_allocator(),
&identity_resolution_material,
&verification_material,
) {
diff --git a/nearby/presence/np_adv/src/extended/deserialize/mod.rs b/nearby/presence/np_adv/src/extended/deserialize/mod.rs
index d431325..4b78a32 100644
--- a/nearby/presence/np_adv/src/extended/deserialize/mod.rs
+++ b/nearby/presence/np_adv/src/extended/deserialize/mod.rs
@@ -14,8 +14,10 @@
//! Deserialization for V1 advertisement contents
+#[cfg(any(test, feature = "alloc"))]
extern crate alloc;
+#[cfg(any(test, feature = "alloc"))]
use alloc::vec::Vec;
use core::array::TryFromSliceError;
@@ -27,12 +29,13 @@
use crypto_provider::CryptoProvider;
use np_hkdf::v1_salt::{self, V1Salt};
+use crate::array_vec::ArrayVecOption;
#[cfg(any(feature = "devtools", test))]
use crate::credential::v1::V1DiscoveryCryptoMaterial;
use crate::credential::v1::V1;
use crate::deserialization_arena::ArenaOutOfSpace;
#[cfg(any(feature = "devtools", test))]
-use crate::deserialization_arena::DeserializationArena;
+use crate::deserialization_arena::DeserializationArenaAllocator;
#[cfg(test)]
use crate::extended::deserialize::encrypted_section::IdentityResolutionOrDeserializationError;
use crate::extended::{NP_V1_ADV_MAX_ENCRYPTED_SECTION_COUNT, NP_V1_ADV_MAX_PUBLIC_SECTION_COUNT};
@@ -66,19 +69,32 @@
pub(crate) fn parse_sections(
adv_header: V1Header,
adv_body: &[u8],
-) -> Result<Vec<IntermediateSection>, nom::Err<error::Error<&[u8]>>> {
+) -> Result<
+ ArrayVecOption<IntermediateSection, NP_V1_ADV_MAX_ENCRYPTED_SECTION_COUNT>,
+ nom::Err<error::Error<&[u8]>>,
+> {
combinator::all_consuming(branch::alt((
// Public advertisement
- multi::many_m_n(
+ multi::fold_many_m_n(
1,
NP_V1_ADV_MAX_PUBLIC_SECTION_COUNT,
IntermediateSection::parser_public_section(),
+ ArrayVecOption::default,
+ |mut acc, item| {
+ acc.push(item);
+ acc
+ },
),
// Encrypted advertisement
- multi::many_m_n(
+ multi::fold_many_m_n(
1,
NP_V1_ADV_MAX_ENCRYPTED_SECTION_COUNT,
IntermediateSection::parser_encrypted_with_header(adv_header),
+ ArrayVecOption::default,
+ |mut acc, item| {
+ acc.push(item);
+ acc
+ },
),
)))(adv_body)
.map(|(_rem, sections)| sections)
@@ -395,7 +411,7 @@
#[cfg(test)]
pub(crate) fn try_resolve_identity_and_deserialize<C, P>(
&'a self,
- arena: &mut DeserializationArena<'a>,
+ allocator: &mut DeserializationArenaAllocator<'a>,
crypto_material: &C,
) -> Result<DecryptedSection<'a>, SectionDeserializeError>
where
@@ -410,7 +426,7 @@
contents
.try_resolve_identity_and_deserialize::<P>(
- arena,
+ allocator,
&identity_resolution_material,
&verification_material,
)
@@ -423,7 +439,7 @@
contents
.try_resolve_identity_and_deserialize::<P>(
- arena,
+ allocator,
&identity_resolution_material,
&verification_material,
)
@@ -439,19 +455,19 @@
P: CryptoProvider,
>(
&self,
- arena: &mut DeserializationArena<'a>,
+ allocator: &mut DeserializationArenaAllocator<'a>,
crypto_material: &C,
) -> Option<Result<ArrayView<u8, NP_ADV_MAX_SECTION_LEN>, ArenaOutOfSpace>> {
match self {
CiphertextSection::SignatureEncryptedIdentity(x) => {
let identity_resolution_material =
crypto_material.signed_identity_resolution_material::<P>();
- x.try_resolve_identity_and_decrypt::<P>(arena, &identity_resolution_material)
+ x.try_resolve_identity_and_decrypt::<P>(allocator, &identity_resolution_material)
}
CiphertextSection::MicEncryptedIdentity(x) => {
let identity_resolution_material =
crypto_material.unsigned_identity_resolution_material::<P>();
- x.try_resolve_identity_and_decrypt::<P>(arena, &identity_resolution_material)
+ x.try_resolve_identity_and_decrypt::<P>(allocator, &identity_resolution_material)
}
}
}
diff --git a/nearby/presence/np_adv/src/extended/deserialize/parse_tests.rs b/nearby/presence/np_adv/src/extended/deserialize/parse_tests.rs
index 506a127..4afa3ec 100644
--- a/nearby/presence/np_adv/src/extended/deserialize/parse_tests.rs
+++ b/nearby/presence/np_adv/src/extended/deserialize/parse_tests.rs
@@ -36,7 +36,8 @@
// de 2 byte header, type 6, len 1
adv_body.extend_from_slice(&[0x81, 0x06, 0x01]);
- let parsed_sections = parse_sections(V1Header { header_byte: 0x20 }, &adv_body).unwrap();
+ let parsed_sections =
+ parse_sections(V1Header { header_byte: 0x20 }, &adv_body).unwrap().into_vec();
assert_eq!(
vec![IntermediateSection::from(PlaintextSection::new(
PlaintextIdentityMode::Public,
@@ -168,7 +169,7 @@
},
];
let parsed_sections = parse_sections(adv_header, &adv_body).unwrap();
- assert_eq!(parsed_sections, &expected_sections.map(IntermediateSection::from));
+ assert_eq!(parsed_sections.into_vec(), expected_sections.map(IntermediateSection::from));
}
#[test]
@@ -296,7 +297,7 @@
},
];
let parsed_sections = parse_sections(adv_header, &adv_body).unwrap();
- assert_eq!(parsed_sections, &expected_sections.map(IntermediateSection::from));
+ assert_eq!(parsed_sections.into_vec(), &expected_sections.map(IntermediateSection::from));
}
#[test]
diff --git a/nearby/presence/np_adv/src/extended/deserialize/section_tests.rs b/nearby/presence/np_adv/src/extended/deserialize/section_tests.rs
index 39644ab..5b1290f 100644
--- a/nearby/presence/np_adv/src/extended/deserialize/section_tests.rs
+++ b/nearby/presence/np_adv/src/extended/deserialize/section_tests.rs
@@ -16,6 +16,7 @@
use super::*;
use crate::deserialization_arena;
+use crate::deserialization_arena::DeserializationArena;
use crate::extended::serialize::AdvertisementType;
use crate::extended::NP_V1_ADV_MAX_PUBLIC_SECTION_COUNT;
use crate::{
@@ -36,7 +37,7 @@
},
parse_adv_header, AdvHeader, WithMatchedCredential,
};
-use core::borrow::{Borrow, BorrowMut};
+use core::borrow::Borrow;
use core::convert::Into;
use crypto_provider::{CryptoProvider, CryptoRng};
use crypto_provider_default::CryptoProviderImpl;
@@ -569,7 +570,7 @@
/// - `Ok(None)` if no matching credential was found, or if `cred_source` provides no credentials
/// - `Err` if an error occurred.
fn try_deserialize_all_creds<'a, S, P>(
- mut arena: impl BorrowMut<DeserializationArena<'a>>,
+ arena: DeserializationArena<'a>,
section: &'a CiphertextSection,
cred_source: &'a S,
) -> Result<TryDeserOutput<'a, S::Matched>, BatchSectionDeserializeError>
@@ -577,11 +578,11 @@
S: DiscoveryCredentialSource<'a, V1>,
P: CryptoProvider,
{
+ let mut allocator = arena.into_allocator();
for (crypto_material, match_data) in cred_source.iter() {
- match section.try_resolve_identity_and_deserialize::<_, P>(
- arena.borrow_mut(),
- crypto_material.borrow(),
- ) {
+ match section
+ .try_resolve_identity_and_deserialize::<_, P>(&mut allocator, crypto_material.borrow())
+ {
Ok(s) => {
let metadata_nonce = crypto_material.metadata_nonce::<P>();
return Ok(Some(WithMatchedCredential::new(match_data, metadata_nonce, s)));
diff --git a/nearby/presence/np_adv/src/filter/mod.rs b/nearby/presence/np_adv/src/filter/mod.rs
index cb020cf..a2b2d9c 100644
--- a/nearby/presence/np_adv/src/filter/mod.rs
+++ b/nearby/presence/np_adv/src/filter/mod.rs
@@ -17,6 +17,7 @@
//! it matched with. Used as a first pass option to quickly check if a buffer should
//! further processed.
use crate::credential::MatchedCredential;
+use crate::legacy::data_elements::DataElementDeserializeError;
use crate::legacy::deserialize::DecryptedAdvContents;
use crate::{
credential::{book::CredentialBook, v0::V0DiscoveryCryptoMaterial},
@@ -157,7 +158,7 @@
IntermediateAdvContents::Plaintext(p) => match self.identity {
IdentityFilterType::Public | IdentityFilterType::Any => self
.data_elements
- .match_v0_legible_adv(p.data_elements())
+ .match_v0_legible_adv(|| p.data_elements())
.map(|()| FilterResult::Public),
_ => Err(NoMatch),
},
@@ -165,7 +166,7 @@
IdentityFilterType::Private | IdentityFilterType::Any => {
let (legible_adv, m) = try_decrypt_and_match::<B, P>(cred_book.v0_iter(), &c)?;
self.data_elements
- .match_v0_legible_adv(legible_adv.data_elements())
+ .match_v0_legible_adv(|| legible_adv.data_elements())
.map(|()| FilterResult::Private(m))
}
_ => Err(NoMatch),
@@ -174,12 +175,12 @@
}
}
-fn try_decrypt_and_match<'a, B, P>(
+fn try_decrypt_and_match<'cred, B, P>(
v0_creds: B::V0Iterator,
adv: &EncryptedAdvContents,
) -> Result<(DecryptedAdvContents, B::Matched), NoMatch>
where
- B: CredentialBook<'a>,
+ B: CredentialBook<'cred>,
P: CryptoProvider,
{
for (crypto_material, m) in v0_creds {
@@ -199,14 +200,15 @@
impl V0DataElementsFilter {
/// A legible adv is either plaintext to begin with, or decrypted contents from an encrypted adv
- fn match_v0_legible_adv<'a, F: PacketFlavor + 'a>(
- &self,
- mut data_elements: impl ExactSizeIterator<Item = &'a PlainDataElement<F>>,
- ) -> Result<(), NoMatch> {
+ fn match_v0_legible_adv<F, I>(&self, data_elements: impl Fn() -> I) -> Result<(), NoMatch>
+ where
+ F: PacketFlavor,
+ I: Iterator<Item = Result<PlainDataElement<F>, DataElementDeserializeError>>,
+ {
match &self.contains_tx_power {
None => Ok(()),
Some(c) => {
- if c == &data_elements.any(|de| matches!(de, PlainDataElement::TxPower(_))) {
+ if c == &data_elements().any(|de| matches!(de, Ok(PlainDataElement::TxPower(_)))) {
Ok(())
} else {
Err(NoMatch)
@@ -217,13 +219,16 @@
match &self.actions_filter {
None => Ok(()),
Some(filter) => {
+ if let Some(_err) = data_elements().find_map(|result| result.err()) {
+ return Err(NoMatch);
+ }
// find if an actions DE exists, if so match on the provided action filter
- let actions = data_elements.find_map(|de| match de {
- PlainDataElement::Actions(actions) => Some(actions),
+ let actions = data_elements().find_map(|de| match de {
+ Ok(PlainDataElement::Actions(actions)) => Some(actions),
_ => None,
});
if let Some(actions) = actions {
- filter.match_v0_actions(actions)
+ filter.match_v0_actions(&actions)
} else {
return Err(NoMatch);
}
diff --git a/nearby/presence/np_adv/src/filter/tests/data_elements_filter_tests.rs b/nearby/presence/np_adv/src/filter/tests/data_elements_filter_tests.rs
index cb7c067..4a893a2 100644
--- a/nearby/presence/np_adv/src/filter/tests/data_elements_filter_tests.rs
+++ b/nearby/presence/np_adv/src/filter/tests/data_elements_filter_tests.rs
@@ -23,16 +23,17 @@
let filter = V0DataElementsFilter { contains_tx_power: Some(true), actions_filter: None };
let tx_power = TxPower::try_from(5).expect("within range");
- let tx_power_de = TxPowerDataElement::from(tx_power);
- let result =
- filter.match_v0_legible_adv([PlainDataElement::<Ciphertext>::TxPower(tx_power_de)].iter());
+ let result = filter.match_v0_legible_adv(|| {
+ [Ok(PlainDataElement::<Ciphertext>::TxPower(TxPowerDataElement::from(tx_power.clone())))]
+ .into_iter()
+ });
assert_eq!(result, Ok(()))
}
#[test]
fn match_not_contains_tx_power() {
let filter = V0DataElementsFilter { contains_tx_power: Some(true), actions_filter: None };
- let result = filter.match_v0_legible_adv::<Plaintext>([].iter());
+ let result = filter.match_v0_legible_adv::<Plaintext, _>(|| [].into_iter());
assert_eq!(result, Err(NoMatch))
}
@@ -42,9 +43,10 @@
.expect("1 is a valid length");
let filter = V0DataElementsFilter { contains_tx_power: None, actions_filter: Some(filter) };
let tx_power = TxPower::try_from(5).expect("within range");
- let tx_power_de = TxPowerDataElement::from(tx_power);
- let result =
- filter.match_v0_legible_adv([PlainDataElement::<Ciphertext>::TxPower(tx_power_de)].iter());
+ let result = filter.match_v0_legible_adv(|| {
+ [Ok(PlainDataElement::<Ciphertext>::TxPower(TxPowerDataElement::from(tx_power.clone())))]
+ .into_iter()
+ });
assert_eq!(result, Err(NoMatch))
}
@@ -54,18 +56,17 @@
.expect("1 is a valid length");
let filter = V0DataElementsFilter { contains_tx_power: None, actions_filter: Some(filter) };
let tx_power = TxPower::try_from(5).expect("within range");
- let tx_power_de = TxPowerDataElement::from(tx_power);
let mut action_bits = ActionBits::<Ciphertext>::default();
action_bits.set_action(ActiveUnlock::from(true));
- let result = filter.match_v0_legible_adv(
+ let result = filter.match_v0_legible_adv(|| {
[
- PlainDataElement::<Ciphertext>::TxPower(tx_power_de),
- PlainDataElement::Actions(action_bits.into()),
+ Ok(PlainDataElement::<Ciphertext>::TxPower(TxPowerDataElement::from(tx_power.clone()))),
+ Ok(PlainDataElement::Actions(action_bits.into())),
]
- .iter(),
- );
+ .into_iter()
+ });
assert_eq!(result, Ok(()))
}
@@ -76,18 +77,17 @@
let filter =
V0DataElementsFilter { contains_tx_power: Some(true), actions_filter: Some(filter) };
let tx_power = TxPower::try_from(5).expect("within range");
- let tx_power_de = TxPowerDataElement::from(tx_power);
let mut action_bits = ActionBits::<Ciphertext>::default();
action_bits.set_action(ActiveUnlock::from(true));
- let result = filter.match_v0_legible_adv(
+ let result = filter.match_v0_legible_adv(|| {
[
- PlainDataElement::<Ciphertext>::TxPower(tx_power_de),
- PlainDataElement::Actions(action_bits.into()),
+ Ok(PlainDataElement::<Ciphertext>::TxPower(TxPowerDataElement::from(tx_power.clone()))),
+ Ok(PlainDataElement::Actions(action_bits.into())),
]
- .iter(),
- );
+ .into_iter()
+ });
assert_eq!(result, Ok(()))
}
@@ -98,9 +98,10 @@
let filter =
V0DataElementsFilter { contains_tx_power: Some(true), actions_filter: Some(filter) };
let tx_power = TxPower::try_from(5).expect("within range");
- let tx_power_de = TxPowerDataElement::from(tx_power);
- let result =
- filter.match_v0_legible_adv([PlainDataElement::<Ciphertext>::TxPower(tx_power_de)].iter());
+ let result = filter.match_v0_legible_adv(|| {
+ [Ok(PlainDataElement::<Ciphertext>::TxPower(TxPowerDataElement::from(tx_power.clone())))]
+ .into_iter()
+ });
assert_eq!(result, Err(NoMatch))
}
diff --git a/nearby/presence/np_adv/src/legacy/data_elements.rs b/nearby/presence/np_adv/src/legacy/data_elements.rs
index 505fdd9..c97cb25 100644
--- a/nearby/presence/np_adv/src/legacy/data_elements.rs
+++ b/nearby/presence/np_adv/src/legacy/data_elements.rs
@@ -13,6 +13,8 @@
// limitations under the License.
//! V0 data elements and core trait impls.
+use nom::error::{ErrorKind, FromExternalError};
+
use crate::legacy::{
de_type::{DataElementType, PlainDataElementType},
serialize::{DataElementBundle, ToDataElementBundle},
@@ -53,6 +55,37 @@
/// The DE type attempting to be deserialized
de_type: DataElementType,
},
+ /// Only one identity data element is allowed in an advertisement, but a duplicate is found
+ /// while parsing.
+ DuplicateIdentityDataElement,
+ /// There is unexpected data remaining in the incoming payload.
+ UnexpectedDataRemaining,
+ /// Parsing error returned from Nom.
+ NomError(nom::error::ErrorKind),
+}
+
+impl FromExternalError<&[u8], DataElementDeserializeError> for DataElementDeserializeError {
+ fn from_external_error(
+ _input: &[u8],
+ _kind: ErrorKind,
+ e: DataElementDeserializeError,
+ ) -> Self {
+ e
+ }
+}
+
+impl nom::error::ParseError<&[u8]> for DataElementDeserializeError {
+ /// Creates an error from the input position and an [ErrorKind]
+ fn from_error_kind(_input: &[u8], kind: ErrorKind) -> Self {
+ Self::NomError(kind)
+ }
+
+ /// Combines an existing error with a new one created from the input
+ /// position and an [ErrorKind]. This is useful when backtracking
+ /// through a parse tree, accumulating error context on the way
+ fn append(_input: &[u8], kind: ErrorKind, _other: Self) -> Self {
+ Self::NomError(kind)
+ }
}
/// Data element holding a [TxPower].
diff --git a/nearby/presence/np_adv/src/legacy/deserialize/mod.rs b/nearby/presence/np_adv/src/legacy/deserialize/mod.rs
index 48555be..938045d 100644
--- a/nearby/presence/np_adv/src/legacy/deserialize/mod.rs
+++ b/nearby/presence/np_adv/src/legacy/deserialize/mod.rs
@@ -16,7 +16,7 @@
//!
//! This module only deals with the _contents_ of an advertisement, not the advertisement header.
-extern crate alloc;
+use core::marker::PhantomData;
use crate::{
credential::v0::V0,
@@ -29,10 +29,12 @@
},
HasIdentityMatch, PlaintextIdentityMode,
};
-use alloc::vec::Vec;
+use array_view::ArrayView;
use crypto_provider::CryptoProvider;
use ldt_np_adv::{LegacySalt, NP_LEGACY_METADATA_KEY_LEN};
-use nom::{bytes, combinator, multi, number, sequence};
+use nom::{bytes, combinator, number, sequence};
+
+use super::BLE_ADV_SVC_CONTENT_LEN;
#[cfg(test)]
mod tests;
@@ -41,16 +43,14 @@
/// ciphertext.
pub(crate) fn deserialize_adv_contents<C: CryptoProvider>(
input: &[u8],
-) -> Result<IntermediateAdvContents, AdvDeserializeError> {
+) -> Result<IntermediateAdvContents<'_>, AdvDeserializeError> {
parse_raw_adv_contents::<C>(input).and_then(|raw_adv| match raw_adv {
- RawAdvertisement::Plaintext(parc) => {
- if parc.data_elements.is_empty() {
+ RawAdvertisement::Plaintext(adv_contents) => {
+ if adv_contents.data_elements().next().is_none() {
return Err(AdvDeserializeError::NoPublicDataElements);
}
- parc.try_deserialize()
- .map(IntermediateAdvContents::Plaintext)
- .map_err(AdvDeserializeError::DataElementDeserializeError)
+ Ok(IntermediateAdvContents::Plaintext(adv_contents))
}
RawAdvertisement::Ciphertext(eac) => Ok(IntermediateAdvContents::Ciphertext(eac)),
})
@@ -63,50 +63,56 @@
fn parse_raw_adv_contents<C: CryptoProvider>(
input: &[u8],
) -> Result<RawAdvertisement, AdvDeserializeError> {
- let (_, data_elements) = parse_data_elements(input)
- .map_err(|_e| AdvDeserializeError::AdvertisementDeserializeError)?;
-
- if let Some(identity_de_type) =
- data_elements.first().and_then(|de| de.de_type.try_as_identity_de_type())
- {
- match identity_de_type.as_encrypted_identity_de_type() {
- Some(encrypted_de_type) => {
- if data_elements.len() == 1 {
- match encrypted_de_type {
- // TODO handle length=0 provisioned identity DEs
- EncryptedIdentityDataElementType::Private
- | EncryptedIdentityDataElementType::Trusted
- | EncryptedIdentityDataElementType::Provisioned => {
- combinator::map(
- parse_encrypted_identity_de_contents,
- |(salt, payload)| {
- RawAdvertisement::Ciphertext(EncryptedAdvContents {
- identity_type: encrypted_de_type,
- salt_padder: ldt_np_adv::salt_padder::<16, C>(salt),
- salt,
- ciphertext: payload,
- })
- },
- )(data_elements[0].contents)
- .map(|(_rem, contents)| contents)
- .map_err(|_e| AdvDeserializeError::AdvertisementDeserializeError)
+ if input.is_empty() {
+ return Err(AdvDeserializeError::MissingIdentity);
+ }
+ match parse_de(input) {
+ Ok((rem, identity_de)) => {
+ if let Some(identity_de_type) = identity_de.de_type.try_as_identity_de_type() {
+ match identity_de_type.as_encrypted_identity_de_type() {
+ Some(encrypted_de_type) => {
+ if matches!(parse_de(rem), Err(nom::Err::Error(..))) {
+ match encrypted_de_type {
+ // TODO handle length=0 provisioned identity DEs
+ EncryptedIdentityDataElementType::Private
+ | EncryptedIdentityDataElementType::Trusted
+ | EncryptedIdentityDataElementType::Provisioned => combinator::map(
+ parse_encrypted_identity_de_contents,
+ |(salt, payload)| {
+ RawAdvertisement::Ciphertext(EncryptedAdvContents {
+ identity_type: encrypted_de_type,
+ salt_padder: ldt_np_adv::salt_padder::<16, C>(salt),
+ salt,
+ ciphertext: payload,
+ })
+ },
+ )(
+ identity_de.contents,
+ )
+ .map(|(_rem, contents)| contents)
+ .map_err(|_e| AdvDeserializeError::AdvertisementDeserializeError),
+ }
+ } else {
+ Err(AdvDeserializeError::TooManyTopLevelDataElements)
}
}
- } else {
- Err(AdvDeserializeError::TooManyTopLevelDataElements)
+ // It's an identity de, but not encrypted, so it must be public, and the rest
+ // must be plain
+ None => Ok(RawAdvertisement::Plaintext(PlaintextAdvContents {
+ identity_type: PlaintextIdentityMode::Public,
+ data: rem,
+ })),
}
+ } else {
+ Err(AdvDeserializeError::MissingIdentity)
}
- // It's an identity de, but not encrypted, so it must be public, and the rest must be
- // plain
- None => plain_data_elements(&data_elements[1..]).map(|pdes| {
- RawAdvertisement::Plaintext(PlaintextAdvRawContents {
- identity_type: PlaintextIdentityMode::Public,
- data_elements: pdes,
- })
- }),
}
- } else {
- Err(AdvDeserializeError::MissingIdentity)
+ Err(nom::Err::Error(_)) | Err(nom::Err::Failure(_)) => {
+ Err(AdvDeserializeError::AdvertisementDeserializeError)
+ }
+ Err(nom::Err::Incomplete(_)) => {
+ panic!("Should not hit Incomplete when using nom::complete parsers")
+ }
}
}
@@ -115,27 +121,16 @@
pub(crate) enum AdvDeserializeError {
/// Parsing the overall advertisement or DE structure failed
AdvertisementDeserializeError,
- /// Deserializing an individual DE from its DE contents failed
- DataElementDeserializeError(DataElementDeserializeError),
/// Must not have any other top level data elements if there is an encrypted identity DE
TooManyTopLevelDataElements,
- /// Must not have an identity DE inside an identity DE
- InvalidDataElementHierarchy,
/// Missing identity DE
MissingIdentity,
/// Non-identity DE contents must not be empty
NoPublicDataElements,
}
-/// Parse an advertisement's contents into raw DEs.
-///
-/// Consumes the entire input.
-fn parse_data_elements(adv_contents: &[u8]) -> nom::IResult<&[u8], Vec<RawDataElement>> {
- combinator::all_consuming(multi::many0(parse_de))(adv_contents)
-}
-
/// Parse an individual DE into its header and contents.
-fn parse_de(input: &[u8]) -> nom::IResult<&[u8], RawDataElement> {
+fn parse_de(input: &[u8]) -> nom::IResult<&[u8], RawDataElement, DataElementDeserializeError> {
let (remaining, (de_type, actual_len)) =
combinator::map_opt(number::complete::u8, |de_header| {
// header: LLLLTTTT
@@ -159,22 +154,6 @@
})(remaining)
}
-/// Returns `Err`` if any DEs are not of a plain DE type.
-fn plain_data_elements<'d, D: AsRef<[RawDataElement<'d>]>>(
- data_elements: D,
-) -> Result<Vec<RawPlainDataElement<'d>>, AdvDeserializeError> {
- data_elements
- .as_ref()
- .iter()
- .map(|de| {
- de.de_type
- .try_as_plain_de_type()
- .map(|de_type| RawPlainDataElement { de_type, contents: de.contents })
- })
- .collect::<Option<Vec<_>>>()
- .ok_or(AdvDeserializeError::InvalidDataElementHierarchy)
-}
-
/// Parse legacy encrypted identity DEs (private, trusted, provisioned) into salt and ciphertext
/// (encrypted metadata key and at least 2 bytes of DEs).
///
@@ -199,6 +178,7 @@
#[derive(Debug, PartialEq, Eq)]
struct RawDataElement<'d> {
de_type: DataElementType,
+ /// Byte array payload of the data element, without the DE header.
contents: &'d [u8],
}
@@ -206,30 +186,76 @@
/// level DE representations.
#[derive(Debug, PartialEq, Eq)]
enum RawAdvertisement<'d> {
- Plaintext(PlaintextAdvRawContents<'d>),
+ Plaintext(PlaintextAdvContents<'d>),
Ciphertext(EncryptedAdvContents<'d>),
}
-/// A plaintext advertisement's content in raw DEs but without further deserialization.
-#[derive(Debug, PartialEq, Eq)]
-struct PlaintextAdvRawContents<'d> {
- identity_type: PlaintextIdentityMode,
- data_elements: Vec<RawPlainDataElement<'d>>,
+/// An iterator that parses the given data elements iteratively. In environments
+/// where memory is not severely constrained, it is usually safer to collect
+/// this into `Result<Vec<PlainDataElement>>` so the validity of the whole
+/// advertisement can be checked before proceeding with further processing.
+#[derive(Clone, Debug, PartialEq, Eq)]
+pub struct PlainDeIterator<'d, F>
+where
+ F: PacketFlavor,
+ actions::ActionsDataElement<F>: DataElement,
+{
+ /// Data to be parsed, containing a sequence of data elements in serialized
+ /// form. This should not contain the identity data elements.
+ data: &'d [u8],
+ _marker: PhantomData<F>,
}
-impl<'d> PlaintextAdvRawContents<'d> {
- /// Deserialize the DE contents into per-DE-type structures.
- ///
- /// Returns `Some` if each DE's contents can be successfully deserialized, otherwise `None`.
- fn try_deserialize(&self) -> Result<PlaintextAdvContents, DataElementDeserializeError> {
- self.data_elements
- .iter()
- .map(|de| de.try_deserialize::<Plaintext>())
- .collect::<Result<Vec<_>, _>>()
- .map(|des| PlaintextAdvContents {
- identity_type: self.identity_type,
- data_elements: des,
- })
+impl<'d, F> PlainDeIterator<'d, F>
+where
+ F: PacketFlavor,
+ actions::ActionsDataElement<F>: DataElement,
+{
+ fn raw_de_to_plain_de(
+ raw_de: RawDataElement<'d>,
+ ) -> Result<PlainDataElement<F>, DataElementDeserializeError> {
+ let de_type = raw_de
+ .de_type
+ .try_as_plain_de_type()
+ .ok_or(DataElementDeserializeError::DuplicateIdentityDataElement)?;
+ (RawPlainDataElement { de_type, contents: raw_de.contents }).try_deserialize()
+ }
+}
+
+impl<'d, F> Iterator for PlainDeIterator<'d, F>
+where
+ F: PacketFlavor,
+ actions::ActionsDataElement<F>: DataElement,
+{
+ type Item = Result<PlainDataElement<F>, DataElementDeserializeError>;
+
+ fn next(&mut self) -> Option<Self::Item> {
+ let parse_result = nom::combinator::cut(nom::combinator::map_res(
+ parse_de,
+ Self::raw_de_to_plain_de,
+ ))(self.data);
+ match parse_result {
+ Ok((rem, de)) => {
+ self.data = rem;
+ Some(Ok(de))
+ }
+ Err(nom::Err::Error(_)) => {
+ panic!("All Errors are turned into Failures with `cut` above");
+ }
+ Err(nom::Err::Failure(DataElementDeserializeError::NomError(
+ nom::error::ErrorKind::Eof,
+ ))) => {
+ if self.data.is_empty() {
+ None
+ } else {
+ Some(Err(DataElementDeserializeError::UnexpectedDataRemaining))
+ }
+ }
+ Err(nom::Err::Failure(e)) => Some(Err(e)),
+ Err(nom::Err::Incomplete(_)) => {
+ panic!("Incomplete unexpected when using nom::complete APIs")
+ }
+ }
}
}
@@ -237,6 +263,7 @@
#[derive(Debug, PartialEq, Eq)]
pub(crate) struct RawPlainDataElement<'d> {
de_type: PlainDataElementType,
+ /// Byte array payload of the data element, without the DE header.
contents: &'d [u8],
}
@@ -297,26 +324,15 @@
DecryptError::DeserializeError(AdvDeserializeError::AdvertisementDeserializeError)
})?;
- let (_remaining, raw_des) = combinator::all_consuming(parse_data_elements)(remaining)
- .map_err(|_e| {
- DecryptError::DeserializeError(AdvDeserializeError::AdvertisementDeserializeError)
- })?;
+ let remaining_arr = ArrayView::try_from_slice(remaining)
+ .expect("Max remaining = 31 - 14 = 17 bytes < BLE_ADV_SVC_CONTENT_LEN");
- plain_data_elements(&raw_des)?
- .into_iter()
- .map(|de| de.try_deserialize())
- .collect::<Result<Vec<_>, _>>()
- .map_err(|e| {
- DecryptError::DeserializeError(AdvDeserializeError::DataElementDeserializeError(e))
- })
- .map(|data_elements| {
- DecryptedAdvContents::new(
- self.identity_type,
- ShortMetadataKey(metadata_key),
- self.salt,
- data_elements,
- )
- })
+ Ok(DecryptedAdvContents::new(
+ self.identity_type,
+ ShortMetadataKey(metadata_key),
+ self.salt,
+ remaining_arr,
+ ))
}
}
@@ -345,26 +361,21 @@
/// The contents of a plaintext advertisement after deserializing DE contents
#[derive(Debug, PartialEq, Eq)]
-pub struct PlaintextAdvContents {
+pub struct PlaintextAdvContents<'d> {
identity_type: PlaintextIdentityMode,
- data_elements: Vec<PlainDataElement<Plaintext>>,
+ /// Contents of the advertisement excluding the identity DE
+ data: &'d [u8],
}
-impl PlaintextAdvContents {
+impl<'d> PlaintextAdvContents<'d> {
/// Returns the identity type used for the advertisement
pub fn identity(&self) -> PlaintextIdentityMode {
self.identity_type
}
/// Returns an iterator over the v0 data elements
- pub fn data_elements(&self) -> impl ExactSizeIterator<Item = &PlainDataElement<Plaintext>> {
- self.data_elements.iter()
- }
-
- /// Destructures this V0 plaintext advertisement
- /// into just the contained data elements
- pub fn to_data_elements(self) -> Vec<PlainDataElement<Plaintext>> {
- self.data_elements
+ pub fn data_elements(&self) -> PlainDeIterator<'d, Plaintext> {
+ PlainDeIterator { data: self.data, _marker: PhantomData }
}
}
@@ -374,7 +385,9 @@
identity_type: EncryptedIdentityDataElementType,
metadata_key: ShortMetadataKey,
salt: LegacySalt,
- data_elements: Vec<PlainDataElement<Ciphertext>>,
+ /// The decrypted data in this advertisement. This should be a sequence of
+ /// serialized data elements, excluding the identity DE.
+ data: ArrayView<u8, { BLE_ADV_SVC_CONTENT_LEN }>,
}
impl DecryptedAdvContents {
@@ -383,9 +396,9 @@
identity_type: EncryptedIdentityDataElementType,
metadata_key: ShortMetadataKey,
salt: LegacySalt,
- data_elements: Vec<PlainDataElement<Ciphertext>>,
+ data: ArrayView<u8, { BLE_ADV_SVC_CONTENT_LEN }>,
) -> Self {
- Self { identity_type, metadata_key, salt, data_elements }
+ Self { identity_type, metadata_key, salt, data }
}
/// The type of identity DE used in the advertisement.
@@ -396,8 +409,8 @@
/// Iterator over the data elements in an advertisement, except for any DEs related to resolving
/// the identity or otherwise validating the payload (e.g. any identity DEs like Private
/// Identity).
- pub fn data_elements(&self) -> impl ExactSizeIterator<Item = &PlainDataElement<Ciphertext>> {
- self.data_elements.iter()
+ pub fn data_elements(&self) -> PlainDeIterator<Ciphertext> {
+ PlainDeIterator { data: self.data.as_slice(), _marker: PhantomData }
}
/// The salt used for decryption of this advertisement.
@@ -418,7 +431,7 @@
#[derive(Debug, PartialEq, Eq)]
pub(crate) enum IntermediateAdvContents<'d> {
/// Plaintext advertisements
- Plaintext(PlaintextAdvContents),
+ Plaintext(PlaintextAdvContents<'d>),
/// Ciphertext advertisements
Ciphertext(EncryptedAdvContents<'d>),
}
diff --git a/nearby/presence/np_adv/src/legacy/deserialize/tests.rs b/nearby/presence/np_adv/src/legacy/deserialize/tests.rs
index 4512bf7..5725e0b 100644
--- a/nearby/presence/np_adv/src/legacy/deserialize/tests.rs
+++ b/nearby/presence/np_adv/src/legacy/deserialize/tests.rs
@@ -12,10 +12,12 @@
// See the License for the specific language governing permissions and
// limitations under the License.
+extern crate alloc;
extern crate std;
use super::*;
-use crate::legacy::actions::{ActionBits, ActionsDataElement};
+use crate::legacy::actions::{ActionBits, ActionsDataElement, Finder, NearbyShare};
+use crate::legacy::serialize::id_de_type_as_generic_de_type;
use crate::shared_data::TxPower;
use crate::{
credential::{v0::V0, SimpleBroadcastCryptoMaterial},
@@ -25,18 +27,19 @@
de_type::DeActualLength,
random_data_elements::{random_de_ciphertext, random_de_plaintext},
serialize::{
- encode_de_header_actual_len, id_de_type_as_generic_de_type, AdvBuilder,
- DataElementBundle, Identity, LdtIdentity, ToDataElementBundle as _,
+ encode_de_header_actual_len, AdvBuilder, DataElementBundle, Identity, LdtIdentity,
+ ToDataElementBundle as _,
},
PacketFlavorEnum, BLE_ADV_SVC_CONTENT_LEN,
},
parse_adv_header, shared_data, AdvHeader, PublicIdentity,
};
+use alloc::vec::Vec;
use array_view::ArrayView;
use crypto_provider_default::CryptoProviderImpl;
use init_with::InitWith as _;
use ldt_np_adv::LdtEncrypterXtsAes128;
-use nom::error;
+use nom::error::{self, ErrorKind};
use rand_ext::rand::{prelude::SliceRandom, Rng as _};
use std::vec;
use strum::IntoEnumIterator as _;
@@ -76,22 +79,27 @@
let adv = parse_raw_adv_contents::<CryptoProviderImpl>(&[
0x03, // public identity
0x15, 0x05, // tx power 5
- 0x36, 0x11, 0x12, 0x13, // actions
+ 0x26, 0x00, 0x44, // actions
])
.unwrap();
- assert_eq!(
- RawAdvertisement::Plaintext(PlaintextAdvRawContents {
- identity_type: PlaintextIdentityMode::Public,
- data_elements: vec![
- RawPlainDataElement { de_type: PlainDataElementType::TxPower, contents: &[0x05] },
- RawPlainDataElement {
- de_type: PlainDataElementType::Actions,
- contents: &[0x11, 0x12, 0x13]
- }
- ],
- }),
- adv
- );
+ match adv {
+ RawAdvertisement::Plaintext(plaintext) => {
+ assert_eq!(PlaintextIdentityMode::Public, plaintext.identity_type);
+ let mut action_bits = ActionBits::default();
+ action_bits.set_action(NearbyShare::from(true));
+ action_bits.set_action(Finder::from(true));
+ assert_eq!(
+ vec![
+ PlainDataElement::<Plaintext>::TxPower(TxPowerDataElement::from(
+ TxPower::try_from(5).unwrap()
+ )),
+ PlainDataElement::Actions(ActionsDataElement::from(action_bits)),
+ ],
+ plaintext.data_elements().collect::<Result<Vec<_>, _>>().unwrap(),
+ );
+ }
+ RawAdvertisement::Ciphertext(_) => panic!("adv should be plaintext"),
+ }
}
#[test]
@@ -100,13 +108,16 @@
0x03, // public identity
])
.unwrap();
- assert_eq!(
- RawAdvertisement::Plaintext(PlaintextAdvRawContents {
- identity_type: PlaintextIdentityMode::Public,
- data_elements: vec![],
- }),
- adv
- );
+ match adv {
+ RawAdvertisement::Plaintext(plaintext) => {
+ assert_eq!(PlaintextIdentityMode::Public, plaintext.identity_type);
+ assert_eq!(
+ Vec::<PlainDataElement<Plaintext>>::new(),
+ plaintext.data_elements().collect::<Result<Vec<_>, _>>().unwrap()
+ );
+ }
+ RawAdvertisement::Ciphertext(_) => panic!("adv should be plaintext"),
+ }
}
#[test]
@@ -114,8 +125,8 @@
// battery uses the header length as is
let input = &[0xFB, 0x01, 0x02, 0x03];
assert_eq!(
- nom::Err::Error(error::Error { input: input.as_slice(), code: error::ErrorKind::Eof }),
- parse_data_elements(input).unwrap_err()
+ nom::Err::Error(DataElementDeserializeError::NomError(ErrorKind::MapOpt)),
+ parse_de(input).unwrap_err(),
);
}
@@ -126,10 +137,15 @@
0x03, // another public identity
0x15, 0x03, // tx power de
];
- assert_eq!(
- AdvDeserializeError::InvalidDataElementHierarchy,
- parse_raw_adv_contents::<CryptoProviderImpl>(input).unwrap_err()
- );
+ match parse_raw_adv_contents::<CryptoProviderImpl>(input).unwrap() {
+ RawAdvertisement::Plaintext(content) => {
+ assert_eq!(
+ DataElementDeserializeError::DuplicateIdentityDataElement,
+ content.data_elements().collect::<Result<Vec<_>, _>>().unwrap_err(),
+ );
+ }
+ RawAdvertisement::Ciphertext(_) => panic!("Adv should be plaintext"),
+ }
}
#[test]
@@ -297,35 +313,42 @@
];
assert_eq!(
- nom::Err::Error(error::Error { input: input.as_slice(), code: error::ErrorKind::MapOpt }),
+ nom::Err::Error(DataElementDeserializeError::NomError(ErrorKind::MapOpt)),
parse_de(&input[..]).unwrap_err()
);
}
#[test]
-fn plain_data_elements_matches_plain_des() {
+fn raw_de_to_plain_de_matches_plain_des() {
assert_eq!(
- vec![
- RawPlainDataElement { de_type: PlainDataElementType::TxPower, contents: &[0x01] },
- RawPlainDataElement { de_type: PlainDataElementType::Actions, contents: &[0x02] }
- ],
- plain_data_elements(&[
- RawDataElement { de_type: DataElementType::TxPower, contents: &[0x01] },
- RawDataElement { de_type: DataElementType::Actions, contents: &[0x02] }
- ])
- .unwrap()
+ PlainDataElement::TxPower(TxPowerDataElement::from(TxPower::try_from(1).unwrap())),
+ PlainDeIterator::<Plaintext>::raw_de_to_plain_de(RawDataElement {
+ de_type: DataElementType::TxPower,
+ contents: &[0x01]
+ })
+ .unwrap(),
+ );
+ assert_eq!(
+ PlainDataElement::Actions(ActionsDataElement::from(
+ ActionBits::try_from(0x00400000).unwrap()
+ )),
+ PlainDeIterator::<Plaintext>::raw_de_to_plain_de(RawDataElement {
+ de_type: DataElementType::Actions,
+ contents: &[0x00, 0x40]
+ })
+ .unwrap(),
);
}
#[test]
-fn plain_data_elements_rejects_identity_de_error() {
+fn raw_de_to_plain_de_rejects_identity_de_error() {
for idet in IdentityDataElementType::iter() {
assert_eq!(
- AdvDeserializeError::InvalidDataElementHierarchy,
- plain_data_elements(&[
- RawDataElement { de_type: DataElementType::TxPower, contents: &[0x01] },
- RawDataElement { de_type: id_de_type_as_generic_de_type(idet), contents: &[0x02] }
- ])
+ DataElementDeserializeError::DuplicateIdentityDataElement,
+ PlainDeIterator::<Plaintext>::raw_de_to_plain_de(RawDataElement {
+ de_type: id_de_type_as_generic_de_type(idet),
+ contents: &[0x02],
+ })
.unwrap_err()
);
}
@@ -432,10 +455,11 @@
eac
);
- assert_eq!(
- DecryptedAdvContents { identity_type, metadata_key, salt, data_elements: des },
- eac.try_decrypt(&cipher).unwrap()
- )
+ let contents = eac.try_decrypt(&cipher).unwrap();
+ assert_eq!(identity_type, contents.identity_type);
+ assert_eq!(metadata_key, contents.metadata_key);
+ assert_eq!(salt, contents.salt);
+ assert_eq!(des, contents.data_elements().collect::<Result<Vec<_>, _>>().unwrap());
} else {
panic!("Unexpected variant: {:?}", parsed_adv);
}
@@ -496,17 +520,16 @@
eac
);
+ let decrypted = eac.try_decrypt(&cipher).unwrap();
+ assert_eq!(EncryptedIdentityDataElementType::Private, decrypted.identity_type);
+ assert_eq!(metadata_key, decrypted.metadata_key);
+ assert_eq!(salt, decrypted.salt);
assert_eq!(
- DecryptedAdvContents {
- identity_type: EncryptedIdentityDataElementType::Private,
- metadata_key,
- salt,
- data_elements: vec![PlainDataElement::TxPower(TxPowerDataElement::from(
- TxPower::try_from(3).unwrap()
- ))],
- },
- eac.try_decrypt(&cipher).unwrap()
- )
+ vec![PlainDataElement::TxPower(TxPowerDataElement::from(
+ TxPower::try_from(3).unwrap()
+ ))],
+ decrypted.data_elements().collect::<Result<Vec<_>, _>>().unwrap()
+ );
} else {
panic!("Unexpected variant: {:?}", parsed_adv);
}
@@ -534,16 +557,14 @@
assert_eq!(AdvHeader::V0, header);
let parsed_adv = deserialize_adv_contents::<CryptoProviderImpl>(remaining).unwrap();
- if let IntermediateAdvContents::Plaintext(parc) = parsed_adv {
+ if let IntermediateAdvContents::Plaintext(adv_contents) = parsed_adv {
+ assert_eq!(PlaintextIdentityMode::Public, adv_contents.identity());
assert_eq!(
- PlaintextAdvContents {
- identity_type: PlaintextIdentityMode::Public,
- data_elements: vec![PlainDataElement::Actions(ActionsDataElement::from(
- ActionBits::default()
- ))],
- },
- parc
- )
+ vec![PlainDataElement::<Plaintext>::Actions(ActionsDataElement::from(
+ ActionBits::default()
+ ))],
+ adv_contents.data_elements().collect::<Result<Vec<_>, _>>().unwrap()
+ );
} else {
panic!("Unexpected variant: {:?}", parsed_adv);
}
@@ -670,8 +691,12 @@
let parsed_adv = deserialize_adv_contents::<CryptoProviderImpl>(&adv[1..]).unwrap();
if let IntermediateAdvContents::Ciphertext(eac) = parsed_adv {
assert_eq!(
- DecryptError::DeserializeError(AdvDeserializeError::InvalidDataElementHierarchy),
- eac.try_decrypt(&cipher).unwrap_err()
+ DataElementDeserializeError::DuplicateIdentityDataElement,
+ eac.try_decrypt(&cipher)
+ .unwrap()
+ .data_elements()
+ .collect::<Result<Vec<_>, _>>()
+ .unwrap_err()
)
} else {
panic!("Unexpected variant: {:?}", parsed_adv);
@@ -734,11 +759,11 @@
);
buf.extend_from_slice(contents);
- let raw_de = combinator::all_consuming(parse_de)(&buf).map(|(_remaining, de)| de).unwrap();
-
- let plain_des = plain_data_elements(&[raw_de]).unwrap();
+ let mut plain_des = PlainDeIterator { data: &buf, _marker: PhantomData }
+ .collect::<Result<Vec<_>, _>>()
+ .unwrap();
assert_eq!(1, plain_des.len());
- plain_des.first().unwrap().try_deserialize().unwrap()
+ plain_des.swap_remove(0)
}
fn plaintext_random_adv_contents_round_trip<I: Identity<Flavor = Plaintext>, F: Fn() -> I>(
@@ -774,31 +799,12 @@
parse_raw_adv_contents::<CryptoProviderImpl>(&serialized.as_slice()[1..]).unwrap();
assert_eq!(AdvHeader::V0, header);
- if let RawAdvertisement::Plaintext(parc) = parsed_adv {
+ if let RawAdvertisement::Plaintext(adv_contents) = parsed_adv {
+ assert_eq!(identity_type, adv_contents.identity_type);
assert_eq!(
- PlaintextAdvRawContents {
- identity_type,
- data_elements: de_tuples
- .iter()
- .map(|(_de, de_type, bundle)| RawPlainDataElement {
- de_type: *de_type,
- contents: bundle.contents_as_slice(),
- })
- .collect()
- },
- parc
+ de_tuples.into_iter().map(|(de, _de_type, _bundle)| de).collect::<Vec<_>>(),
+ adv_contents.data_elements().collect::<Result<Vec<_>, _>>().unwrap(),
);
-
- assert_eq!(
- PlaintextAdvContents {
- identity_type,
- data_elements: de_tuples
- .into_iter()
- .map(|(de, _de_type, _bundle)| de)
- .collect(),
- },
- parc.try_deserialize().unwrap()
- )
} else {
panic!("Unexpected variant: {:?}", parsed_adv);
}
diff --git a/nearby/presence/np_adv/src/lib.rs b/nearby/presence/np_adv/src/lib.rs
index 45207ff..1c42796 100644
--- a/nearby/presence/np_adv/src/lib.rs
+++ b/nearby/presence/np_adv/src/lib.rs
@@ -21,6 +21,7 @@
#![forbid(unsafe_code)]
#![deny(missing_docs)]
+#[cfg(any(test, feature = "alloc"))]
extern crate alloc;
extern crate core;
use crate::{
@@ -39,20 +40,22 @@
},
};
+#[cfg(any(test, feature = "alloc"))]
use alloc::vec::Vec;
+use array_vec::ArrayVecOption;
#[cfg(feature = "devtools")]
use array_view::ArrayView;
-use core::borrow::BorrowMut;
use core::fmt::Debug;
use crypto_provider::CryptoProvider;
-use deserialization_arena::DeserializationArena;
-
+use deserialization_arena::{DeserializationArena, DeserializationArenaAllocator};
#[cfg(feature = "devtools")]
use extended::NP_ADV_MAX_SECTION_LEN;
+use extended::NP_V1_ADV_MAX_ENCRYPTED_SECTION_COUNT;
use legacy::{data_elements::DataElementDeserializeError, deserialize::AdvDeserializeError};
use nom::{combinator, number};
pub use strum;
+mod array_vec;
pub mod credential;
pub mod de_type;
#[cfg(test)]
@@ -76,21 +79,7 @@
/// Parse, deserialize, decrypt, and validate a complete NP advertisement (the entire contents of
/// the service data for the NP UUID).
pub fn deserialize_advertisement<'adv, 'cred, B, P>(
- mut arena: impl BorrowMut<DeserializationArena<'adv>>,
- adv: &'adv [u8],
- cred_book: &'cred B,
-) -> Result<DeserializedAdvertisement<'adv, B::Matched>, AdvDeserializationError>
-where
- B: CredentialBook<'cred>,
- P: CryptoProvider,
-{
- // Turn `impl BorrowMut` into the concrete `&mut` reference so that the compiler does not need
- // to duplicate the function code for different concrete types.
- deserialize_advertisement_impl::<B, P>(arena.borrow_mut(), adv, cred_book)
-}
-
-fn deserialize_advertisement_impl<'adv, 'cred, B, P>(
- arena: &mut DeserializationArena<'adv>,
+ arena: DeserializationArena<'adv>,
adv: &'adv [u8],
cred_book: &'cred B,
) -> Result<DeserializedAdvertisement<'adv, B::Matched>, AdvDeserializationError>
@@ -128,15 +117,6 @@
ParseError,
/// No suitable credential found to decrypt the given section.
NoMatchingCredentials,
- /// Given Arena is not large enough to hold the decrypted data.
- ArenaOutOfSpace,
-}
-
-#[cfg(feature = "devtools")]
-impl From<ArenaOutOfSpace> for AdvDecryptionError {
- fn from(_: ArenaOutOfSpace) -> Self {
- Self::ArenaOutOfSpace
- }
}
/// Decrypt, but do not further deserialize the v1 bytes, intended for developer tooling uses only.
@@ -144,7 +124,7 @@
/// structured format and provides extra type safety.
#[cfg(feature = "devtools")]
pub fn deser_decrypt_v1_section_bytes_for_dev_tools<'adv, 'cred, B, P>(
- arena: &mut DeserializationArena<'adv>,
+ arena: DeserializationArena<'adv>,
cred_book: &'cred B,
header_byte: u8,
section_bytes: &'adv [u8],
@@ -161,21 +141,22 @@
IntermediateSection::Ciphertext(section) => section,
};
+ let mut allocator = arena.into_allocator();
for (crypto_material, _) in cred_book.v1_iter() {
if let Some(plaintext) = cipher_section
- .try_resolve_identity_and_decrypt::<_, P>(arena.borrow_mut(), &crypto_material)
+ .try_resolve_identity_and_decrypt::<_, P>(&mut allocator, &crypto_material)
{
- return plaintext
- .map(|pt| {
- let encryption_scheme = match cipher_section {
- CiphertextSection::SignatureEncryptedIdentity(_) => {
- V1EncryptionScheme::Signature
- }
- CiphertextSection::MicEncryptedIdentity(_) => V1EncryptionScheme::Mic,
- };
- (pt, encryption_scheme)
- })
- .map_err(|e| e.into());
+ let pt = plaintext.expect(concat!(
+ "Should not run out of space because DeserializationArenaAllocator is big ",
+ "enough to hold a single advertisement, and we exit immediately upon ",
+ "successful decryption",
+ ));
+
+ let encryption_scheme = match cipher_section {
+ CiphertextSection::SignatureEncryptedIdentity(_) => V1EncryptionScheme::Signature,
+ CiphertextSection::MicEncryptedIdentity(_) => V1EncryptionScheme::Mic,
+ };
+ return Ok((pt, encryption_scheme));
}
}
Err(AdvDecryptionError::NoMatchingCredentials)
@@ -197,8 +178,14 @@
/// section ordering as they appeared within the original advertisement to ensure
/// that the fully-deserialized advertisement may be correctly reconstructed.
struct SectionsInProcessing<'adv, M: MatchedCredential> {
- deserialized_sections: Vec<(usize, V1DeserializedSection<'adv, M>)>,
- encrypted_sections: Vec<(usize, ResolvableCiphertextSection<'adv>)>,
+ deserialized_sections: ArrayVecOption<
+ (usize, V1DeserializedSection<'adv, M>),
+ { NP_V1_ADV_MAX_ENCRYPTED_SECTION_COUNT },
+ >,
+ encrypted_sections: ArrayVecOption<
+ (usize, ResolvableCiphertextSection<'adv>),
+ { NP_V1_ADV_MAX_ENCRYPTED_SECTION_COUNT },
+ >,
malformed_sections_count: usize,
}
@@ -214,8 +201,8 @@
parse_sections(header, remaining).map_err(|_| AdvDeserializationError::ParseError {
details_hazmat: AdvDeserializationErrorDetailsHazmat::AdvertisementDeserializeError,
})?;
- let mut deserialized_sections = Vec::new();
- let mut encrypted_sections = Vec::new();
+ let mut deserialized_sections = ArrayVecOption::default();
+ let mut encrypted_sections = ArrayVecOption::default();
// keep track of ordering for later sorting during `self.finished_with_decryption_attempts()`.
for (idx, s) in int_sections.into_iter().enumerate() {
match s {
@@ -247,7 +234,7 @@
/// the cost of iterating over sections-in-memory.
fn try_decrypt_with_credential<C: V1DiscoveryCryptoMaterial, P: CryptoProvider>(
&mut self,
- arena: &mut DeserializationArena<'adv>,
+ arena: &mut DeserializationArenaAllocator<'adv>,
crypto_material: C,
match_data: M,
) -> Result<(), ArenaOutOfSpace> {
@@ -278,14 +265,14 @@
let deserialization_result = match §ion.ciphertext_section {
CiphertextSection::SignatureEncryptedIdentity(c) => c
.try_deserialize(
- arena.borrow_mut(),
+ arena,
identity_match,
&crypto_material.signed_verification_material::<P>(),
)
.map_err(SectionDeserializeError::from),
CiphertextSection::MicEncryptedIdentity(c) => c
.try_deserialize(
- arena.borrow_mut(),
+ arena,
identity_match,
&crypto_material.unsigned_verification_material::<P>(),
)
@@ -351,7 +338,7 @@
/// Deserialize and decrypt the contents of a v1 adv after the version header
fn deser_decrypt_v1<'adv, 'cred, B, P>(
- arena: &mut DeserializationArena<'adv>,
+ arena: DeserializationArena<'adv>,
cred_book: &'cred B,
remaining: &'adv [u8],
header: V1Header,
@@ -365,14 +352,18 @@
header, remaining,
)?;
+ let mut allocator = arena.into_allocator();
+
// Hot loop
// We assume that iterating credentials is more expensive than iterating sections
for (crypto_material, match_data) in cred_book.v1_iter() {
- sections_in_processing.try_decrypt_with_credential::<_, P>(
- arena.borrow_mut(),
- crypto_material,
- match_data,
- )?;
+ sections_in_processing
+ .try_decrypt_with_credential::<_, P>(&mut allocator, crypto_material, match_data)
+ .expect(concat!(
+ "Should not run out of space because DeserializationArenaAllocator is big ",
+ "enough to hold a single advertisement, and we exit immediately upon ",
+ "successful decryption",
+ ));
if sections_in_processing.resolved_all_identities() {
// No need to consider the other credentials
break;
@@ -382,12 +373,12 @@
}
/// Deserialize and decrypt the contents of a v0 adv after the version header
-fn deser_decrypt_v0<'a, B, P>(
- cred_book: &'a B,
- remaining: &[u8],
-) -> Result<V0AdvertisementContents<B::Matched>, AdvDeserializationError>
+fn deser_decrypt_v0<'adv, 'cred, B, P>(
+ cred_book: &'cred B,
+ remaining: &'adv [u8],
+) -> Result<V0AdvertisementContents<'adv, B::Matched>, AdvDeserializationError>
where
- B: CredentialBook<'a>,
+ B: CredentialBook<'cred>,
P: CryptoProvider,
{
let contents = legacy::deserialize::deserialize_adv_contents::<P>(remaining)?;
@@ -449,11 +440,12 @@
}
/// An NP advertisement with its header parsed.
+#[allow(clippy::large_enum_variant)]
#[derive(Debug, PartialEq, Eq)]
pub enum DeserializedAdvertisement<'adv, M: MatchedCredential> {
/// V0 header has all reserved bits, so there is no data to represent other than the version
/// itself.
- V0(V0AdvertisementContents<M>),
+ V0(V0AdvertisementContents<'adv, M>),
/// V1 advertisement
V1(V1AdvertisementContents<'adv, M>),
}
@@ -461,7 +453,7 @@
impl<'adv, M: MatchedCredential> DeserializedAdvertisement<'adv, M> {
/// Attempts to cast this deserialized advertisement into the `V0AdvertisementContents`
/// variant. If the underlying advertisement is not V0, this will instead return `None`.
- pub fn into_v0(self) -> Option<V0AdvertisementContents<M>> {
+ pub fn into_v0(self) -> Option<V0AdvertisementContents<'adv, M>> {
match self {
Self::V0(x) => Some(x),
_ => None,
@@ -480,18 +472,26 @@
/// The contents of a deserialized and decrypted V1 advertisement.
#[derive(Debug, PartialEq, Eq)]
pub struct V1AdvertisementContents<'adv, M: MatchedCredential> {
- sections: Vec<V1DeserializedSection<'adv, M>>,
+ sections: ArrayVecOption<V1DeserializedSection<'adv, M>, NP_V1_ADV_MAX_ENCRYPTED_SECTION_COUNT>,
invalid_sections: usize,
}
impl<'adv, M: MatchedCredential> V1AdvertisementContents<'adv, M> {
- fn new(sections: Vec<V1DeserializedSection<'adv, M>>, invalid_sections: usize) -> Self {
+ fn new(
+ sections: ArrayVecOption<
+ V1DeserializedSection<'adv, M>,
+ NP_V1_ADV_MAX_ENCRYPTED_SECTION_COUNT,
+ >,
+ invalid_sections: usize,
+ ) -> Self {
Self { sections, invalid_sections }
}
/// Destructures this V1 advertisement into just the sections
/// which could be successfully deserialized and decrypted
- pub fn into_sections(self) -> Vec<V1DeserializedSection<'adv, M>> {
+ pub fn into_sections(
+ self,
+ ) -> ArrayVecOption<V1DeserializedSection<'adv, M>, NP_V1_ADV_MAX_ENCRYPTED_SECTION_COUNT> {
self.sections
}
@@ -508,9 +508,9 @@
/// Advertisement content that was either already plaintext or has been decrypted.
#[derive(Debug, PartialEq, Eq)]
-pub enum V0AdvertisementContents<M: MatchedCredential> {
+pub enum V0AdvertisementContents<'adv, M: MatchedCredential> {
/// Contents of an originally plaintext advertisement
- Plaintext(PlaintextAdvContents),
+ Plaintext(PlaintextAdvContents<'adv>),
/// Contents that was ciphertext in the original advertisement, and has been decrypted
/// with the credential in the [MatchedCredential]
Decrypted(WithMatchedCredential<M, DecryptedAdvContents>),
@@ -650,14 +650,6 @@
/// [AdvDeserializationErrorDetailsHazmat] before using this field.
details_hazmat: AdvDeserializationErrorDetailsHazmat,
},
- /// The given arena is not large enough to hold the decrypted data.
- ArenaOutOfSpace,
-}
-
-impl From<ArenaOutOfSpace> for AdvDeserializationError {
- fn from(_: ArenaOutOfSpace) -> Self {
- Self::ArenaOutOfSpace
- }
}
impl Debug for AdvDeserializationError {
@@ -665,7 +657,6 @@
match self {
AdvDeserializationError::HeaderParseError => write!(f, "HeaderParseError"),
AdvDeserializationError::ParseError { .. } => write!(f, "ParseError"),
- AdvDeserializationError::ArenaOutOfSpace => write!(f, "ArenaOutOfSpace"),
}
}
}
@@ -699,24 +690,12 @@
AdvDeserializationErrorDetailsHazmat::AdvertisementDeserializeError,
}
}
- AdvDeserializeError::DataElementDeserializeError(e) => {
- AdvDeserializationError::ParseError {
- details_hazmat:
- AdvDeserializationErrorDetailsHazmat::V0DataElementDeserializeError(e),
- }
- }
AdvDeserializeError::TooManyTopLevelDataElements => {
AdvDeserializationError::ParseError {
details_hazmat:
AdvDeserializationErrorDetailsHazmat::TooManyTopLevelDataElements,
}
}
- AdvDeserializeError::InvalidDataElementHierarchy => {
- AdvDeserializationError::ParseError {
- details_hazmat:
- AdvDeserializationErrorDetailsHazmat::InvalidDataElementHierarchy,
- }
- }
AdvDeserializeError::MissingIdentity => AdvDeserializationError::ParseError {
details_hazmat: AdvDeserializationErrorDetailsHazmat::MissingIdentity,
},
diff --git a/nearby/presence/np_adv/tests/examples_v0.rs b/nearby/presence/np_adv/tests/examples_v0.rs
index 27ea7c5..8310531 100644
--- a/nearby/presence/np_adv/tests/examples_v0.rs
+++ b/nearby/presence/np_adv/tests/examples_v0.rs
@@ -35,8 +35,9 @@
0,
CryptoProviderImpl,
>(&[], &[]);
+ let arena = deserialization_arena!();
let adv = deserialize_advertisement::<_, CryptoProviderImpl>(
- deserialization_arena!(),
+ arena,
&[
0x00, // adv header
0x03, // public identity
@@ -52,10 +53,10 @@
V0AdvertisementContents::Plaintext(p) => {
assert_eq!(PlaintextIdentityMode::Public, p.identity());
assert_eq!(
- vec![&PlainDataElement::TxPower(TxPowerDataElement::from(
+ vec![PlainDataElement::TxPower(TxPowerDataElement::from(
TxPower::try_from(3).unwrap()
- )),],
- p.data_elements().collect::<Vec<_>>()
+ ))],
+ p.data_elements().collect::<Result<Vec<_>, _>>().unwrap()
);
}
_ => panic!("this example is plaintext"),
@@ -154,7 +155,7 @@
assert_eq!(metadata_key, decrypted.metadata_key());
assert_eq!(
- vec![&PlainDataElement::TxPower(TxPowerDataElement::from(TxPower::try_from(3).unwrap())),],
- decrypted.data_elements().collect::<Vec<_>>()
+ vec![PlainDataElement::TxPower(TxPowerDataElement::from(TxPower::try_from(3).unwrap())),],
+ decrypted.data_elements().collect::<Result<Vec<_>, _>>().unwrap(),
);
}
diff --git a/nearby/presence/np_c_ffi/Cargo.lock b/nearby/presence/np_c_ffi/Cargo.lock
index f55cf29..796292b 100644
--- a/nearby/presence/np_c_ffi/Cargo.lock
+++ b/nearby/presence/np_c_ffi/Cargo.lock
@@ -473,7 +473,6 @@
name = "handle_map"
version = "0.1.0"
dependencies = [
- "crypto_provider",
"lock_adapter",
]
diff --git a/nearby/presence/np_cpp_ffi/tests/CMakeLists.txt b/nearby/presence/np_cpp_ffi/tests/CMakeLists.txt
index bddda64..7ca9eb0 100644
--- a/nearby/presence/np_cpp_ffi/tests/CMakeLists.txt
+++ b/nearby/presence/np_cpp_ffi/tests/CMakeLists.txt
@@ -17,9 +17,10 @@
deserialize_result_tests.cc
deserialize_v0_tests.cc
deserialize_v1_tests.cc
- global_config_tests.cc
credential_book_tests.cc
byte_buffer_tests.cc
+ np_cpp_test.h
+ np_cpp_test.cc
)
target_link_libraries(
diff --git a/nearby/presence/np_cpp_ffi/tests/credential_book_tests.cc b/nearby/presence/np_cpp_ffi/tests/credential_book_tests.cc
index 53218ee..3a32904 100644
--- a/nearby/presence/np_cpp_ffi/tests/credential_book_tests.cc
+++ b/nearby/presence/np_cpp_ffi/tests/credential_book_tests.cc
@@ -14,13 +14,27 @@
#include "nearby_protocol.h"
#include "shared_test_util.h"
+#include "np_cpp_test.h"
#include "gtest/gtest.h"
-TEST(NpFfiCredentialBookTests, TestMoveConstructor) {
+TEST_F(NpCppTest, TestSetMaxCredBooks) {
+ auto book1_result = nearby_protocol::CredentialBook::TryCreate();
+ ASSERT_TRUE(book1_result.ok());
+
+ auto book2_result = nearby_protocol::CredentialBook::TryCreate();
+ ASSERT_TRUE(book2_result.ok());
+
+ auto book3_result = nearby_protocol::CredentialBook::TryCreate();
+ ASSERT_FALSE(book3_result.ok());
+ ASSERT_TRUE(absl::IsResourceExhausted(book3_result.status()));
+}
+
+TEST_F(NpCppTest, TestMoveConstructor) {
auto book = nearby_protocol::CredentialBook::TryCreate().value();
auto deserialize_result =
- nearby_protocol::Deserializer::DeserializeAdvertisement(V0AdvSimple, book);
+ nearby_protocol::Deserializer::DeserializeAdvertisement(V0AdvSimple,
+ book);
ASSERT_EQ(deserialize_result.GetKind(),
np_ffi::internal::DeserializeAdvertisementResultKind::V0);
@@ -47,10 +61,11 @@
"");
}
-TEST(NpFfiCredentialBookTests, TestMoveAssignment) {
+TEST_F(NpCppTest, TestMoveAssignment) {
auto book = nearby_protocol::CredentialBook::TryCreate().value();
auto deserialize_result =
- nearby_protocol::Deserializer::DeserializeAdvertisement(V0AdvSimple, book);
+ nearby_protocol::Deserializer::DeserializeAdvertisement(V0AdvSimple,
+ book);
ASSERT_EQ(deserialize_result.GetKind(),
np_ffi::internal::DeserializeAdvertisementResultKind::V0);
diff --git a/nearby/presence/np_cpp_ffi/tests/deserialize_result_tests.cc b/nearby/presence/np_cpp_ffi/tests/deserialize_result_tests.cc
index 54872c6..737fd18 100644
--- a/nearby/presence/np_cpp_ffi/tests/deserialize_result_tests.cc
+++ b/nearby/presence/np_cpp_ffi/tests/deserialize_result_tests.cc
@@ -14,14 +14,15 @@
#include "nearby_protocol.h"
#include "shared_test_util.h"
+#include "np_cpp_test.h"
#include "absl/strings/escaping.h"
#include "gtest/gtest.h"
-TEST(NpFfiDeserializeResultTests, TestResultMoveConstructor) {
+TEST_F(NpCppTest, TestResultMoveConstructor) {
auto book = nearby_protocol::CredentialBook::TryCreate().value();
- auto result =
- nearby_protocol::Deserializer::DeserializeAdvertisement(V0AdvSimple, book);
+ auto result = nearby_protocol::Deserializer::DeserializeAdvertisement(
+ V0AdvSimple, book);
ASSERT_EQ(result.GetKind(),
np_ffi::internal::DeserializeAdvertisementResultKind::V0);
@@ -49,7 +50,7 @@
ASSERT_DEATH([[maybe_unused]] auto failure = moved_again.GetKind(), "");
}
-TEST(NpFfiDeserializeResultTests, DeserializeFromStringView) {
+TEST_F(NpCppTest, DeserializeFromStringView) {
auto bytes = absl::HexStringToBytes("00031503");
auto buffer = nearby_protocol::ByteBuffer<255>::CopyFrom(bytes);
ASSERT_TRUE(buffer.ok());
@@ -86,16 +87,16 @@
ASSERT_EQ(tx_power.tx_power, 3);
}
-TEST(NpFfiDeserializeResultTests, TestResultMoveAssignment) {
+TEST_F(NpCppTest, TestResultMoveAssignment) {
auto book = nearby_protocol::CredentialBook::TryCreate().value();
- auto result =
- nearby_protocol::Deserializer::DeserializeAdvertisement(V0AdvSimple, book);
+ auto result = nearby_protocol::Deserializer::DeserializeAdvertisement(
+ V0AdvSimple, book);
ASSERT_EQ(result.GetKind(),
np_ffi::internal::DeserializeAdvertisementResultKind::V0);
// create a second result
- auto another_result =
- nearby_protocol::Deserializer::DeserializeAdvertisement(V0AdvSimple, book);
+ auto another_result = nearby_protocol::Deserializer::DeserializeAdvertisement(
+ V0AdvSimple, book);
ASSERT_EQ(another_result.GetKind(),
np_ffi::internal::DeserializeAdvertisementResultKind::V0);
@@ -119,10 +120,7 @@
ASSERT_DEATH([[maybe_unused]] auto failure = moved_again.GetKind(), "");
}
-TEST(NpFfiDeserializeResultTests, TestInvalidPayloadHeader) {
- ASSERT_TRUE(
- nearby_protocol::GlobalConfig::SetPanicHandler(test_panic_handler));
-
+TEST_F(NpCppTest, TestInvalidPayloadHeader) {
// An invalid header result should result in error
nearby_protocol::RawAdvertisementPayload InvalidHeaderPayload(
nearby_protocol::ByteBuffer<255>({1, {0xFF}}));
@@ -141,10 +139,7 @@
"");
}
-TEST(NpFfiDeserializeResultTests, TestInvalidV0Cast) {
- ASSERT_TRUE(
- nearby_protocol::GlobalConfig::SetPanicHandler(test_panic_handler));
-
+TEST_F(NpCppTest, TestInvalidV0Cast) {
auto maybe_credential_book = nearby_protocol::CredentialBook::TryCreate();
auto deserialize_result =
nearby_protocol::Deserializer::DeserializeAdvertisement(
@@ -156,10 +151,7 @@
"");
}
-TEST(NpFfiDeserializeResultTests, TestInvalidV1Cast) {
- ASSERT_TRUE(
- nearby_protocol::GlobalConfig::SetPanicHandler(test_panic_handler));
-
+TEST_F(NpCppTest, TestInvalidV1Cast) {
// Create an empty credential book and verify that is is successful
auto maybe_credential_book = nearby_protocol::CredentialBook::TryCreate();
ASSERT_TRUE(maybe_credential_book.ok());
@@ -173,7 +165,7 @@
"");
}
-TEST(NpFfiDeserializeResultTests, V0UseResultTwice) {
+TEST_F(NpCppTest, V0UseResultTwice) {
auto book_result = nearby_protocol::CredentialBook::TryCreate();
ASSERT_TRUE(book_result.ok());
@@ -191,7 +183,7 @@
"");
}
-TEST(NpFfiDeserializeResultTests, V1UseResultTwice) {
+TEST_F(NpCppTest, V1UseResultTwice) {
auto book_result = nearby_protocol::CredentialBook::TryCreate();
ASSERT_TRUE(book_result.ok());
@@ -209,7 +201,7 @@
"");
}
-TEST(NpFfiDeserializeResultTests, IntoV0AfterOutOfScope) {
+TEST_F(NpCppTest, IntoV0AfterOutOfScope) {
auto book_result = nearby_protocol::CredentialBook::TryCreate();
ASSERT_TRUE(book_result.ok());
@@ -228,7 +220,7 @@
"");
}
-TEST(NpFfiDeserializeResultTests, IntoV1AfterOutOfScope) {
+TEST_F(NpCppTest, IntoV1AfterOutOfScope) {
auto book_result = nearby_protocol::CredentialBook::TryCreate();
ASSERT_TRUE(book_result.ok());
@@ -247,7 +239,7 @@
"");
}
-TEST(NpFfiDeserializeV0Tests, V0ResultKindAfterOutOfScope) {
+TEST_F(NpCppTest, V0ResultKindAfterOutOfScope) {
auto book_result = nearby_protocol::CredentialBook::TryCreate();
ASSERT_TRUE(book_result.ok());
@@ -266,7 +258,7 @@
{ [[maybe_unused]] auto failure = deserialize_result.GetKind(); }, "");
}
-TEST(NpFfiDeserializeResultTests, V1ResultKindAfterOutOfScope) {
+TEST_F(NpCppTest, V1ResultKindAfterOutOfScope) {
auto book_result = nearby_protocol::CredentialBook::TryCreate();
ASSERT_TRUE(book_result.ok());
diff --git a/nearby/presence/np_cpp_ffi/tests/deserialize_v0_tests.cc b/nearby/presence/np_cpp_ffi/tests/deserialize_v0_tests.cc
index cd95c48..e2a10a0 100644
--- a/nearby/presence/np_cpp_ffi/tests/deserialize_v0_tests.cc
+++ b/nearby/presence/np_cpp_ffi/tests/deserialize_v0_tests.cc
@@ -14,10 +14,24 @@
#include "nearby_protocol.h"
#include "shared_test_util.h"
+#include "np_cpp_test.h"
#include "gtest/gtest.h"
-TEST(NpFfiDeserializeV0Tests, V0SingleDataElementTxPower) {
+TEST_F(NpCppTest, InvalidCast) {
+ auto book = nearby_protocol::CredentialBook::TryCreate().value();
+ auto deserialize_result =
+ nearby_protocol::Deserializer::DeserializeAdvertisement(V0AdvSimple, book);
+ ASSERT_EQ(deserialize_result.GetKind(),
+ np_ffi::internal::DeserializeAdvertisementResultKind::V0);
+
+ // Now try to cast the result into the wrong type and verify the process
+ // aborts
+ ASSERT_DEATH({ [[maybe_unused]] auto failure = deserialize_result.IntoV1(); },
+ "");
+}
+
+TEST_F(NpCppTest, V0SingleDataElementTxPower) {
nearby_protocol::RawAdvertisementPayload adv(
nearby_protocol::ByteBuffer<255>({
4,
@@ -56,7 +70,7 @@
ASSERT_EQ(tx_power.tx_power, 3);
}
-TEST(NpFfiDeserializeV0Tests, V0LengthOneActionsDataElement) {
+TEST_F(NpCppTest, V0LengthOneActionsDataElement) {
nearby_protocol::RawAdvertisementPayload adv(
nearby_protocol::ByteBuffer<255>({
4,
@@ -92,10 +106,10 @@
ASSERT_EQ(de.GetKind(), nearby_protocol::V0DataElementKind::Actions);
auto actions = de.AsActions();
- ASSERT_EQ(actions.GetAsU32(), 0);
+ ASSERT_EQ(actions.GetAsU32(), (uint32_t)0);
}
-TEST(NpFfiDeserializeV0Tests, V0LengthTwoActionsDataElement) {
+TEST_F(NpCppTest, V0LengthTwoActionsDataElement) {
nearby_protocol::RawAdvertisementPayload adv(
nearby_protocol::ByteBuffer<255>({
5,
@@ -150,7 +164,7 @@
ASSERT_EQ(actions.GetContextSyncSequenceNumber(), 0xD);
}
-TEST(NpFfiDeserializeV0Tests, V0MultipleDataElements) {
+TEST_F(NpCppTest, V0MultipleDataElements) {
nearby_protocol::RawAdvertisementPayload adv(nearby_protocol::ByteBuffer<255>(
{7,
{
@@ -195,11 +209,11 @@
ASSERT_EQ(second_de.GetKind(), nearby_protocol::V0DataElementKind::Actions);
auto actions = second_de.AsActions();
- ASSERT_EQ(actions.GetAsU32(), 0x00460000);
- ASSERT_EQ(actions.GetContextSyncSequenceNumber(), 0);
+ ASSERT_EQ(actions.GetAsU32(), (uint32_t)0x00460000);
+ ASSERT_EQ(actions.GetContextSyncSequenceNumber(), (uint8_t)0);
}
-TEST(NpFfiDeserializeV0Tests, V0EmptyPayload) {
+TEST_F(NpCppTest, V0EmptyPayload) {
auto maybe_credential_book = nearby_protocol::CredentialBook::TryCreate();
ASSERT_TRUE(maybe_credential_book.ok());
auto deserialize_result =
@@ -210,10 +224,10 @@
nearby_protocol::DeserializeAdvertisementResultKind::Error);
}
-TEST(NpFfiDeserializeV0Tests, TestV0AdvMoveConstructor) {
+TEST_F(NpCppTest, TestV0AdvMoveConstructor) {
auto book = nearby_protocol::CredentialBook::TryCreate().value();
- auto result =
- nearby_protocol::Deserializer::DeserializeAdvertisement(V0AdvSimple, book);
+ auto result = nearby_protocol::Deserializer::DeserializeAdvertisement(
+ V0AdvSimple, book);
ASSERT_EQ(result.GetKind(),
np_ffi::internal::DeserializeAdvertisementResultKind::V0);
auto adv = result.IntoV0();
@@ -237,17 +251,17 @@
ASSERT_DEATH([[maybe_unused]] auto failure = moved_again.GetKind(), "");
}
-TEST(NpFfiDeserializeResultTests, TestV0AdvMoveAssignment) {
+TEST_F(NpCppTest, TestV0AdvMoveAssignment) {
auto book = nearby_protocol::CredentialBook::TryCreate().value();
- auto result =
- nearby_protocol::Deserializer::DeserializeAdvertisement(V0AdvSimple, book);
+ auto result = nearby_protocol::Deserializer::DeserializeAdvertisement(
+ V0AdvSimple, book);
ASSERT_EQ(result.GetKind(),
np_ffi::internal::DeserializeAdvertisementResultKind::V0);
auto adv = result.IntoV0();
// create a second result
- auto another_result =
- nearby_protocol::Deserializer::DeserializeAdvertisement(V0AdvSimple, book);
+ auto another_result = nearby_protocol::Deserializer::DeserializeAdvertisement(
+ V0AdvSimple, book);
ASSERT_EQ(another_result.GetKind(),
np_ffi::internal::DeserializeAdvertisementResultKind::V0);
auto adv2 = another_result.IntoV0();
@@ -270,39 +284,48 @@
ASSERT_DEATH([[maybe_unused]] auto failure = moved_again.GetKind(), "");
}
-TEST(NpFfiDeserializeV0Tests, V0AdvDestructor) {
- nearby_protocol::GlobalConfig::SetMaxNumDeserializedV0Advertisements(1);
+static nearby_protocol::DeserializeAdvertisementResult
+CreateAdv(nearby_protocol::CredentialBook &book) {
+ auto adv = nearby_protocol::Deserializer::DeserializeAdvertisement(
+ V0AdvSimple, book);
+ return adv;
+}
+
+TEST_F(NpCppTest, V0AdvDestructor) {
auto book_result = nearby_protocol::CredentialBook::TryCreate();
ASSERT_TRUE(book_result.ok());
{
- auto deserialize_result =
- nearby_protocol::Deserializer::DeserializeAdvertisement(
- V0AdvSimple, book_result.value());
+ auto deserialize_result = CreateAdv(book_result.value());
+ auto deserialize_result2 = CreateAdv(book_result.value());
+ // Deserialize 2 advertisements, which will take up 2 slots in the handle
+ // map
ASSERT_EQ(deserialize_result.GetKind(),
np_ffi::internal::DeserializeAdvertisementResultKind::V0);
+ ASSERT_EQ(deserialize_result2.GetKind(),
+ np_ffi::internal::DeserializeAdvertisementResultKind::V0);
// Going over max amount should result in error
- auto deserialize_result2 =
+ auto deserialize_result3 =
nearby_protocol::Deserializer::DeserializeAdvertisement(
V0AdvSimple, book_result.value());
- ASSERT_EQ(deserialize_result2.GetKind(),
+ ASSERT_EQ(deserialize_result3.GetKind(),
np_ffi::internal::DeserializeAdvertisementResultKind::Error);
- // Calling IntoV0() should move the underlying resources into the v0 object
- // when both go out of scope only one should be freed
+ // Calling IntoV0() should move the underlying resources into the v0
+ // object when both go out of scope only one should be freed
auto v0_adv = deserialize_result.IntoV0();
}
// Now that the first v0 adv is out of scope, it should be de-allocated which
// will create room for one more to be created.
- auto deserialize_result3 =
+ auto deserialize_result =
nearby_protocol::Deserializer::DeserializeAdvertisement(
V0AdvSimple, book_result.value());
- ASSERT_EQ(deserialize_result3.GetKind(),
+ ASSERT_EQ(deserialize_result.GetKind(),
np_ffi::internal::DeserializeAdvertisementResultKind::V0);
}
-TEST(NpFfiDeserializeV0Tests, V0AdvUseAfterMove) {
+TEST_F(NpCppTest, V0AdvUseAfterMove) {
auto maybe_credential_book = nearby_protocol::CredentialBook::TryCreate();
ASSERT_TRUE(maybe_credential_book.ok());
auto deserialize_result =
@@ -322,10 +345,10 @@
ASSERT_DEATH([[maybe_unused]] auto failure = v0_adv.IntoLegible(), "");
}
-TEST(NpFfiDeserializeV0Tests, TestLegibleAdvMoveConstructor) {
+TEST_F(NpCppTest, TestLegibleAdvMoveConstructor) {
auto book = nearby_protocol::CredentialBook::TryCreate().value();
- auto result =
- nearby_protocol::Deserializer::DeserializeAdvertisement(V0AdvSimple, book);
+ auto result = nearby_protocol::Deserializer::DeserializeAdvertisement(
+ V0AdvSimple, book);
ASSERT_EQ(result.GetKind(),
np_ffi::internal::DeserializeAdvertisementResultKind::V0);
auto legible = result.IntoV0().IntoLegible();
@@ -356,17 +379,17 @@
ASSERT_DEATH([[maybe_unused]] auto failure = moved_again.IntoPayload(), "");
}
-TEST(NpFfiDeserializeResultTests, TestLegibleAdvMoveAssignment) {
+TEST_F(NpCppTest, TestLegibleAdvMoveAssignment) {
auto book = nearby_protocol::CredentialBook::TryCreate().value();
- auto result =
- nearby_protocol::Deserializer::DeserializeAdvertisement(V0AdvSimple, book);
+ auto result = nearby_protocol::Deserializer::DeserializeAdvertisement(
+ V0AdvSimple, book);
ASSERT_EQ(result.GetKind(),
np_ffi::internal::DeserializeAdvertisementResultKind::V0);
auto legible = result.IntoV0().IntoLegible();
// create a second result
- auto another_result =
- nearby_protocol::Deserializer::DeserializeAdvertisement(V0AdvSimple, book);
+ auto another_result = nearby_protocol::Deserializer::DeserializeAdvertisement(
+ V0AdvSimple, book);
ASSERT_EQ(another_result.GetKind(),
np_ffi::internal::DeserializeAdvertisementResultKind::V0);
auto legible2 = another_result.IntoV0().IntoLegible();
@@ -402,7 +425,7 @@
return v0_adv.IntoLegible();
}
-TEST(NpFfiDeserializeV0Tests, V0LegibleAdvUseAfterMove) {
+TEST_F(NpCppTest, V0LegibleAdvUseAfterMove) {
auto book = nearby_protocol::CredentialBook::TryCreate().value();
auto legible_adv = CreateLegibleAdv(book);
@@ -422,16 +445,19 @@
ASSERT_DEATH([[maybe_unused]] auto failure = legible_adv.IntoPayload(), "");
}
-TEST(NpFfiDeserializeV0Tests, LegibleAdvDestructor) {
+TEST_F(NpCppTest, LegibleAdvDestructor) {
auto book = nearby_protocol::CredentialBook::TryCreate().value();
- nearby_protocol::GlobalConfig::SetMaxNumDeserializedV0Advertisements(1);
{
auto legible_adv = CreateLegibleAdv(book);
+ auto legible_adv2 = CreateLegibleAdv(book);
- // check that legible adv is valid.
+ // check that legible advs are valid.
ASSERT_EQ(legible_adv.GetIdentity().GetKind(),
nearby_protocol::DeserializedV0IdentityKind::Plaintext);
ASSERT_EQ(legible_adv.GetNumberOfDataElements(), 1);
+ ASSERT_EQ(legible_adv2.GetIdentity().GetKind(),
+ nearby_protocol::DeserializedV0IdentityKind::Plaintext);
+ ASSERT_EQ(legible_adv2.GetNumberOfDataElements(), 1);
// allocation slots should be full
ASSERT_EQ(nearby_protocol::Deserializer::DeserializeAdvertisement(
@@ -453,14 +479,15 @@
return legible_adv.IntoPayload();
}
-TEST(NpFfiDeserializeV0Tests, V0PayloadDestructor) {
+TEST_F(NpCppTest, V0PayloadDestructor) {
auto book = nearby_protocol::CredentialBook::TryCreate().value();
- nearby_protocol::GlobalConfig::SetMaxNumDeserializedV0Advertisements(1);
{
auto payload = CreatePayload(book);
+ auto payload2 = CreatePayload(book);
// check that payload adv is valid even though its parent is out of scope
ASSERT_TRUE(payload.TryGetDataElement(0).ok());
+ ASSERT_TRUE(payload2.TryGetDataElement(0).ok());
// allocation slots should be full
ASSERT_EQ(nearby_protocol::Deserializer::DeserializeAdvertisement(
@@ -477,7 +504,7 @@
nearby_protocol::DeserializeAdvertisementResultKind::V0);
}
-TEST(NpFfiDeserializeV0Tests, TestV0PayloadMoveConstructor) {
+TEST_F(NpCppTest, TestV0PayloadMoveConstructor) {
auto book = nearby_protocol::CredentialBook::TryCreate().value();
auto result = nearby_protocol::Deserializer::DeserializeAdvertisement(
V0AdvSimple, book);
@@ -503,7 +530,7 @@
"");
}
-TEST(NpFfiDeserializeResultTests, TestV0PayloadMoveAssignment) {
+TEST_F(NpCppTest, TestV0PayloadMoveAssignment) {
auto book = nearby_protocol::CredentialBook::TryCreate().value();
auto result = nearby_protocol::Deserializer::DeserializeAdvertisement(
V0AdvSimple, book);
@@ -534,10 +561,7 @@
"");
}
-TEST(NpFfiDeserializeV0Tests, InvalidDataElementCast) {
- ASSERT_TRUE(
- nearby_protocol::GlobalConfig::SetPanicHandler(test_panic_handler));
-
+TEST_F(NpCppTest, InvalidDataElementCast) {
auto maybe_credential_book = nearby_protocol::CredentialBook::TryCreate();
ASSERT_TRUE(maybe_credential_book.ok());
auto deserialize_result =
@@ -566,7 +590,7 @@
ASSERT_DEATH([[maybe_unused]] auto failure = de.AsActions();, "");
}
-TEST(NpFfiDeserializeV0Tests, InvalidDataElementIndex) {
+TEST_F(NpCppTest, InvalidDataElementIndex) {
auto maybe_credential_book = nearby_protocol::CredentialBook::TryCreate();
ASSERT_TRUE(maybe_credential_book.ok());
auto deserialize_result =
diff --git a/nearby/presence/np_cpp_ffi/tests/deserialize_v1_tests.cc b/nearby/presence/np_cpp_ffi/tests/deserialize_v1_tests.cc
index 115ad14..f46457e 100644
--- a/nearby/presence/np_cpp_ffi/tests/deserialize_v1_tests.cc
+++ b/nearby/presence/np_cpp_ffi/tests/deserialize_v1_tests.cc
@@ -14,10 +14,11 @@
#include "nearby_protocol.h"
#include "shared_test_util.h"
+#include "np_cpp_test.h"
#include "gtest/gtest.h"
-TEST(NpFfiDeserializeV1Tests, V1SimpleTestCase) {
+TEST_F(NpCppTest, V1SimpleTestCase) {
auto maybe_credential_book = nearby_protocol::CredentialBook::TryCreate();
ASSERT_TRUE(maybe_credential_book.ok());
@@ -47,7 +48,7 @@
auto de = section.value().TryGetDataElement(0);
ASSERT_TRUE(de.ok());
- ASSERT_EQ(de.value().GetDataElementTypeCode(), 5);
+ ASSERT_EQ(de.value().GetDataElementTypeCode(), (uint8_t)5);
auto payload = de.value().GetPayload();
auto vec = payload.ToVector();
@@ -55,7 +56,7 @@
ASSERT_EQ(vec, expected);
}
-TEST(NpFfiDeserializeV1Tests, TestV1AdvMoveConstructor) {
+TEST_F(NpCppTest, TestV1AdvMoveConstructor) {
auto book = nearby_protocol::CredentialBook::TryCreate().value();
auto result = nearby_protocol::Deserializer::DeserializeAdvertisement(
V1AdvSimple, book);
@@ -91,7 +92,7 @@
"");
}
-TEST(NpFfiDeserializeV1Tests, TestV1AdvMoveAssignment) {
+TEST_F(NpCppTest, TestV1AdvMoveAssignment) {
auto book = nearby_protocol::CredentialBook::TryCreate().value();
auto result = nearby_protocol::Deserializer::DeserializeAdvertisement(
V1AdvSimple, book);
@@ -151,9 +152,7 @@
np_ffi::internal::DeserializeAdvertisementResultKind::V1;
}
-TEST(NpFfiDeserializeV1Tests, TestSectionOwnership) {
- // Capping the max number so we can verify that de-allocations are happening
- nearby_protocol::GlobalConfig::SetMaxNumDeserializedV1Advertisements(1);
+TEST_F(NpCppTest, TestSectionOwnership) {
auto maybe_credential_book = nearby_protocol::CredentialBook::TryCreate();
ASSERT_TRUE(maybe_credential_book.ok());
@@ -164,6 +163,12 @@
ASSERT_EQ(section.NumberOfDataElements(), 1);
ASSERT_TRUE(section.TryGetDataElement(0).ok());
+ auto section2 = GetSection(maybe_credential_book.value());
+ ASSERT_EQ(section2.GetIdentityKind(),
+ nearby_protocol::DeserializedV1IdentityKind::Plaintext);
+ ASSERT_EQ(section2.NumberOfDataElements(), 1);
+ ASSERT_TRUE(section2.TryGetDataElement(0).ok());
+
ASSERT_FALSE(TryDeserializeNewV1Adv(maybe_credential_book.value()));
}
@@ -175,7 +180,7 @@
/*
* Multiple sections are not supported in plaintext advertisements
* TODO Update the below test to use encrypted sections
-TEST(NpFfiDeserializeV1Tests, V1MultipleSections) {
+TEST(NpCppTest, V1MultipleSections) {
auto maybe_credential_book = nearby_protocol::CredentialBook::TryCreate();
ASSERT_TRUE(maybe_credential_book.ok());
diff --git a/nearby/presence/np_cpp_ffi/tests/global_config_tests.cc b/nearby/presence/np_cpp_ffi/tests/global_config_tests.cc
deleted file mode 100644
index 6936f23..0000000
--- a/nearby/presence/np_cpp_ffi/tests/global_config_tests.cc
+++ /dev/null
@@ -1,205 +0,0 @@
-// Copyright 2023 Google LLC
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-// http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-#include "nearby_protocol.h"
-#include "shared_test_util.h"
-
-#include <iostream>
-
-#include "gtest/gtest.h"
-
-TEST(NpFfiGlobalConfigTests, TestPanicHandler) {
- ASSERT_TRUE(
- nearby_protocol::GlobalConfig::SetPanicHandler(test_panic_handler));
- auto book = nearby_protocol::CredentialBook::TryCreate().value();
- auto deserialize_result =
- nearby_protocol::Deserializer::DeserializeAdvertisement(V0AdvSimple, book);
- ASSERT_EQ(deserialize_result.GetKind(),
- np_ffi::internal::DeserializeAdvertisementResultKind::V0);
-
- // Now try to cast the result into the wrong type and verify the process
- // aborts
- ASSERT_DEATH({ [[maybe_unused]] auto failure = deserialize_result.IntoV1(); },
- "");
-}
-
-TEST(NpFfiGlobalConfigTests, TestPanicHandlerTwice) {
- ASSERT_TRUE(
- nearby_protocol::GlobalConfig::SetPanicHandler(test_panic_handler));
-
- // Second time trying to set should fail
- ASSERT_FALSE(
- nearby_protocol::GlobalConfig::SetPanicHandler(test_panic_handler));
-}
-
-// There is not much we can actually test here since this will affect memory
-// consumption. This is more of just a simple check that things still work after
-// configuring this
-TEST(NpFfiGlobalConfigTests, TestSetMaxShardsDefault) {
- // 0 should still work as default behavior
- nearby_protocol::GlobalConfig::SetNumShards(0);
-
- auto book = nearby_protocol::CredentialBook::TryCreate().value();
- auto book2 = nearby_protocol::CredentialBook::TryCreate().value();
- auto book3 = nearby_protocol::CredentialBook::TryCreate().value();
- auto deserialize_result =
- nearby_protocol::Deserializer::DeserializeAdvertisement(V0AdvSimple, book);
-
- // Should still work
- ASSERT_EQ(deserialize_result.GetKind(),
- np_ffi::internal::DeserializeAdvertisementResultKind::V0);
-
- // Call again with a lower number, should have no effect. books 2 and 3 should
- // still work.
- nearby_protocol::GlobalConfig::SetNumShards(1);
- auto deserialize_result_2 =
- nearby_protocol::Deserializer::DeserializeAdvertisement(V0AdvSimple,
- book2);
- ASSERT_EQ(deserialize_result_2.GetKind(),
- np_ffi::internal::DeserializeAdvertisementResultKind::V0);
- auto deserialize_result_3 =
- nearby_protocol::Deserializer::DeserializeAdvertisement(V0AdvSimple,
- book3);
- ASSERT_EQ(deserialize_result_3.GetKind(),
- np_ffi::internal::DeserializeAdvertisementResultKind::V0);
-}
-
-TEST(NpFfiGlobalConfigTests, TestSetMaxShardsSmall) {
- nearby_protocol::GlobalConfig::SetNumShards(1);
- auto book = nearby_protocol::CredentialBook::TryCreate().value();
-
- // should still be able to parse 2 payloads with only one shard
- auto deserialize_result1 =
- nearby_protocol::Deserializer::DeserializeAdvertisement(V0AdvSimple, book);
- ASSERT_EQ(deserialize_result1.GetKind(),
- np_ffi::internal::DeserializeAdvertisementResultKind::V0);
- auto deserialize_result2 =
- nearby_protocol::Deserializer::DeserializeAdvertisement(V0AdvSimple, book);
- ASSERT_EQ(deserialize_result2.GetKind(),
- np_ffi::internal::DeserializeAdvertisementResultKind::V0);
-}
-
-TEST(NpFfiGlobalConfigTests, TestSetMaxCredBooks) {
- nearby_protocol::GlobalConfig::SetMaxNumCredentialBooks(1);
- auto book1_result = nearby_protocol::CredentialBook::TryCreate();
- ASSERT_TRUE(book1_result.ok());
-
- auto book2_result = nearby_protocol::CredentialBook::TryCreate();
- ASSERT_FALSE(book2_result.ok());
- ASSERT_TRUE(absl::IsResourceExhausted(book2_result.status()));
-}
-
-TEST(NpFfiGlobalConfigTests, TestSetMaxCredBooksAfterFirstCall) {
- auto book = nearby_protocol::CredentialBook::TryCreate().value();
- auto book2 = nearby_protocol::CredentialBook::TryCreate().value();
- auto book3 = nearby_protocol::CredentialBook::TryCreate().value();
-
- // setting this after books have already been created should have no affect
- nearby_protocol::GlobalConfig::SetMaxNumCredentialBooks(1);
- auto book4_result = nearby_protocol::CredentialBook::TryCreate();
- ASSERT_TRUE(book4_result.ok());
-}
-
-TEST(NpFfiGlobalConfigTests, TestSetMaxV0Advs) {
- nearby_protocol::GlobalConfig::SetMaxNumDeserializedV0Advertisements(1);
- auto book_result = nearby_protocol::CredentialBook::TryCreate();
- ASSERT_TRUE(book_result.ok());
-
- {
- auto deserialize_result =
- nearby_protocol::Deserializer::DeserializeAdvertisement(
- V0AdvSimple, book_result.value());
- ASSERT_EQ(deserialize_result.GetKind(),
- np_ffi::internal::DeserializeAdvertisementResultKind::V0);
-
- // Going over max amount should result in error
- auto deserialize_result2 =
- nearby_protocol::Deserializer::DeserializeAdvertisement(
- V0AdvSimple, book_result.value());
- ASSERT_EQ(deserialize_result2.GetKind(),
- np_ffi::internal::DeserializeAdvertisementResultKind::Error);
- }
-
- // Now that the first v0 adv is out of scope, it will be de-allocated which
- // will create room for one more to be created.
- auto deserialize_result3 =
- nearby_protocol::Deserializer::DeserializeAdvertisement(
- V0AdvSimple, book_result.value());
- ASSERT_EQ(deserialize_result3.GetKind(),
- np_ffi::internal::DeserializeAdvertisementResultKind::V0);
-}
-
-TEST(NpFfiGlobalConfigTests, TestSetMaxV1Advs) {
- nearby_protocol::GlobalConfig::SetMaxNumDeserializedV1Advertisements(1);
- auto book_result = nearby_protocol::CredentialBook::TryCreate();
- ASSERT_TRUE(book_result.ok());
-
- {
- auto deserialize_result =
- nearby_protocol::Deserializer::DeserializeAdvertisement(
- V1AdvSimple, book_result.value());
- ASSERT_EQ(deserialize_result.GetKind(),
- np_ffi::internal::DeserializeAdvertisementResultKind::V1);
-
- // Going over max amount should result in error
- auto deserialize_result2 =
- nearby_protocol::Deserializer::DeserializeAdvertisement(
- V1AdvSimple, book_result.value());
- ASSERT_EQ(deserialize_result2.GetKind(),
- np_ffi::internal::DeserializeAdvertisementResultKind::Error);
- }
-
- // Now that the first v1 adv is out of scope, it will be de-allocated which
- // will create room for one more to be created.
- auto deserialize_result3 =
- nearby_protocol::Deserializer::DeserializeAdvertisement(
- V1AdvSimple, book_result.value());
- ASSERT_EQ(deserialize_result3.GetKind(),
- np_ffi::internal::DeserializeAdvertisementResultKind::V1);
-}
-
-// Same test case as above, but verifies that the de-allocation still succeeds
-// after calling IntoV1() and that no double frees occur.
-TEST(NpFfiGlobalConfigTests, TestSetMaxV1AdvsFreeAfterInto) {
- nearby_protocol::GlobalConfig::SetMaxNumDeserializedV1Advertisements(1);
- auto book_result = nearby_protocol::CredentialBook::TryCreate();
- ASSERT_TRUE(book_result.ok());
-
- {
- auto deserialize_result =
- nearby_protocol::Deserializer::DeserializeAdvertisement(
- V1AdvSimple, book_result.value());
- ASSERT_EQ(deserialize_result.GetKind(),
- np_ffi::internal::DeserializeAdvertisementResultKind::V1);
-
- // Going over max amount should result in error
- auto deserialize_result2 =
- nearby_protocol::Deserializer::DeserializeAdvertisement(
- V1AdvSimple, book_result.value());
- ASSERT_EQ(deserialize_result2.GetKind(),
- np_ffi::internal::DeserializeAdvertisementResultKind::Error);
-
- // Calling IntoV1() should move the underlying resources into the v0 object
- // when both go out of scope only one should be freed
- auto v0_adv = deserialize_result.IntoV1();
- }
-
- // Now that the first v1 adv is out of scope, it will be de-allocated which
- // will create room for one more to be created.
- auto deserialize_result3 =
- nearby_protocol::Deserializer::DeserializeAdvertisement(
- V1AdvSimple, book_result.value());
- ASSERT_EQ(deserialize_result3.GetKind(),
- np_ffi::internal::DeserializeAdvertisementResultKind::V1);
-}
\ No newline at end of file
diff --git a/nearby/presence/np_cpp_ffi/tests/np_cpp_test.cc b/nearby/presence/np_cpp_ffi/tests/np_cpp_test.cc
new file mode 100644
index 0000000..52049e7
--- /dev/null
+++ b/nearby/presence/np_cpp_ffi/tests/np_cpp_test.cc
@@ -0,0 +1,17 @@
+// Copyright 2023 Google LLC
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include "np_cpp_test.h"
+
+bool NpCppTest::panic_handler_set = false;
\ No newline at end of file
diff --git a/nearby/presence/np_cpp_ffi/tests/np_cpp_test.h b/nearby/presence/np_cpp_ffi/tests/np_cpp_test.h
new file mode 100644
index 0000000..4fc20e1
--- /dev/null
+++ b/nearby/presence/np_cpp_ffi/tests/np_cpp_test.h
@@ -0,0 +1,41 @@
+// Copyright 2023 Google LLC
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#ifndef NEARBY_PRESENCE_NP_CPP_FFI_TESTS_NP_CPP_TEST_H_
+#define NEARBY_PRESENCE_NP_CPP_FFI_TESTS_NP_CPP_TEST_H_
+
+#include "nearby_protocol.h"
+#include "shared_test_util.h"
+
+#include <gtest/gtest.h>
+
+class NpCppTest : public testing::Test {
+protected:
+ static void SetUpTestSuite() {
+ if (!panic_handler_set) {
+ ASSERT_TRUE(
+ nearby_protocol::GlobalConfig::SetPanicHandler(test_panic_handler));
+ panic_handler_set = true;
+ nearby_protocol::GlobalConfig::SetMaxNumDeserializedV0Advertisements(2);
+ nearby_protocol::GlobalConfig::SetMaxNumDeserializedV1Advertisements(2);
+ nearby_protocol::GlobalConfig::SetMaxNumCredentialBooks(2);
+ } else {
+ ASSERT_FALSE(
+ nearby_protocol::GlobalConfig::SetPanicHandler(test_panic_handler));
+ }
+ }
+ static bool panic_handler_set;
+};
+
+#endif // NEARBY_PRESENCE_NP_CPP_FFI_TESTS_NP_CPP_TEST_H_
diff --git a/nearby/presence/np_ffi_core/Cargo.toml b/nearby/presence/np_ffi_core/Cargo.toml
index cdcc7d3..126302f 100644
--- a/nearby/presence/np_ffi_core/Cargo.toml
+++ b/nearby/presence/np_ffi_core/Cargo.toml
@@ -6,7 +6,7 @@
[dependencies]
array_view.workspace = true
-np_adv.workspace = true
+np_adv = { workspace = true, features = ["alloc"] }
handle_map.workspace = true
crypto_provider.workspace = true
crypto_provider_default = {workspace = true, default-features = false}
diff --git a/nearby/presence/np_ffi_core/src/deserialize/mod.rs b/nearby/presence/np_ffi_core/src/deserialize/mod.rs
index 9c20fde..38f59ba 100644
--- a/nearby/presence/np_ffi_core/src/deserialize/mod.rs
+++ b/nearby/presence/np_ffi_core/src/deserialize/mod.rs
@@ -87,7 +87,7 @@
V1(DeserializedV1Advertisement),
}
-struct DeserializeAdvertisementError;
+pub(crate) struct DeserializeAdvertisementError;
impl From<HandleMapFullError> for DeserializeAdvertisementError {
fn from(_: HandleMapFullError) -> Self {
diff --git a/nearby/presence/np_ffi_core/src/deserialize/v0.rs b/nearby/presence/np_ffi_core/src/deserialize/v0.rs
index 7b9ee75..d8c008a 100644
--- a/nearby/presence/np_ffi_core/src/deserialize/v0.rs
+++ b/nearby/presence/np_ffi_core/src/deserialize/v0.rs
@@ -68,7 +68,7 @@
pub(crate) fn allocate_with_contents<M: np_adv::credential::MatchedCredential>(
contents: np_adv::V0AdvertisementContents<M>,
- ) -> Result<Self, HandleMapFullError> {
+ ) -> Result<Self, DeserializeAdvertisementError> {
match contents {
np_adv::V0AdvertisementContents::Plaintext(plaintext_contents) => {
let adv = LegibleDeserializedV0Advertisement::allocate_with_plaintext_contents(
@@ -99,8 +99,11 @@
impl LegibleDeserializedV0Advertisement {
pub(crate) fn allocate_with_plaintext_contents(
contents: np_adv::legacy::deserialize::PlaintextAdvContents,
- ) -> Result<Self, HandleMapFullError> {
- let data_elements = contents.to_data_elements();
+ ) -> Result<Self, DeserializeAdvertisementError> {
+ let data_elements = contents
+ .data_elements()
+ .collect::<Result<Vec<_>, _>>()
+ .map_err(|_| DeserializeAdvertisementError)?;
let num_des = data_elements.len() as u8;
let payload = V0Payload::allocate_with_data_elements(data_elements)?;
Ok(Self { num_des, payload, identity: DeserializedV0Identity::Plaintext })
@@ -187,6 +190,8 @@
}
use v0_payload::V0Payload;
+use super::DeserializeAdvertisementError;
+
impl LocksLongerThan<V0Payload> for CredentialBook {}
impl V0Payload {
diff --git a/nearby/presence/np_ffi_core/src/deserialize/v1.rs b/nearby/presence/np_ffi_core/src/deserialize/v1.rs
index be44fb7..819bb8c 100644
--- a/nearby/presence/np_ffi_core/src/deserialize/v1.rs
+++ b/nearby/presence/np_ffi_core/src/deserialize/v1.rs
@@ -13,11 +13,13 @@
// limitations under the License.
//! Core NP Rust FFI structures and methods for v1 advertisement deserialization.
+use super::DeserializeAdvertisementError;
use crate::common::*;
use crate::credentials::credential_book::CredentialBook;
use crate::utils::*;
use array_view::ArrayView;
-use handle_map::{declare_handle_map, HandleLike, HandleMapDimensions, HandleMapFullError};
+use handle_map::{declare_handle_map, HandleLike, HandleMapDimensions};
+use legible_v1_sections::LegibleV1Sections;
use np_adv::extended::deserialize::DataElementParseError;
use std::vec::Vec;
@@ -59,12 +61,13 @@
pub(crate) fn allocate_with_contents<M: np_adv::credential::MatchedCredential>(
contents: np_adv::V1AdvertisementContents<M>,
- ) -> Result<Self, HandleMapFullError> {
+ ) -> Result<Self, DeserializeAdvertisementError> {
// 16-section limit enforced by np_adv
let num_undecryptable_sections = contents.invalid_sections_count() as u8;
let legible_sections = contents.into_sections();
let num_legible_sections = legible_sections.len() as u8;
- let legible_sections = LegibleV1Sections::allocate_with_contents(legible_sections)?;
+ let legible_sections =
+ LegibleV1Sections::allocate_with_contents(legible_sections.into_vec())?;
Ok(Self { num_undecryptable_sections, num_legible_sections, legible_sections })
}
}
@@ -107,14 +110,18 @@
}
impl<'adv, M: np_adv::credential::MatchedCredential>
- From<Vec<np_adv::V1DeserializedSection<'adv, M>>> for LegibleV1SectionsInternals
+ TryFrom<Vec<np_adv::V1DeserializedSection<'adv, M>>> for LegibleV1SectionsInternals
{
- fn from(contents: Vec<np_adv::V1DeserializedSection<'adv, M>>) -> Self {
+ type Error = DataElementParseError;
+
+ fn try_from(
+ contents: Vec<np_adv::V1DeserializedSection<'adv, M>>,
+ ) -> Result<Self, Self::Error> {
let sections = contents
.into_iter()
- .filter_map(|s| DeserializedV1SectionInternals::try_from(s).ok())
- .collect();
- Self { sections }
+ .map(DeserializedV1SectionInternals::try_from)
+ .collect::<Result<Vec<_>, _>>()?;
+ Ok(Self { sections })
}
}
@@ -131,15 +138,16 @@
type LegibleV1Sections: HandleLike<Object = super::LegibleV1SectionsInternals>;
}
}
-use legible_v1_sections::LegibleV1Sections;
impl LocksLongerThan<LegibleV1Sections> for CredentialBook {}
impl LegibleV1Sections {
pub(crate) fn allocate_with_contents<M: np_adv::credential::MatchedCredential>(
contents: Vec<np_adv::V1DeserializedSection<M>>,
- ) -> Result<Self, HandleMapFullError> {
- Self::allocate(move || LegibleV1SectionsInternals::from(contents))
+ ) -> Result<Self, DeserializeAdvertisementError> {
+ let section = LegibleV1SectionsInternals::try_from(contents)
+ .map_err(|_| DeserializeAdvertisementError)?;
+ Self::allocate(move || section).map_err(|e| e.into())
}
}
diff --git a/nearby/util/handle_map/Cargo.toml b/nearby/util/handle_map/Cargo.toml
index ee9dbf3..fbe3a20 100644
--- a/nearby/util/handle_map/Cargo.toml
+++ b/nearby/util/handle_map/Cargo.toml
@@ -6,7 +6,6 @@
[dependencies]
lock_adapter.workspace = true
-crypto_provider.workspace = true
[dev-dependencies]
criterion.workspace = true