1 // Copyright 2022 Google LLC
2 //
3 // Licensed under the Apache License, Version 2.0 (the "License");
4 // you may not use this file except in compliance with the License.
5 // You may obtain a copy of the License at
6 //
7 //      http://www.apache.org/licenses/LICENSE-2.0
8 //
9 // Unless required by applicable law or agreed to in writing, software
10 // distributed under the License is distributed on an "AS IS" BASIS,
11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 // See the License for the specific language governing permissions and
13 // limitations under the License.
14 
15 //! V1 advertisement support.
16 use crate::DeLengthOutOfRange;
17 use array_view::ArrayView;
18 use crypto_provider::CryptoRng;
19 
20 pub mod data_elements;
21 pub mod de_type;
22 pub mod deserialize;
23 pub mod salt;
24 pub mod section_signature_payload;
25 pub mod serialize;
26 
27 // TODO make this easy to use w/ configurable arena size
28 /// Maximum size of an NP advertisement, including the adv header
29 pub const BLE_5_ADV_SVC_MAX_CONTENT_LEN: usize = 254
30     // length and type bytes for svc data TLV
31     - 1 - 1
32     // NP UUID
33     - 2;
34 
35 /// Maximum number of sections in an advertisement
36 pub const NP_V1_ADV_MAX_ENCRYPTED_SECTION_COUNT: usize = 8;
37 
38 /// Maximum number of public sections in an advertisement
39 pub const NP_V1_ADV_MAX_PUBLIC_SECTION_COUNT: usize = 1;
40 
41 /// Maximum size of a NP section, including its length header byte
42 pub const NP_ADV_MAX_SECTION_LEN: usize = NP_ADV_MAX_SECTION_CONTENTS_LEN + 1;
43 
44 // TODO should this be 255 (or 256, if we +1 the length)?
45 /// Maximum hypothetical size of a NP section's contents, excluding its header
46 /// byte. This is longer than can fit in a BLE 5 extended adv, but other media
47 /// could fit it, like mDNS.
48 const NP_ADV_MAX_SECTION_CONTENTS_LEN: usize = 255;
49 
50 /// Size of a V1 identity token
51 pub const V1_IDENTITY_TOKEN_LEN: usize = 16;
52 
53 // 4-bit encoding ids
54 /// Encoding ID for unencrypted sections with no salt
55 pub const V1_ENCODING_UNENCRYPTED: u8 = 0x00;
56 /// Encoding ID for encrypted sections with a MIC and a short salt
57 pub const V1_ENCODING_ENCRYPTED_MIC_WITH_SHORT_SALT_AND_TOKEN: u8 = 0x01;
58 /// Encoding ID for encrypted sections with a MIC and an extended salt
59 pub const V1_ENCODING_ENCRYPTED_MIC_WITH_EXTENDED_SALT_AND_TOKEN: u8 = 0x02;
60 /// Encoding ID for encrypted sections with a signature and an extended salt
61 pub const V1_ENCODING_ENCRYPTED_SIGNATURE_WITH_EXTENDED_SALT_AND_TOKEN: u8 = 0x03;
62 
63 // The maximum de length that fits into a non-extended de header
64 const MAX_NON_EXTENDED_LEN: u8 = 7;
65 // The maximum type code that fits into a non-extended de header
66 const MAX_NON_EXTENDED_TYPE_CODE: u32 = 15;
67 
de_requires_extended_bit(type_code: u32, de_len: u8) -> bool68 fn de_requires_extended_bit(type_code: u32, de_len: u8) -> bool {
69     de_len > MAX_NON_EXTENDED_LEN || type_code > MAX_NON_EXTENDED_TYPE_CODE
70 }
71 
72 /// 16-byte plaintext identity token.
73 ///
74 /// Identity tokens are present in encrypted form in a section's header.
75 #[derive(Debug, Clone, Copy, Hash, Eq, PartialEq)]
76 pub struct V1IdentityToken([u8; V1_IDENTITY_TOKEN_LEN]);
77 
78 impl From<[u8; V1_IDENTITY_TOKEN_LEN]> for V1IdentityToken {
from(value: [u8; V1_IDENTITY_TOKEN_LEN]) -> Self79     fn from(value: [u8; V1_IDENTITY_TOKEN_LEN]) -> Self {
80         Self(value)
81     }
82 }
83 
84 impl V1IdentityToken {
85     /// Returns a reference to the inner byte array
bytes(&self) -> &[u8; V1_IDENTITY_TOKEN_LEN]86     pub fn bytes(&self) -> &[u8; V1_IDENTITY_TOKEN_LEN] {
87         &self.0
88     }
89 
90     /// Returns the inner byte array
into_bytes(self) -> [u8; V1_IDENTITY_TOKEN_LEN]91     pub const fn into_bytes(self) -> [u8; V1_IDENTITY_TOKEN_LEN] {
92         self.0
93     }
94 
95     /// Returns the token bytes as a slice
as_slice(&self) -> &[u8]96     pub fn as_slice(&self) -> &[u8] {
97         &self.0
98     }
99 }
100 
101 impl AsRef<[u8]> for V1IdentityToken {
as_ref(&self) -> &[u8]102     fn as_ref(&self) -> &[u8] {
103         &self.0
104     }
105 }
106 
107 impl crypto_provider::FromCryptoRng for V1IdentityToken {
new_random<R: CryptoRng>(rng: &mut R) -> Self108     fn new_random<R: CryptoRng>(rng: &mut R) -> Self {
109         Self(rng.gen())
110     }
111 }
112 
113 /// Max V1 DE length (7 bit length field).
114 pub(crate) const MAX_DE_LEN: usize = 127;
115 
116 /// Length of a DE's content -- must be in `[0, 127]`
117 #[derive(Debug, PartialEq, Eq, Clone, Copy)]
118 pub struct DeLength {
119     len: u8,
120 }
121 
122 impl DeLength {
123     /// A convenient constant for zero length.
124     pub const ZERO: DeLength = DeLength { len: 0 };
125 
as_u8(&self) -> u8126     fn as_u8(&self) -> u8 {
127         self.len
128     }
129 }
130 
131 impl TryFrom<u8> for DeLength {
132     type Error = DeLengthOutOfRange;
133 
try_from(value: u8) -> Result<Self, Self::Error>134     fn try_from(value: u8) -> Result<Self, Self::Error> {
135         if usize::from(value) <= MAX_DE_LEN {
136             Ok(Self { len: value })
137         } else {
138             Err(DeLengthOutOfRange {})
139         }
140     }
141 }
142 
143 impl TryFrom<usize> for DeLength {
144     type Error = DeLengthOutOfRange;
145 
try_from(value: usize) -> Result<Self, Self::Error>146     fn try_from(value: usize) -> Result<Self, Self::Error> {
147         value.try_into().map_err(|_e| DeLengthOutOfRange).and_then(|num: u8| num.try_into())
148     }
149 }
150 
151 /// Convert a tinyvec into an equivalent ArrayView
to_array_view<T, const N: usize>(vec: tinyvec::ArrayVec<[T; N]>) -> ArrayView<T, N> where [T; N]: tinyvec::Array,152 pub(crate) fn to_array_view<T, const N: usize>(vec: tinyvec::ArrayVec<[T; N]>) -> ArrayView<T, N>
153 where
154     [T; N]: tinyvec::Array,
155 {
156     let len = vec.len();
157     ArrayView::try_from_array(vec.into_inner(), len).expect("len is from original vec")
158 }
159 
160 #[cfg(test)]
161 mod tests {
162     use super::*;
163     use rand::{distributions, Rng};
164 
165     // support randomly generating tokens just for tests
166     impl distributions::Distribution<V1IdentityToken> for distributions::Standard {
sample<R: Rng + ?Sized>(&self, rng: &mut R) -> V1IdentityToken167         fn sample<R: Rng + ?Sized>(&self, rng: &mut R) -> V1IdentityToken {
168             V1IdentityToken::from(rng.gen::<[u8; V1_IDENTITY_TOKEN_LEN]>())
169         }
170     }
171 }
172