Project import generated by Copybara. GitOrigin-RevId: 89e6b51eb1f772d98e85f45fc0d5c7fe23230c16 Change-Id: Icf50d4604bba16d6925221eb14132e6e8057dac8
diff --git a/nearby/.gitignore b/nearby/.gitignore index b7d2235..49cb45e 100644 --- a/nearby/.gitignore +++ b/nearby/.gitignore
@@ -3,3 +3,4 @@ /*.mdb /auth_token.txt .DS_Store +presence/cmake-build-debug
diff --git a/nearby/Cargo.lock b/nearby/Cargo.lock index 657ac34..dd4f6ff 100644 --- a/nearby/Cargo.lock +++ b/nearby/Cargo.lock
@@ -780,17 +780,6 @@ ] [[package]] -name = "derive-getters" -version = "0.2.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0122f262bf9c9a367829da84f808d9fb128c10ef283bbe7b0922a77cf07b2747" -dependencies = [ - "proc-macro2", - "quote", - "syn 1.0.109", -] - -[[package]] name = "diff" version = "0.1.13" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -1138,12 +1127,6 @@ ] [[package]] -name = "init_with" -version = "1.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0175f63815ce00183bf755155ad0cb48c65226c5d17a724e369c25418d2b7699" - -[[package]] name = "inout" version = "0.1.3" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -1386,7 +1369,6 @@ "crypto_provider", "crypto_provider_default", "hex", - "init_with", "lazy_static", "ldt", "ldt_np_adv", @@ -1407,6 +1389,17 @@ ] [[package]] +name = "np_adv_dynamic" +version = "0.1.0" +dependencies = [ + "array_view", + "crypto_provider", + "np_adv", + "sink", + "thiserror", +] + +[[package]] name = "np_ed25519" version = "0.1.0" dependencies = [ @@ -1425,8 +1418,10 @@ "crypto_provider_default", "handle_map", "lazy_static", + "ldt_np_adv", "lock_adapter", "np_adv", + "np_hkdf", ] [[package]] @@ -2307,7 +2302,6 @@ dependencies = [ "crypto_provider", "crypto_provider_default", - "derive-getters", "log", "num-bigint", "rand",
diff --git a/nearby/Cargo.toml b/nearby/Cargo.toml index df92f64..774e1d8 100644 --- a/nearby/Cargo.toml +++ b/nearby/Cargo.toml
@@ -20,6 +20,7 @@ "presence/ldt_np_jni", "presence/ldt_tbc", "presence/np_adv", + "presence/np_adv_dynamic", "presence/np_ed25519", "presence/np_ffi_core", "presence/np_hkdf", @@ -40,6 +41,22 @@ "presence/np_c_ffi", ] +[workspace.lints.rust] +unsafe_code = "deny" +missing_docs = "deny" +trivial_casts = "deny" +trivial_numeric_casts = "deny" +unused_extern_crates = "deny" +unused_import_braces = "deny" +unused_results = "deny" + +[workspace.lints.clippy] +indexing_slicing = "deny" +unwrap_used = "deny" +panic = "deny" +expect_used = "deny" + + [workspace.dependencies] # local crates array_ref = { path = "presence/array_ref" } @@ -63,6 +80,7 @@ ldt_np_adv = { path = "presence/ldt_np_adv" } ldt_tbc = { path = "presence/ldt_tbc" } np_adv = { path = "presence/np_adv" } +np_adv_dynamic = { path = "presence/np_adv_dynamic" } np_ed25519 = { path = "presence/np_ed25519" } np_ffi_core = { path = "presence/np_ffi_core" } sink = { path = "presence/sink" } @@ -122,6 +140,7 @@ strum = { version = "0.25.0", default-features=false } strum_macros = { version = "0.25.3", default-features=false } owo-colors = "3.5.0" +rhai = { version = "1.16.3", features = ["sync"] } [workspace.package] version = "0.1.0"
diff --git a/nearby/connections/connections_adv/connections_adv/Cargo.toml b/nearby/connections/connections_adv/connections_adv/Cargo.toml index ff17ad2..2b0710d 100644 --- a/nearby/connections/connections_adv/connections_adv/Cargo.toml +++ b/nearby/connections/connections_adv/connections_adv/Cargo.toml
@@ -4,6 +4,5 @@ edition.workspace = true publish.workspace = true -# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html - -[dependencies] +[lints] +workspace = true \ No newline at end of file
diff --git a/nearby/connections/connections_adv/connections_adv/src/lib.rs b/nearby/connections/connections_adv/connections_adv/src/lib.rs index 4f7198b..4cfcdfa 100644 --- a/nearby/connections/connections_adv/connections_adv/src/lib.rs +++ b/nearby/connections/connections_adv/connections_adv/src/lib.rs
@@ -12,6 +12,9 @@ // See the License for the specific language governing permissions and // limitations under the License. +//! Placeholder crate for connections advertisements + +/// placeholder pub fn add(left: usize, right: usize) -> usize { left + right }
diff --git a/nearby/connections/ukey2/ukey2/Cargo.toml b/nearby/connections/ukey2/ukey2/Cargo.toml index afae71e..161c5b4 100644 --- a/nearby/connections/ukey2/ukey2/Cargo.toml +++ b/nearby/connections/ukey2/ukey2/Cargo.toml
@@ -4,6 +4,9 @@ edition.workspace = true publish.workspace = true +[lints] +workspace = true + [features] default = [] test_rustcrypto = ["crypto_provider_default/rustcrypto"] @@ -16,7 +19,6 @@ ukey2_proto.workspace = true log.workspace = true -derive-getters = "0.2.0" num-bigint = "0.4.3" [dev-dependencies]
diff --git a/nearby/connections/ukey2/ukey2/src/lib.rs b/nearby/connections/ukey2/ukey2/src/lib.rs index d8f2270..dc17b83 100644 --- a/nearby/connections/ukey2/ukey2/src/lib.rs +++ b/nearby/connections/ukey2/ukey2/src/lib.rs
@@ -18,15 +18,6 @@ //! establish a secure channel. //! //! For a full description of the protocol, see <https://github.com/google/ukey2>. -#![forbid(unsafe_code)] -#![deny( - missing_docs, - trivial_casts, - trivial_numeric_casts, - unused_extern_crates, - unused_import_braces, - unused_results -)] mod proto_adapter; mod state_machine;
diff --git a/nearby/connections/ukey2/ukey2/src/proto_adapter.rs b/nearby/connections/ukey2/ukey2/src/proto_adapter.rs index de3c4b9..2986276 100644 --- a/nearby/connections/ukey2/ukey2/src/proto_adapter.rs +++ b/nearby/connections/ukey2/ukey2/src/proto_adapter.rs
@@ -17,7 +17,6 @@ use crypto_provider::elliptic_curve::EcdhProvider; use crypto_provider::p256::{P256EcdhProvider, P256PublicKey, P256}; use crypto_provider::CryptoProvider; -use derive_getters::Getters; use ukey2_proto::ukey2_all_proto::{securemessage, ukey}; /// For generated proto types for UKEY2 messages @@ -79,23 +78,44 @@ ClientFinish, } -#[derive(Getters)] pub(crate) struct ClientInit { version: i32, commitments: Vec<CipherCommitment>, next_protocol: String, } +impl ClientInit { + pub fn version(&self) -> i32 { + self.version + } + + pub fn commitments(&self) -> &[CipherCommitment] { + &self.commitments + } + + pub fn next_protocol(&self) -> &str { + &self.next_protocol + } +} + #[allow(dead_code)] -#[derive(Getters)] pub(crate) struct ServerInit { version: i32, random: [u8; 32], handshake_cipher: HandshakeCipher, - #[getter(skip)] pub(crate) public_key: Vec<u8>, } +impl ServerInit { + pub fn version(&self) -> i32 { + self.version + } + + pub fn handshake_cipher(&self) -> HandshakeCipher { + self.handshake_cipher + } +} + pub(crate) struct ClientFinished { pub(crate) public_key: Vec<u8>, } @@ -119,12 +139,22 @@ } } -#[derive(Clone, Getters)] +#[derive(Clone)] pub(crate) struct CipherCommitment { cipher: HandshakeCipher, commitment: Vec<u8>, } +impl CipherCommitment { + pub fn cipher(&self) -> HandshakeCipher { + self.cipher + } + + pub fn commitment(&self) -> &[u8] { + &self.commitment + } +} + pub(crate) enum GenericPublicKey<C: CryptoProvider> { Ec256(<C::P256 as EcdhProvider<P256>>::PublicKey), // Other public key types are not supported @@ -256,6 +286,7 @@ /// representation. If the input byte array is not positive or cannot be fit into 32 byte unsigned /// int range, then `None` is returned. fn positive_twos_complement_to_32_byte_unsigned(twos_complement: &[u8]) -> Option<[u8; 32]> { + #[allow(clippy::indexing_slicing)] if !twos_complement.is_empty() && (twos_complement[0] & 0x80) == 0 { let mut twos_complement_iter = twos_complement.iter().rev(); let mut result = [0_u8; 32];
diff --git a/nearby/connections/ukey2/ukey2/src/state_machine.rs b/nearby/connections/ukey2/ukey2/src/state_machine.rs index c7cefc3..d2021e6 100644 --- a/nearby/connections/ukey2/ukey2/src/state_machine.rs +++ b/nearby/connections/ukey2/ukey2/src/state_machine.rs
@@ -43,7 +43,8 @@ error_message: self.msg, ..Default::default() }; - alert_message.to_wrapped_msg().write_to_bytes().unwrap() + #[allow(clippy::expect_used)] + alert_message.to_wrapped_msg().write_to_bytes().expect("Writing to proto should succeed") } }
diff --git a/nearby/connections/ukey2/ukey2/src/tests.rs b/nearby/connections/ukey2/ukey2/src/tests.rs index 65274a6..92c0358 100644 --- a/nearby/connections/ukey2/ukey2/src/tests.rs +++ b/nearby/connections/ukey2/ukey2/src/tests.rs
@@ -12,6 +12,8 @@ // See the License for the specific language governing permissions and // limitations under the License. +#![allow(clippy::unwrap_used)] + use crate::{ proto_adapter::{IntoAdapter as _, MessageType, ToWrappedMessage as _}, ukey2_handshake::HandshakeCipher,
diff --git a/nearby/connections/ukey2/ukey2/src/ukey2_handshake.rs b/nearby/connections/ukey2/ukey2/src/ukey2_handshake.rs index 70db4a3..0b2be0e 100644 --- a/nearby/connections/ukey2/ukey2/src/ukey2_handshake.rs +++ b/nearby/connections/ukey2/ukey2/src/ukey2_handshake.rs
@@ -12,6 +12,9 @@ // 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. +#![allow(clippy::expect_used)] +// TODO: remove this and convert all unwraps to expects +#![allow(clippy::unwrap_used)] pub(crate) use crate::proto_adapter::{ CipherCommitment, ClientFinished, ClientInit, GenericPublicKey, HandshakeCipher, @@ -89,7 +92,7 @@ HandshakeCipher::P256Sha512 => { let p256_key = <C::P256 as P256EcdhProvider>::PublicKey::from_bytes(key.as_slice()) - .unwrap(); + .expect(""); let (x, y) = p256_key.to_affine_coordinates().unwrap(); let bigboi_x = num_bigint::BigInt::from_biguint( num_bigint::Sign::Plus, @@ -167,7 +170,7 @@ client_init: ClientInit, client_init_msg_bytes: Vec<u8>, ) -> Result<Ukey2ServerStage2<C>, ClientInitError> { - if client_init.version() != &1 { + if client_init.version() != 1 { return Err(ClientInitError::BadVersion); } @@ -188,7 +191,7 @@ // proto enum uses the priority as the numeric value .max_by_key(|c| c.cipher().as_proto() as i32) .ok_or(ClientInitError::BadHandshakeCipher)?; - match *commitment.cipher() { + match commitment.cipher() { // pick in priority order HandshakeCipher::Curve25519Sha512 => { let secret = ServerKeyPair::Curve25519( @@ -474,7 +477,7 @@ server_init: ServerInit, server_init_bytes: Vec<u8>, ) -> Result<Ukey2Client, ServerInitError> { - if server_init.version() != &1 { + if server_init.version() != 1 { return Err(ServerInitError::BadVersion); } @@ -483,7 +486,7 @@ .commitment_ciphers .iter() .fold(None, |accum, c| { - if server_init.handshake_cipher() == c { + if server_init.handshake_cipher() == *c { match accum { None => Some(c), Some(_) => accum,
diff --git a/nearby/connections/ukey2/ukey2/tests/tests.rs b/nearby/connections/ukey2/ukey2/tests/tests.rs index 2f51a8c..85b972c 100644 --- a/nearby/connections/ukey2/ukey2/tests/tests.rs +++ b/nearby/connections/ukey2/ukey2/tests/tests.rs
@@ -12,6 +12,8 @@ // See the License for the specific language governing permissions and // limitations under the License. +#![allow(clippy::unwrap_used)] + use crypto_provider_default::CryptoProviderImpl; use rand::{rngs::StdRng, SeedableRng}; use std::collections::hash_set;
diff --git a/nearby/connections/ukey2/ukey2_connections/Cargo.toml b/nearby/connections/ukey2/ukey2_connections/Cargo.toml index 8337e96..e7884c4 100644 --- a/nearby/connections/ukey2/ukey2_connections/Cargo.toml +++ b/nearby/connections/ukey2/ukey2_connections/Cargo.toml
@@ -4,6 +4,9 @@ edition.workspace = true publish.workspace = true +[lints] +workspace = true + [features] default = [] test_boringssl = ["crypto_provider_default/boringssl"]
diff --git a/nearby/connections/ukey2/ukey2_connections/benches/ukey2_benches.rs b/nearby/connections/ukey2/ukey2_connections/benches/ukey2_benches.rs index e48400a..f816165 100644 --- a/nearby/connections/ukey2/ukey2_connections/benches/ukey2_benches.rs +++ b/nearby/connections/ukey2/ukey2_connections/benches/ukey2_benches.rs
@@ -12,6 +12,8 @@ // See the License for the specific language governing permissions and // limitations under the License. +#![allow(missing_docs, clippy::expect_used, clippy::unwrap_used)] + use criterion::{black_box, criterion_group, criterion_main, Criterion, Throughput}; use rand::{Rng, SeedableRng}; @@ -65,10 +67,10 @@ let (mut initiator_ctx, mut server_ctx) = run_handshake_with_rng::<CryptoProviderImpl, _>(rand::rngs::StdRng::from_entropy()); for len in [10 * kib, 1024 * kib] { - group.throughput(Throughput::Bytes(len as u64)); + let _ = group.throughput(Throughput::Bytes(len as u64)); plaintext.resize(len, 0); rand::thread_rng().fill(&mut plaintext[..]); - group.bench_function(format!("UKEY2 encrypt/decrypt {}KiB", len / kib), |b| { + let _ = group.bench_function(format!("UKEY2 encrypt/decrypt {}KiB", len / kib), |b| { b.iter(|| { let msg = initiator_ctx .encode_message_to_peer::<CryptoProviderImpl, &[u8]>(&plaintext, None);
diff --git a/nearby/connections/ukey2/ukey2_connections/src/lib.rs b/nearby/connections/ukey2/ukey2_connections/src/lib.rs index 3a441bb..b3ed937 100644 --- a/nearby/connections/ukey2/ukey2_connections/src/lib.rs +++ b/nearby/connections/ukey2/ukey2_connections/src/lib.rs
@@ -24,7 +24,9 @@ //! from the handshake context once the handshake is complete, and controls the encryption and //! decryption of the payload messages. -#![deny(missing_docs)] +#![allow(clippy::expect_used)] +//TODO: remove this and fix instances of unwrap +#![allow(clippy::unwrap_used, clippy::panic)] mod crypto_utils; mod d2d_connection_context_v1;
diff --git a/nearby/connections/ukey2/ukey2_connections/src/tests.rs b/nearby/connections/ukey2/ukey2_connections/src/tests.rs index 32a6b28..d6acecc 100644 --- a/nearby/connections/ukey2/ukey2_connections/src/tests.rs +++ b/nearby/connections/ukey2/ukey2_connections/src/tests.rs
@@ -12,6 +12,8 @@ // See the License for the specific language governing permissions and // limitations under the License. +#![allow(clippy::indexing_slicing)] + use crypto_provider::CryptoProvider; use crypto_provider_default::CryptoProviderImpl; use rand::SeedableRng;
diff --git a/nearby/connections/ukey2/ukey2_jni/Cargo.toml b/nearby/connections/ukey2/ukey2_jni/Cargo.toml index 4852444..08566dd 100644 --- a/nearby/connections/ukey2/ukey2_jni/Cargo.toml +++ b/nearby/connections/ukey2/ukey2_jni/Cargo.toml
@@ -4,6 +4,9 @@ edition.workspace = true publish.workspace = true +[lints] +workspace = true + # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html [dependencies]
diff --git a/nearby/connections/ukey2/ukey2_jni/src/lib.rs b/nearby/connections/ukey2/ukey2_jni/src/lib.rs index 82d317e..b7764fa 100644 --- a/nearby/connections/ukey2/ukey2_jni/src/lib.rs +++ b/nearby/connections/ukey2/ukey2_jni/src/lib.rs
@@ -12,6 +12,12 @@ // See the License for the specific language governing permissions and // limitations under the License. +//! JNI bindings for the ukey2 rust implementation + +#![allow(unsafe_code, clippy::expect_used)] +//TODO: remove this and fix instances of unwrap/panic +#![allow(clippy::unwrap_used, clippy::panic)] + use std::collections::HashMap; use jni::objects::{JByteArray, JClass}; @@ -52,14 +58,28 @@ } pub(crate) fn insert_handshake_handle(item: D2DBox) -> u64 { - let handle = generate_handle(); - HANDLE_MAPPING.lock().insert(handle, item); + let mut handle = generate_handle(); + let map = HANDLE_MAPPING.lock(); + while map.contains_key(&handle) { + handle = generate_handle(); + } + + let result = HANDLE_MAPPING.lock().insert(handle, item); + // result should always be None since we checked that handle map does not contain the key already + assert!(result.is_none()); handle } pub(crate) fn insert_conn_handle(item: ConnectionBox) -> u64 { - let handle = generate_handle(); - CONNECTION_HANDLE_MAPPING.lock().insert(handle, item); + let mut handle = generate_handle(); + let map = CONNECTION_HANDLE_MAPPING.lock(); + while map.contains_key(&handle) { + handle = generate_handle(); + } + + let result = CONNECTION_HANDLE_MAPPING.lock().insert(handle, item); + // result should always be None since we checked that handle map does not contain the key already + assert!(result.is_none()); handle } @@ -71,7 +91,8 @@ HandshakeError(HandshakeError), } -// D2DHandshakeContext +/// Tells the caller whether the handshake has completed or not. If the handshake is complete, +/// the caller may call `to_connection_context`to obtain a connection context. #[no_mangle] pub extern "system" fn Java_com_google_security_cryptauth_lib_securegcm_D2DHandshakeContext_is_1handshake_1complete( mut env: JNIEnv, @@ -88,6 +109,7 @@ is_complete as jboolean } +/// Creates a new handshake context #[no_mangle] pub extern "system" fn Java_com_google_security_cryptauth_lib_securegcm_D2DHandshakeContext_create_1context( _: JNIEnv, @@ -107,6 +129,7 @@ } } +/// Constructs the next message that should be sent in the handshake. #[no_mangle] pub extern "system" fn Java_com_google_security_cryptauth_lib_securegcm_D2DHandshakeContext_get_1next_1handshake_1message( mut env: JNIEnv, @@ -130,9 +153,10 @@ .into_raw() } -#[no_mangle] +/// Parses a handshake message and advances the internal state of the context. +// Safety: We know the message pointer is safe as it is coming directly from the JVM. #[allow(clippy::not_unsafe_ptr_arg_deref)] -/// Safety: We know the message pointer is safe as it is coming directly from the JVM. +#[no_mangle] pub extern "system" fn Java_com_google_security_cryptauth_lib_securegcm_D2DHandshakeContext_parse_1handshake_1message( mut env: JNIEnv, _: JClass, @@ -163,6 +187,8 @@ } } +/// Returns the `CompletedHandshake` using the results from this handshake context. May only +/// be called if `is_handshake_complete` returns true. #[no_mangle] pub extern "system" fn Java_com_google_security_cryptauth_lib_securegcm_D2DHandshakeContext_get_1verification_1string( mut env: JNIEnv, @@ -201,6 +227,8 @@ .into_raw() } +/// Creates a [`D2DConnectionContextV1`] using the results of the handshake. May only be called +/// if `is_handshake_complete` returns true. #[no_mangle] pub extern "system" fn Java_com_google_security_cryptauth_lib_securegcm_D2DHandshakeContext_to_1connection_1context( mut env: JNIEnv, @@ -224,16 +252,17 @@ .expect("failed to find error class"); return -1; } else { - HANDLE_MAPPING.lock().remove(&(context_handle as u64)); + let _ = HANDLE_MAPPING.lock().remove(&(context_handle as u64)); } insert_conn_handle(Box::new(conn_context.unwrap())) as jlong } -// D2DConnectionContextV1 -#[no_mangle] +/// Once initiator and responder have exchanged public keys, use this method to encrypt and +/// sign a payload. Both initiator and responder devices can use this message. +// Safety: We know the payload and associated_data pointers are safe as they are coming directly +// from the JVM. #[allow(clippy::not_unsafe_ptr_arg_deref)] -/// Safety: We know the payload and associated_data pointers are safe as they are coming directly -/// from the JVM. +#[no_mangle] pub extern "system" fn Java_com_google_security_cryptauth_lib_securegcm_D2DConnectionContextV1_encode_1message_1to_1peer( mut env: JNIEnv, _: JClass, @@ -271,10 +300,13 @@ .into_raw() } -#[no_mangle] +/// Once `InitiatorHello` and `ResponderHello` (and payload) are exchanged, use this method to +/// decrypt and verify a message received from the other device. Both initiator and responder +/// devices can use this message. +// Safety: We know the message and associated_data pointers are safe as they are coming directly +// from the JVM. #[allow(clippy::not_unsafe_ptr_arg_deref)] -/// Safety: We know the message and associated_data pointers are safe as they are coming directly -/// from the JVM. +#[no_mangle] pub extern "system" fn Java_com_google_security_cryptauth_lib_securegcm_D2DConnectionContextV1_decode_1message_1from_1peer( mut env: JNIEnv, _: JClass, @@ -322,6 +354,7 @@ .into_raw() } +/// Returns the last sequence number used to encode a message. #[no_mangle] pub extern "system" fn Java_com_google_security_cryptauth_lib_securegcm_D2DConnectionContextV1_get_1sequence_1number_1for_1encoding( mut env: JNIEnv, @@ -329,14 +362,15 @@ context_handle: jlong, ) -> jint { if let Some(ctx) = CONNECTION_HANDLE_MAPPING.lock().get(&(context_handle as u64)) { - ctx.get_sequence_number_for_encoding() as jint + ctx.get_sequence_number_for_encoding() } else { env.throw_new("com/google/security/cryptauth/lib/securegcm/BadHandleException", "") .expect("failed to find error class"); - -1 as jint + -1 } } +/// Returns the last sequence number used to decode a message. #[no_mangle] pub extern "system" fn Java_com_google_security_cryptauth_lib_securegcm_D2DConnectionContextV1_get_1sequence_1number_1for_1decoding( mut env: JNIEnv, @@ -344,14 +378,16 @@ context_handle: jlong, ) -> jint { if let Some(ctx) = CONNECTION_HANDLE_MAPPING.lock().get(&(context_handle as u64)) { - ctx.get_sequence_number_for_decoding() as jint + ctx.get_sequence_number_for_decoding() } else { env.throw_new("com/google/security/cryptauth/lib/securegcm/BadHandleException", "") .expect("failed to find error class"); - -1 as jint + -1 } } +/// Creates a saved session that can later be used for resumption. The session data may be +/// persisted, but it must be stored in a secure location. #[no_mangle] pub extern "system" fn Java_com_google_security_cryptauth_lib_securegcm_D2DConnectionContextV1_save_1session( mut env: JNIEnv, @@ -369,9 +405,10 @@ .into_raw() } +/// Creates a connection context from a saved session. +// Safety: We know the session_info pointer is safe because it is coming directly from the JVM. #[no_mangle] #[allow(clippy::not_unsafe_ptr_arg_deref)] -/// Safety: We know the session_info pointer is safe because it is coming directly from the JVM. pub extern "system" fn Java_com_google_security_cryptauth_lib_securegcm_D2DConnectionContextV1_from_1saved_1session( mut env: JNIEnv, _: JClass, @@ -399,6 +436,9 @@ insert_conn_handle(conn_context_final) as jlong } +/// Returns a cryptographic digest (SHA256) of the session keys prepended by the SHA256 hash +/// of the ASCII string "D2D". Since the server and client share the same session keys, the +/// resulting session unique is also the same. #[no_mangle] pub extern "system" fn Java_com_google_security_cryptauth_lib_securegcm_D2DConnectionContextV1_get_1session_1unique( mut env: JNIEnv,
diff --git a/nearby/connections/ukey2/ukey2_shell/Cargo.toml b/nearby/connections/ukey2/ukey2_shell/Cargo.toml index d1f48e4..9a2856d 100644 --- a/nearby/connections/ukey2/ukey2_shell/Cargo.toml +++ b/nearby/connections/ukey2/ukey2_shell/Cargo.toml
@@ -4,11 +4,11 @@ edition.workspace = true publish.workspace = true -# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html +[lints] +workspace = true [dependencies] crypto_provider_rustcrypto = { workspace = true, features = [ "alloc" ] } ukey2_rs = { version = "0.1.0", path = "../ukey2" } ukey2_connections = { version = "0.1.0", path = "../ukey2_connections" } - clap = { version = "4.0.17", default-features = false, features = ["std", "derive"] }
diff --git a/nearby/connections/ukey2/ukey2_shell/src/main.rs b/nearby/connections/ukey2/ukey2_shell/src/main.rs index e4bcc0d..3eca7b0 100644 --- a/nearby/connections/ukey2/ukey2_shell/src/main.rs +++ b/nearby/connections/ukey2/ukey2_shell/src/main.rs
@@ -12,6 +12,12 @@ // See the License for the specific language governing permissions and // limitations under the License. +//! Provides a sample ukey2 shell app which can be run from the command line + +#![allow(clippy::expect_used)] +//TODO: remove this and fix instances of unwrap +#![allow(clippy::unwrap_used, clippy::panic, clippy::indexing_slicing)] + use std::io::{Read, Write}; use std::process::exit; @@ -191,9 +197,9 @@ let args = Ukey2Cli::parse(); let shell = Ukey2Shell::new(args.verification_string_length); if args.mode == MODE_INITIATOR { - shell.run_as_initiator(); + let _ = shell.run_as_initiator(); } else if args.mode == MODE_RESPONDER { - shell.run_as_responder(); + let _ = shell.run_as_responder(); } else { exit(1); }
diff --git a/nearby/crypto/crypto_provider/Cargo.toml b/nearby/crypto/crypto_provider/Cargo.toml index e7dd8ba..1a588bb 100644 --- a/nearby/crypto/crypto_provider/Cargo.toml +++ b/nearby/crypto/crypto_provider/Cargo.toml
@@ -4,6 +4,9 @@ edition.workspace = true publish.workspace = true +[lints] +workspace = true + [dependencies] tinyvec.workspace = true
diff --git a/nearby/crypto/crypto_provider/benches/constant_time_eq_bench.rs b/nearby/crypto/crypto_provider/benches/constant_time_eq_bench.rs index f401327..70c65cf 100644 --- a/nearby/crypto/crypto_provider/benches/constant_time_eq_bench.rs +++ b/nearby/crypto/crypto_provider/benches/constant_time_eq_bench.rs
@@ -12,6 +12,8 @@ // See the License for the specific language governing permissions and // limitations under the License. +#![allow(missing_docs, clippy::indexing_slicing)] + use criterion::{ criterion_group, criterion_main, measurement::WallTime, BatchSize, BenchmarkGroup, Criterion, }; @@ -33,7 +35,7 @@ fn add_benches<C: CryptoProvider>(group: &mut BenchmarkGroup<WallTime>, rng: &mut ThreadRng) { const TEST_LEN: usize = 1000; for i in (0..=TEST_LEN).step_by(100) { - group.bench_function( + let _ = group.bench_function( &format!( "constant_time_eq impl {} differ by {:04} bytes", std::any::type_name::<C>(),
diff --git a/nearby/crypto/crypto_provider/benches/hkdf_bench.rs b/nearby/crypto/crypto_provider/benches/hkdf_bench.rs index 4c11d0d..e73f8cf 100644 --- a/nearby/crypto/crypto_provider/benches/hkdf_bench.rs +++ b/nearby/crypto/crypto_provider/benches/hkdf_bench.rs
@@ -12,6 +12,8 @@ // See the License for the specific language governing permissions and // limitations under the License. +#![allow(missing_docs, clippy::expect_used)] + use criterion::{criterion_group, criterion_main, Criterion}; use hex_literal::hex; @@ -26,15 +28,16 @@ let salt = hex!("000102030405060708090a0b0c"); let info = hex!("f0f1f2f3f4f5f6f7f8f9"); - c.bench_function(&format!("bench hkdf with salt {}", std::any::type_name::<C>()), |b| { - b.iter(|| { - let hk = C::HkdfSha256::new(Some(&salt[..]), &ikm); - let mut okm = [0u8; 42]; - hk.expand(&info, &mut okm).expect("42 is a valid length for Sha256 to output"); + let _ = + c.bench_function(&format!("bench hkdf with salt {}", std::any::type_name::<C>()), |b| { + b.iter(|| { + let hk = C::HkdfSha256::new(Some(&salt[..]), &ikm); + let mut okm = [0u8; 42]; + hk.expand(&info, &mut okm).expect("42 is a valid length for Sha256 to output"); + }); }); - }); - c.bench_function(&format!("bench hkdf no salt {}", std::any::type_name::<C>()), |b| { + let _ = c.bench_function(&format!("bench hkdf no salt {}", std::any::type_name::<C>()), |b| { b.iter(|| { let hk = C::HkdfSha256::new(None, &ikm); let mut okm = [0u8; 42];
diff --git a/nearby/crypto/crypto_provider/benches/hmac_bench.rs b/nearby/crypto/crypto_provider/benches/hmac_bench.rs index c7ccf1a..9bcfde0 100644 --- a/nearby/crypto/crypto_provider/benches/hmac_bench.rs +++ b/nearby/crypto/crypto_provider/benches/hmac_bench.rs
@@ -12,6 +12,8 @@ // See the License for the specific language governing permissions and // limitations under the License. +#![allow(missing_docs)] + use criterion::{criterion_group, criterion_main, Criterion}; use crypto_provider::hmac::Hmac; @@ -26,7 +28,7 @@ let key: [u8; 32] = rand_ext::random_bytes::<32, C>(&mut rng); let update_data: [u8; 16] = rand_ext::random_bytes::<16, C>(&mut rng); - c.bench_function("bench for hmac sha256 single update", |b| { + let _ = c.bench_function("bench for hmac sha256 single update", |b| { b.iter(|| { let mut hmac = C::HmacSha256::new_from_key(key); hmac.update(&update_data); @@ -40,7 +42,7 @@ let key: [u8; 64] = rand_ext::random_bytes::<64, C>(&mut rng); let update_data: [u8; 16] = random_bytes::<16, C>(&mut rng); - c.bench_function("bench for hmac sha512 single update", |b| { + let _ = c.bench_function("bench for hmac sha512 single update", |b| { b.iter(|| { let mut hmac = C::HmacSha512::new_from_key(key); hmac.update(&update_data);
diff --git a/nearby/crypto/crypto_provider/src/aes/cbc.rs b/nearby/crypto/crypto_provider/src/aes/cbc.rs index 59ce52d..82a857b 100644 --- a/nearby/crypto/crypto_provider/src/aes/cbc.rs +++ b/nearby/crypto/crypto_provider/src/aes/cbc.rs
@@ -29,6 +29,7 @@ pub trait AesCbcPkcs7Padded { /// Calculate the padded output length (e.g. output of `encrypt`) from the unpadded length (e.g. /// input message of `encrypt`). + #[allow(clippy::expect_used)] fn padded_output_len(unpadded_len: usize) -> usize { (unpadded_len - (unpadded_len % 16)) .checked_add(16) @@ -109,7 +110,7 @@ #[test] #[should_panic] fn test_padded_output_len_overflow() { - AesCbcPkcs7PaddedStub::padded_output_len(usize::MAX); + let _ = AesCbcPkcs7PaddedStub::padded_output_len(usize::MAX); } struct AesCbcPkcs7PaddedStub;
diff --git a/nearby/crypto/crypto_provider/src/lib.rs b/nearby/crypto/crypto_provider/src/lib.rs index 0031ebb..214175d 100644 --- a/nearby/crypto/crypto_provider/src/lib.rs +++ b/nearby/crypto/crypto_provider/src/lib.rs
@@ -11,12 +11,11 @@ // 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.' -#![no_std] -#![forbid(unsafe_code)] -#![deny(missing_docs)] //! Crypto abstraction trait only crate, which provides traits for cryptographic primitives +#![no_std] + use core::fmt::Debug; use crate::aes::{Aes128Key, Aes256Key};
diff --git a/nearby/crypto/crypto_provider_default/Cargo.toml b/nearby/crypto/crypto_provider_default/Cargo.toml index 2a155bf..4c906e4 100644 --- a/nearby/crypto/crypto_provider_default/Cargo.toml +++ b/nearby/crypto/crypto_provider_default/Cargo.toml
@@ -4,6 +4,9 @@ edition.workspace = true publish.workspace = true +[lints] +workspace = true + [dependencies] crypto_provider.workspace = true crypto_provider_rustcrypto = {workspace = true, optional = true}
diff --git a/nearby/crypto/crypto_provider_default/src/lib.rs b/nearby/crypto/crypto_provider_default/src/lib.rs index b880ba8..52da9d4 100644 --- a/nearby/crypto/crypto_provider_default/src/lib.rs +++ b/nearby/crypto/crypto_provider_default/src/lib.rs
@@ -16,8 +16,6 @@ //! feature flag. #![no_std] -#![forbid(unsafe_code)] -#![deny(missing_docs)] cfg_if::cfg_if! { if #[cfg(feature = "rustcrypto")] {
diff --git a/nearby/crypto/crypto_provider_openssl/Cargo.toml b/nearby/crypto/crypto_provider_openssl/Cargo.toml index 84d4380..19ef3e4 100644 --- a/nearby/crypto/crypto_provider_openssl/Cargo.toml +++ b/nearby/crypto/crypto_provider_openssl/Cargo.toml
@@ -4,6 +4,9 @@ edition.workspace = true publish.workspace = true +[lints] +workspace = true + [dependencies] crypto_provider = { workspace = true, features = ["alloc", "std"] } crypto_provider_stubs.workspace = true
diff --git a/nearby/crypto/crypto_provider_openssl/src/aes.rs b/nearby/crypto/crypto_provider_openssl/src/aes.rs index c2ac5b1..857be74 100644 --- a/nearby/crypto/crypto_provider_openssl/src/aes.rs +++ b/nearby/crypto/crypto_provider_openssl/src/aes.rs
@@ -71,7 +71,7 @@ let mut crypter = Crypter::new(Cipher::aes_128_ecb(), Mode::Encrypt, self.0.as_slice(), None).unwrap(); crypter.pad(false); - crypter.update(block, &mut output).unwrap(); + let _ = crypter.update(block, &mut output).unwrap(); block.copy_from_slice(&output[..crypto_provider::aes::BLOCK_SIZE]); } } @@ -83,7 +83,7 @@ let mut crypter = Crypter::new(Cipher::aes_128_ecb(), Mode::Decrypt, self.0.as_slice(), None).unwrap(); crypter.pad(false); - crypter.update(block, &mut output).unwrap(); + let _ = crypter.update(block, &mut output).unwrap(); block.copy_from_slice(&output[..crypto_provider::aes::BLOCK_SIZE]); } } @@ -106,7 +106,7 @@ let mut crypter = Crypter::new(Cipher::aes_256_ecb(), Mode::Encrypt, self.0.as_slice(), None).unwrap(); crypter.pad(false); - crypter.update(block, &mut output).unwrap(); + let _ = crypter.update(block, &mut output).unwrap(); block.copy_from_slice(&output[..crypto_provider::aes::BLOCK_SIZE]); } } @@ -118,7 +118,7 @@ let mut crypter = Crypter::new(Cipher::aes_256_ecb(), Mode::Decrypt, self.0.as_slice(), None).unwrap(); crypter.pad(false); - crypter.update(block, &mut output).unwrap(); + let _ = crypter.update(block, &mut output).unwrap(); block.copy_from_slice(&output[..crypto_provider::aes::BLOCK_SIZE]); } }
diff --git a/nearby/crypto/crypto_provider_openssl/src/hkdf_openssl.rs b/nearby/crypto/crypto_provider_openssl/src/hkdf_openssl.rs index d429a4d..b726f5f 100644 --- a/nearby/crypto/crypto_provider_openssl/src/hkdf_openssl.rs +++ b/nearby/crypto/crypto_provider_openssl/src/hkdf_openssl.rs
@@ -42,7 +42,9 @@ let md = H::get_md(); ctx.derive_init().expect("hkdf derive init should not fail"); ctx.set_hkdf_md(md).expect("hkdf set md should not fail"); - self.salt.as_ref().map(|salt| ctx.set_hkdf_salt(salt.as_slice())); + let _ = self.salt.as_ref().map(|salt| { + ctx.set_hkdf_salt(salt.as_slice()).expect("setting the salt is infallible") + }); ctx.set_hkdf_key(self.ikm.as_slice()).expect("should be able to set key"); ctx.add_hkdf_info(&info_components.concat()).expect("should be able to add info"); ctx.derive(Some(okm)).map_err(|_| InvalidLength).map(|_| ())
diff --git a/nearby/crypto/crypto_provider_openssl/src/hmac_boringssl.rs b/nearby/crypto/crypto_provider_openssl/src/hmac_boringssl.rs index 9b86726..5272995 100644 --- a/nearby/crypto/crypto_provider_openssl/src/hmac_boringssl.rs +++ b/nearby/crypto/crypto_provider_openssl/src/hmac_boringssl.rs
@@ -50,7 +50,7 @@ fn finalize(mut self) -> [u8; N] { let mut buf = [0_u8; N]; - self.ctx.finalize(&mut buf).expect("wrong length"); + let _ = self.ctx.finalize(&mut buf).expect("wrong length"); buf }
diff --git a/nearby/crypto/crypto_provider_openssl/src/lib.rs b/nearby/crypto/crypto_provider_openssl/src/lib.rs index 8d1db1d..04b4e4c 100644 --- a/nearby/crypto/crypto_provider_openssl/src/lib.rs +++ b/nearby/crypto/crypto_provider_openssl/src/lib.rs
@@ -12,10 +12,13 @@ // 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. -#![deny(missing_docs, clippy::indexing_slicing, clippy::panic)] //! Crate which provides impls for CryptoProvider backed by openssl +// This crate treats allocation errors as handleable, which leads to unwraps everywhere, so they +// have to be allowed here. This will be fixed when we can migrate over to the new boringssl bindings +#![allow(clippy::expect_used, clippy::unwrap_used)] + use cfg_if::cfg_if; pub use openssl; use openssl::hash::MessageDigest;
diff --git a/nearby/crypto/crypto_provider_openssl/src/sha2.rs b/nearby/crypto/crypto_provider_openssl/src/sha2.rs index 92e663e..68fee79 100644 --- a/nearby/crypto/crypto_provider_openssl/src/sha2.rs +++ b/nearby/crypto/crypto_provider_openssl/src/sha2.rs
@@ -38,7 +38,8 @@ mdctx.digest_init(Self::get_md()).unwrap(); mdctx.digest_update(input).unwrap(); let mut buf = [0_u8; 32]; - mdctx.digest_final(&mut buf).unwrap(); + let size = mdctx.digest_final(&mut buf).unwrap(); + debug_assert_eq!(size, 32); buf } } @@ -63,7 +64,8 @@ mdctx.digest_init(Self::get_md()).unwrap(); mdctx.digest_update(input).unwrap(); let mut buf = [0_u8; 64]; - mdctx.digest_final(&mut buf).unwrap(); + let size = mdctx.digest_final(&mut buf).unwrap(); + debug_assert_eq!(size, 64); buf } }
diff --git a/nearby/crypto/crypto_provider_rustcrypto/Cargo.toml b/nearby/crypto/crypto_provider_rustcrypto/Cargo.toml index a0638ff..14e8ad9 100644 --- a/nearby/crypto/crypto_provider_rustcrypto/Cargo.toml +++ b/nearby/crypto/crypto_provider_rustcrypto/Cargo.toml
@@ -4,6 +4,9 @@ edition.workspace = true publish.workspace = true +[lints] +workspace = true + [dependencies] aead = "0.5.1" aes-gcm-siv = { version = "0.11.1", features = [
diff --git a/nearby/crypto/crypto_provider_rustcrypto/src/lib.rs b/nearby/crypto/crypto_provider_rustcrypto/src/lib.rs index 628f679..2985dec 100644 --- a/nearby/crypto/crypto_provider_rustcrypto/src/lib.rs +++ b/nearby/crypto/crypto_provider_rustcrypto/src/lib.rs
@@ -11,15 +11,8 @@ // 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. + #![no_std] -#![forbid(unsafe_code)] -#![deny( - missing_docs, - clippy::indexing_slicing, - clippy::unwrap_used, - clippy::panic, - clippy::expect_used -)] //! Crate which provides impls for CryptoProvider backed by RustCrypto crates
diff --git a/nearby/crypto/rand_core_05_adapter/Cargo.toml b/nearby/crypto/rand_core_05_adapter/Cargo.toml index 0a8fc47..1a02526 100644 --- a/nearby/crypto/rand_core_05_adapter/Cargo.toml +++ b/nearby/crypto/rand_core_05_adapter/Cargo.toml
@@ -4,9 +4,10 @@ edition.workspace = true publish.workspace = true +[lints] +workspace = true + [dependencies] rand.workspace = true # an older rand_core used by ed25519-dalek so we can adapt newer rand to it rand_core05 = { package = "rand_core", version = "0.5.1" } - -[dev-dependencies]
diff --git a/nearby/crypto/rand_core_05_adapter/src/lib.rs b/nearby/crypto/rand_core_05_adapter/src/lib.rs index 6484983..9de0020 100644 --- a/nearby/crypto/rand_core_05_adapter/src/lib.rs +++ b/nearby/crypto/rand_core_05_adapter/src/lib.rs
@@ -13,15 +13,8 @@ // limitations under the License. //! Adapter for using rand_core 0.5 RNGs with code that expects rand_core 0.5 RNGs. + #![no_std] -#![forbid(unsafe_code)] -#![deny( - missing_docs, - clippy::indexing_slicing, - clippy::unwrap_used, - clippy::panic, - clippy::expect_used -)] /// A trivial adapter to expose rand 1.0/rand_core 0.6 rngs to ed25519-dalek's rand_core 0.5 types, /// which we import under a separate name so there's no clash.
diff --git a/nearby/presence/array_ref/Cargo.toml b/nearby/presence/array_ref/Cargo.toml index f912c41..b74168b 100644 --- a/nearby/presence/array_ref/Cargo.toml +++ b/nearby/presence/array_ref/Cargo.toml
@@ -4,8 +4,10 @@ edition.workspace = true publish.workspace = true +[lints] +workspace = true + [features] default = [] std = [] -[dependencies]
diff --git a/nearby/presence/array_ref/src/lib.rs b/nearby/presence/array_ref/src/lib.rs index 3ff84fc..769d3ef 100644 --- a/nearby/presence/array_ref/src/lib.rs +++ b/nearby/presence/array_ref/src/lib.rs
@@ -12,8 +12,6 @@ // 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. -#![forbid(unsafe_code)] -#![deny(missing_docs)] //! Crate exposing macros to take array references of slices
diff --git a/nearby/presence/array_view/Cargo.toml b/nearby/presence/array_view/Cargo.toml index 48693c4..8ac53d2 100644 --- a/nearby/presence/array_view/Cargo.toml +++ b/nearby/presence/array_view/Cargo.toml
@@ -4,11 +4,9 @@ edition.workspace = true publish.workspace = true +[lints] +workspace = true + [features] default = [] std = [] - -[dependencies] - -[dev-dependencies] -
diff --git a/nearby/presence/array_view/src/lib.rs b/nearby/presence/array_view/src/lib.rs index c57e959..e2ea8d4 100644 --- a/nearby/presence/array_view/src/lib.rs +++ b/nearby/presence/array_view/src/lib.rs
@@ -14,14 +14,6 @@ //! A no_std friendly array wrapper to expose a variable length prefix of the array. #![no_std] -#![forbid(unsafe_code)] -#![deny( - missing_docs, - clippy::indexing_slicing, - clippy::unwrap_used, - clippy::panic, - clippy::expect_used -)] #[cfg(feature = "std")] extern crate std;
diff --git a/nearby/presence/ldt/Cargo.toml b/nearby/presence/ldt/Cargo.toml index 2ee7b4c..337dd0d 100644 --- a/nearby/presence/ldt/Cargo.toml +++ b/nearby/presence/ldt/Cargo.toml
@@ -4,6 +4,9 @@ edition.workspace = true publish.workspace = true +[lints] +workspace = true + [features] default = [] std = [] @@ -13,7 +16,7 @@ ldt_tbc.workspace = true [dev-dependencies] -crypto_provider_default = {workspace = true, features = ["rustcrypto"]} +crypto_provider_default = { workspace = true, features = ["rustcrypto"] } rand_ext.workspace = true test_helper.workspace = true xts_aes.workspace = true @@ -21,7 +24,7 @@ rand.workspace = true rand_pcg.workspace = true base64.workspace = true -serde_json = {workspace = true, features = ["std"]} +serde_json = { workspace = true, features = ["std"] } anyhow.workspace = true hex.workspace = true
diff --git a/nearby/presence/ldt/benches/ldt_scan.rs b/nearby/presence/ldt/benches/ldt_scan.rs index dac1374..b1711ad 100644 --- a/nearby/presence/ldt/benches/ldt_scan.rs +++ b/nearby/presence/ldt/benches/ldt_scan.rs
@@ -12,6 +12,8 @@ // See the License for the specific language governing permissions and // limitations under the License. +#![allow(missing_docs, unused_results, clippy::indexing_slicing, clippy::unwrap_used)] + use criterion::{black_box, criterion_group, criterion_main, Criterion}; use crypto_provider::{CryptoProvider, CryptoRng}; use crypto_provider_rustcrypto::RustCrypto;
diff --git a/nearby/presence/ldt/examples/gen_ldt_xor_pad_test_vectors.rs b/nearby/presence/ldt/examples/gen_ldt_xor_pad_test_vectors.rs index 517e7ec..7e7c943 100644 --- a/nearby/presence/ldt/examples/gen_ldt_xor_pad_test_vectors.rs +++ b/nearby/presence/ldt/examples/gen_ldt_xor_pad_test_vectors.rs
@@ -12,6 +12,10 @@ // See the License for the specific language governing permissions and // limitations under the License. +//! Generates LDT test vectors which can be used to verify implementations + +#![allow(clippy::unwrap_used)] + use crypto_provider::aes::BLOCK_SIZE; use crypto_provider::{aes, CryptoProvider, CryptoRng}; use crypto_provider_rustcrypto::RustCrypto;
diff --git a/nearby/presence/ldt/examples/ldt_benchmark.rs b/nearby/presence/ldt/examples/ldt_benchmark.rs index df87c28..101e17b 100644 --- a/nearby/presence/ldt/examples/ldt_benchmark.rs +++ b/nearby/presence/ldt/examples/ldt_benchmark.rs
@@ -14,6 +14,8 @@ //! A manual benchmark for more interactive parameter-twiddling. +#![allow(clippy::unwrap_used, clippy::indexing_slicing)] + use clap::Parser as _; use crypto_provider_rustcrypto::RustCrypto; use ldt::{LdtDecryptCipher, LdtEncryptCipher, LdtKey, Mix, Swap, XorPadder};
diff --git a/nearby/presence/ldt/examples/ldt_prp.rs b/nearby/presence/ldt/examples/ldt_prp.rs index f8e055c..7734952 100644 --- a/nearby/presence/ldt/examples/ldt_prp.rs +++ b/nearby/presence/ldt/examples/ldt_prp.rs
@@ -20,6 +20,9 @@ //! //! The output shows how many times a change to the first n bytes wasn't detected, as well as a //! histogram of how many bits were flipped in the entire plaintext. + +#![allow(clippy::unwrap_used, clippy::indexing_slicing)] + use clap::{self, Parser as _}; use crypto_provider::aes::BLOCK_SIZE; use crypto_provider::{CryptoProvider, CryptoRng};
diff --git a/nearby/presence/ldt/src/lib.rs b/nearby/presence/ldt/src/lib.rs index 9065e12..8596ad2 100644 --- a/nearby/presence/ldt/src/lib.rs +++ b/nearby/presence/ldt/src/lib.rs
@@ -15,8 +15,6 @@ //! Provides an implementation of [LDT](https://eprint.iacr.org/2017/841.pdf). #![no_std] -#![forbid(unsafe_code)] -#![deny(clippy::indexing_slicing, clippy::unwrap_used, clippy::panic, clippy::expect_used)] #[cfg(feature = "std")] extern crate std;
diff --git a/nearby/presence/ldt/tests/ldt_roundtrip.rs b/nearby/presence/ldt/tests/ldt_roundtrip.rs index b5f930e..251cefe 100644 --- a/nearby/presence/ldt/tests/ldt_roundtrip.rs +++ b/nearby/presence/ldt/tests/ldt_roundtrip.rs
@@ -12,6 +12,8 @@ // See the License for the specific language governing permissions and // limitations under the License. +#![allow(clippy::unwrap_used)] + use crypto_provider::aes::BLOCK_SIZE; use crypto_provider::{CryptoProvider, CryptoRng}; use crypto_provider_default::CryptoProviderImpl;
diff --git a/nearby/presence/ldt/tests/ldt_test_vectors.rs b/nearby/presence/ldt/tests/ldt_test_vectors.rs index 477243d..03b5d3f 100644 --- a/nearby/presence/ldt/tests/ldt_test_vectors.rs +++ b/nearby/presence/ldt/tests/ldt_test_vectors.rs
@@ -12,6 +12,8 @@ // See the License for the specific language governing permissions and // limitations under the License. +#![allow(clippy::unwrap_used, clippy::indexing_slicing)] + use anyhow::anyhow; use crypto_provider_default::CryptoProviderImpl; use ldt::{DefaultPadder, LdtDecryptCipher, LdtEncryptCipher, LdtKey, Swap, XorPadder}; @@ -26,7 +28,7 @@ ); let mut file = fs::File::open(full_path)?; let mut data = String::new(); - file.read_to_string(&mut data)?; + let _ = file.read_to_string(&mut data)?; let test_cases = match serde_json::de::from_str(&data)? { serde_json::Value::Array(a) => a, _ => return Err(anyhow!("bad json")), @@ -73,7 +75,7 @@ test_helper::get_data_file("presence/ldt/resources/test/ldt-xor-pad-testvectors.json"); let mut file = fs::File::open(full_path)?; let mut data = String::new(); - file.read_to_string(&mut data)?; + let _ = file.read_to_string(&mut data)?; let test_cases = match serde_json::de::from_str(&data)? { serde_json::Value::Array(a) => a, _ => return Err(anyhow!("bad json")),
diff --git a/nearby/presence/ldt/tests/tests.rs b/nearby/presence/ldt/tests/tests.rs index b9e061a..25938fb 100644 --- a/nearby/presence/ldt/tests/tests.rs +++ b/nearby/presence/ldt/tests/tests.rs
@@ -70,7 +70,8 @@ fn normal_pad_too_big_panics() { let padder = DefaultPadder; let input = [0x99; 16]; - <DefaultPadder as Padder<16, XtsAes128<CryptoProviderImpl>>>::pad_tweak(&padder, &input); + let _ = + <DefaultPadder as Padder<16, XtsAes128<CryptoProviderImpl>>>::pad_tweak(&padder, &input); } #[test] @@ -129,7 +130,7 @@ let padder = [0x24; BLOCK_SIZE].into(); // need 1 byte for padding, and 2 more for salt let input = [0x99; 16]; - <XorPadder<BLOCK_SIZE> as Padder<BLOCK_SIZE, XtsAes128<CryptoProviderImpl>>>::pad_tweak( + let _ = <XorPadder<BLOCK_SIZE> as Padder<BLOCK_SIZE, XtsAes128<CryptoProviderImpl>>>::pad_tweak( &padder, &input, ); }
diff --git a/nearby/presence/ldt_np_adv/Cargo.toml b/nearby/presence/ldt_np_adv/Cargo.toml index 522e97b..3f19bdd 100644 --- a/nearby/presence/ldt_np_adv/Cargo.toml +++ b/nearby/presence/ldt_np_adv/Cargo.toml
@@ -4,6 +4,9 @@ edition.workspace = true publish.workspace = true +[lints] +workspace = true + [features] default = [] std = []
diff --git a/nearby/presence/ldt_np_adv/benches/ldt_adv_scan.rs b/nearby/presence/ldt_np_adv/benches/ldt_adv_scan.rs index d7d7fad..7c0b4aa 100644 --- a/nearby/presence/ldt_np_adv/benches/ldt_adv_scan.rs +++ b/nearby/presence/ldt_np_adv/benches/ldt_adv_scan.rs
@@ -12,6 +12,8 @@ // See the License for the specific language governing permissions and // limitations under the License. +#![allow(missing_docs)] + use criterion::{black_box, criterion_group, criterion_main, Criterion}; use ldt_np_adv::*; @@ -30,7 +32,7 @@ let mut rng = rand_pcg::Pcg64::from_seed(seed); for &len in &[1_usize, 10, 1000] { - c.bench_function(&format!("Scan adv with fresh ciphers/{len}"), |b| { + let _ = c.bench_function(&format!("Scan adv with fresh ciphers/{len}"), |b| { let configs = random_configs::<C, _>(&mut rng, len); let payload_len = rng.gen_range(crypto_provider::aes::BLOCK_SIZE..=LDT_XTS_AES_MAX_LEN); let payload = random_vec(&mut rng, payload_len); @@ -42,7 +44,7 @@ black_box(find_matching_item::<C>(&ciphers, salt, &payload)) }); }); - c.bench_function(&format!("Scan adv with existing ciphers/{len}"), |b| { + let _ = c.bench_function(&format!("Scan adv with existing ciphers/{len}"), |b| { let configs = random_configs::<C, _>(&mut rng, len); let payload_len = rng.gen_range(crypto_provider::aes::BLOCK_SIZE..=LDT_XTS_AES_MAX_LEN); let payload = random_vec(&mut rng, payload_len); @@ -64,7 +66,7 @@ payload: &[u8], ) { let padder = salt_padder::<16, C>(salt); - ciphers + let _ = ciphers .iter() .enumerate() .filter_map(|(index, item)| {
diff --git a/nearby/presence/ldt_np_adv/src/lib.rs b/nearby/presence/ldt_np_adv/src/lib.rs index 31aa50d..542d6a5 100644 --- a/nearby/presence/ldt_np_adv/src/lib.rs +++ b/nearby/presence/ldt_np_adv/src/lib.rs
@@ -14,14 +14,6 @@ //! Nearby Presence-specific usage of LDT. #![no_std] -#![forbid(unsafe_code)] -#![deny( - missing_docs, - clippy::indexing_slicing, - clippy::unwrap_used, - clippy::panic, - clippy::expect_used -)] #[cfg(feature = "std")] extern crate std;
diff --git a/nearby/presence/ldt_np_adv/src/np_adv_test_vectors.rs b/nearby/presence/ldt_np_adv/src/np_adv_test_vectors.rs index 90d09b1..cf176dd 100644 --- a/nearby/presence/ldt_np_adv/src/np_adv_test_vectors.rs +++ b/nearby/presence/ldt_np_adv/src/np_adv_test_vectors.rs
@@ -33,7 +33,7 @@ test_helper::get_data_file("presence/ldt_np_adv/resources/test/np_adv_test_vectors.json"); let mut file = fs::File::open(full_path)?; let mut data = String::new(); - file.read_to_string(&mut data)?; + let _ = file.read_to_string(&mut data)?; let test_cases = match serde_json::de::from_str(&data)? { serde_json::Value::Array(a) => a, _ => return Err(anyhow!("bad json")),
diff --git a/nearby/presence/ldt_np_jni/Cargo.toml b/nearby/presence/ldt_np_jni/Cargo.toml index 6803d56..2551c14 100644 --- a/nearby/presence/ldt_np_jni/Cargo.toml +++ b/nearby/presence/ldt_np_jni/Cargo.toml
@@ -4,12 +4,15 @@ edition.workspace = true publish.workspace = true +[lints] +workspace = true + [dependencies] ldt.workspace = true ldt_np_adv.workspace = true np_hkdf.workspace = true crypto_provider.workspace = true -crypto_provider_default = {workspace = true, default-features = false} +crypto_provider_default = { workspace = true, default-features = false } cfg-if.workspace = true jni.workspace = true
diff --git a/nearby/presence/ldt_np_jni/src/lib.rs b/nearby/presence/ldt_np_jni/src/lib.rs index 538a44e..e232c1e 100644 --- a/nearby/presence/ldt_np_jni/src/lib.rs +++ b/nearby/presence/ldt_np_jni/src/lib.rs
@@ -23,7 +23,7 @@ // We are not actually no_std because the jni crate is pulling it in, but at least this enforces // that this lib isn't using anything from the std lib #![no_std] -#![deny(missing_docs)] +#![allow(unsafe_code)] // Allow using Box in no_std extern crate alloc; @@ -86,6 +86,7 @@ })?; let hkdf_key_seed = NpKeySeedHkdf::<CryptoProviderImpl>::new( + #[allow(clippy::expect_used)] key_seed.as_slice().try_into().expect("Length is checked above"), ); @@ -122,9 +123,11 @@ } })?; let hkdf_key_seed = NpKeySeedHkdf::<CryptoProviderImpl>::new( + #[allow(clippy::expect_used)] key_seed.as_slice().try_into().expect("Length is checked above"), ); + #[allow(clippy::expect_used)] let cipher = ldt_np_adv::build_np_adv_decrypter_from_key_seed::<CryptoProviderImpl>( &hkdf_key_seed, hmac_tag.as_slice().try_into().expect("Length is checked above"), @@ -252,7 +255,7 @@ let ret = f(&boxed); // don't consume the box -- need to keep the handle alive - Box::leak(boxed); + let _ = Box::leak(boxed); ret }
diff --git a/nearby/presence/ldt_tbc/Cargo.toml b/nearby/presence/ldt_tbc/Cargo.toml index 0177f87..8941051 100644 --- a/nearby/presence/ldt_tbc/Cargo.toml +++ b/nearby/presence/ldt_tbc/Cargo.toml
@@ -4,6 +4,9 @@ edition.workspace = true publish.workspace = true +[lints] +workspace = true + [features] default = [] std = []
diff --git a/nearby/presence/ldt_tbc/src/lib.rs b/nearby/presence/ldt_tbc/src/lib.rs index d24da7e..aefeb17 100644 --- a/nearby/presence/ldt_tbc/src/lib.rs +++ b/nearby/presence/ldt_tbc/src/lib.rs
@@ -12,14 +12,6 @@ // 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. -#![forbid(unsafe_code)] -#![deny( - missing_docs, - clippy::indexing_slicing, - clippy::unwrap_used, - clippy::panic, - clippy::expect_used -)] //! Defining traits for an LDT specific Tweakable Block Cipher
diff --git a/nearby/presence/np_adv/Cargo.toml b/nearby/presence/np_adv/Cargo.toml index 97e12f5..e502c35 100644 --- a/nearby/presence/np_adv/Cargo.toml +++ b/nearby/presence/np_adv/Cargo.toml
@@ -4,6 +4,9 @@ edition.workspace = true publish.workspace = true +[lints] +workspace = true + [dependencies] array_view = { path = "../array_view" } ldt_np_adv.workspace = true @@ -29,8 +32,7 @@ hex.workspace = true rand.workspace = true rand_ext = { path = "../rand_ext" } -init_with = "1.1.0" -serde_json = {workspace = true, features = ["std"]} +serde_json = { workspace = true, features = ["std"] } serde.workspace = true anyhow.workspace = true test_helper = { path = "../test_helper" }
diff --git a/nearby/presence/np_adv/benches/deser_adv.rs b/nearby/presence/np_adv/benches/deser_adv.rs index e998047..08fd5c4 100644 --- a/nearby/presence/np_adv/benches/deser_adv.rs +++ b/nearby/presence/np_adv/benches/deser_adv.rs
@@ -12,6 +12,15 @@ // See the License for the specific language governing permissions and // limitations under the License. +#![allow( + missing_docs, + unused_results, + clippy::unwrap_used, + clippy::expect_used, + clippy::indexing_slicing, + clippy::panic +)] + use core::marker::PhantomData; use criterion::{black_box, criterion_group, criterion_main, Bencher, Criterion}; use crypto_provider::{CryptoProvider, CryptoRng};
diff --git a/nearby/presence/np_adv/src/array_vec.rs b/nearby/presence/np_adv/src/array_vec.rs index a92ff5b..bcc387e 100644 --- a/nearby/presence/np_adv/src/array_vec.rs +++ b/nearby/presence/np_adv/src/array_vec.rs
@@ -89,12 +89,14 @@ /// (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())) + self.0.sort_unstable_by_key(|a| { + f(a.as_ref().expect("Iterated values in ArrayVecOption should never be None")) + }) } /// 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() + self.0.swap_remove(index).expect("since index is is bounds, the value at index will always be Some which is safe to unwrap") } /// Converts this vector into a regular `Vec`, unwrapping all of the @@ -125,7 +127,7 @@ 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() + self.0[index].as_ref().expect("This panics if provided index is out of bounds") } } @@ -167,7 +169,7 @@ 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); + let _ = vec.swap_remove(2); assert_eq!(vec![1_u32, 2, 5, 4], vec.clone().into_vec()); }
diff --git a/nearby/presence/np_adv/src/credential/v1.rs b/nearby/presence/np_adv/src/credential/v1.rs index 7d060f2..386799f 100644 --- a/nearby/presence/np_adv/src/credential/v1.rs +++ b/nearby/presence/np_adv/src/credential/v1.rs
@@ -18,7 +18,7 @@ protocol_version_seal, BroadcastCryptoMaterial, DiscoveryCryptoMaterial, ProtocolVersion, }; use crate::MetadataKey; -use crypto_provider::{aes::Aes128Key, ed25519, CryptoProvider, CryptoRng}; +use crypto_provider::{aes::Aes128Key, ed25519, ed25519::PublicKey, CryptoProvider, CryptoRng}; use np_hkdf::UnsignedSectionKeys; /// Cryptographic information about a particular V1 discovery credential @@ -32,17 +32,17 @@ } impl V1DiscoveryCredential { /// Construct a V1 discovery credential from the provided identity data. - pub fn new<C: CryptoProvider>( + pub fn new( key_seed: [u8; 32], expected_unsigned_metadata_key_hmac: [u8; 32], expected_signed_metadata_key_hmac: [u8; 32], - pub_key: np_ed25519::PublicKey<C>, + pub_key: ed25519::RawPublicKey, ) -> Self { Self { key_seed, expected_unsigned_metadata_key_hmac, expected_signed_metadata_key_hmac, - pub_key: pub_key.to_bytes(), + pub_key, } } @@ -400,7 +400,7 @@ let key_seed = self.key_seed(); let metadata_key = self.metadata_key(); let pub_key = self.signing_key().derive_public_key::<C::Ed25519>(); - let pub_key = np_ed25519::PublicKey::<C>::new(pub_key); + let pub_key = pub_key.to_bytes(); let hkdf = np_hkdf::NpKeySeedHkdf::<C>::new(&key_seed); let unsigned = hkdf
diff --git a/nearby/presence/np_adv/src/deser_v0_tests.rs b/nearby/presence/np_adv/src/deser_v0_tests.rs index 93c3b97..f6643de 100644 --- a/nearby/presence/np_adv/src/deser_v0_tests.rs +++ b/nearby/presence/np_adv/src/deser_v0_tests.rs
@@ -12,6 +12,8 @@ // See the License for the specific language governing permissions and // limitations under the License. +#![allow(clippy::unwrap_used)] + use rand::{seq::SliceRandom as _, SeedableRng as _}; extern crate std;
diff --git a/nearby/presence/np_adv/src/deser_v1_tests.rs b/nearby/presence/np_adv/src/deser_v1_tests.rs index 903ed3d..cea0063 100644 --- a/nearby/presence/np_adv/src/deser_v1_tests.rs +++ b/nearby/presence/np_adv/src/deser_v1_tests.rs
@@ -12,6 +12,8 @@ // See the License for the specific language governing permissions and // limitations under the License. +#![allow(clippy::unwrap_used)] + use core::marker::PhantomData; use rand::{rngs::StdRng, seq::SliceRandom as _, Rng as _, SeedableRng as _};
diff --git a/nearby/presence/np_adv/src/extended/data_elements/tests.rs b/nearby/presence/np_adv/src/extended/data_elements/tests.rs index a0f9a2f..6f085cd 100644 --- a/nearby/presence/np_adv/src/extended/data_elements/tests.rs +++ b/nearby/presence/np_adv/src/extended/data_elements/tests.rs
@@ -12,6 +12,8 @@ // See the License for the specific language governing permissions and // limitations under the License. +#![allow(clippy::unwrap_used)] + extern crate std; use super::*;
diff --git a/nearby/presence/np_adv/src/extended/de_type.rs b/nearby/presence/np_adv/src/extended/de_type.rs index 105d15e..975df90 100644 --- a/nearby/presence/np_adv/src/extended/de_type.rs +++ b/nearby/presence/np_adv/src/extended/de_type.rs
@@ -36,7 +36,7 @@ impl From<u8> for DeType { fn from(value: u8) -> Self { - DeType { code: value as u32 } + DeType { code: value.into() } } } @@ -46,6 +46,12 @@ } } +impl From<DeType> for u32 { + fn from(value: DeType) -> Self { + value.code + } +} + pub(crate) trait ExtendedDataElementType: Sized { /// A type code for use in the DE header. fn type_code(&self) -> DeType;
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 9bd54b8..bf78b7d 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
@@ -12,6 +12,8 @@ // See the License for the specific language governing permissions and // limitations under the License. +#![allow(clippy::unwrap_used)] + extern crate std; use super::*; @@ -190,7 +192,7 @@ let mut expected = Vec::new(); // battery de expected.extend_from_slice(txpower_de.clone().de_header().serialize().as_slice()); - txpower_de.write_de_contents(&mut expected); + let _ = txpower_de.write_de_contents(&mut expected); assert_eq!(metadata_key, decrypted.metadata_key_plaintext); assert_eq!(&expected, decrypted.plaintext_contents);
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 2236630..eb2fe01 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
@@ -123,8 +123,7 @@ let mut result = Vec::new(); result.extend_from_slice(&self.metadata_key_plaintext.0); result.extend_from_slice(self.plaintext_contents); - // Won't panic because of the involved lengths - ArrayView::try_from_slice(&result).unwrap() + ArrayView::try_from_slice(&result).expect("Won't panic because of the involved lengths") } } @@ -181,8 +180,10 @@ &self, ) -> SectionIdentityResolutionContents { let nonce = self.compute_nonce::<C>(); - let metadata_key_ciphertext: [u8; METADATA_KEY_LEN] = - self.all_ciphertext[..METADATA_KEY_LEN].try_into().unwrap(); + let metadata_key_ciphertext: [u8; METADATA_KEY_LEN] = self.all_ciphertext + [..METADATA_KEY_LEN] + .try_into() + .expect("slice will always fit into same size array"); SectionIdentityResolutionContents { nonce, metadata_key_ciphertext } }
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 2370aeb..af70228 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
@@ -12,6 +12,8 @@ // See the License for the specific language governing permissions and // limitations under the License. +#![allow(clippy::unwrap_used)] + extern crate std; use crate::deserialization_arena; @@ -127,11 +129,11 @@ // plaintext is correct { - let crypto_material = V1DiscoveryCredential::new::<CryptoProviderImpl>( + let crypto_material = V1DiscoveryCredential::new( key_seed, key_seed_hkdf.extended_unsigned_metadata_key_hmac_key().calculate_hmac(&metadata_key.0), key_seed_hkdf.extended_signed_metadata_key_hmac_key().calculate_hmac(&metadata_key.0), - key_pair.public(), + key_pair.public().to_bytes(), ); let signed_identity_resolution_material = crypto_material.signed_identity_resolution_material::<CryptoProviderImpl>(); @@ -150,7 +152,7 @@ let mut expected = Vec::new(); expected.extend_from_slice(txpower_de.de_header().serialize().as_slice()); - txpower_de.write_de_contents(&mut expected); + let _ = txpower_de.write_de_contents(&mut expected); let nonce: AesCtrNonce = section_salt.derive(Some(1.into())).unwrap(); @@ -177,11 +179,11 @@ // deserialization to Section works { - let crypto_material = V1DiscoveryCredential::new::<CryptoProviderImpl>( + let crypto_material = V1DiscoveryCredential::new( key_seed, key_seed_hkdf.extended_unsigned_metadata_key_hmac_key().calculate_hmac(&metadata_key.0), key_seed_hkdf.extended_signed_metadata_key_hmac_key().calculate_hmac(&metadata_key.0), - key_pair.public(), + key_pair.public().to_bytes(), ); let signed_identity_resolution_material = crypto_material.signed_identity_resolution_material::<CryptoProviderImpl>(); @@ -524,11 +526,11 @@ contents ); - let crypto_material = V1DiscoveryCredential::new::<CryptoProviderImpl>( + let crypto_material = V1DiscoveryCredential::new( key_seed, key_seed_hkdf.extended_unsigned_metadata_key_hmac_key().calculate_hmac(&metadata_key.0), key_seed_hkdf.extended_signed_metadata_key_hmac_key().calculate_hmac(&metadata_key.0), - key_pair.public(), + key_pair.public().to_bytes(), ); let identity_resolution_material = crypto_material.signed_identity_resolution_material::<CryptoProviderImpl>();
diff --git a/nearby/presence/np_adv/src/extended/deserialize/mod.rs b/nearby/presence/np_adv/src/extended/deserialize/mod.rs index 4b78a32..6ec7881 100644 --- a/nearby/presence/np_adv/src/extended/deserialize/mod.rs +++ b/nearby/presence/np_adv/src/extended/deserialize/mod.rs
@@ -17,7 +17,7 @@ #[cfg(any(test, feature = "alloc"))] extern crate alloc; -#[cfg(any(test, feature = "alloc"))] +#[cfg(any(test, feature = "alloc", feature = "devtools"))] use alloc::vec::Vec; use core::array::TryFromSliceError; @@ -845,8 +845,11 @@ } fn salt(&self) -> RawV1Salt { - // should never panic - RawV1Salt(self.bytes[Self::TOTAL_DE_LEN - 16..].try_into().ok().unwrap()) + RawV1Salt( + self.bytes[Self::TOTAL_DE_LEN - 16..] + .try_into() + .expect("a 16 byte slice will always fit into a 16 byte array"), + ) } }
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 4afa3ec..41eb054 100644 --- a/nearby/presence/np_adv/src/extended/deserialize/parse_tests.rs +++ b/nearby/presence/np_adv/src/extended/deserialize/parse_tests.rs
@@ -12,6 +12,8 @@ // See the License for the specific language governing permissions and // limitations under the License. +#![allow(clippy::unwrap_used)] + extern crate std; use super::*; use crate::extended::deserialize::encrypted_section::{ @@ -461,7 +463,7 @@ adv_body.extend_from_slice(&[0x81]); } if remove_section_len { - adv_body.remove(0); + let _ = adv_body.remove(0); } let ordered_adv = adv_body.clone(); @@ -476,7 +478,7 @@ // * the identity does not follow the section length // * the section length is 0 if remove_section_len || !add_public_identity || (ordered_adv != adv_body) { - parse_sections(V1Header { header_byte: 0x20 }, &adv_body) + let _ = parse_sections(V1Header { header_byte: 0x20 }, &adv_body) .expect_err("Expected to fail because of missing section length or identity"); } }
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 5b1290f..f7e2025 100644 --- a/nearby/presence/np_adv/src/extended/deserialize/section_tests.rs +++ b/nearby/presence/np_adv/src/extended/deserialize/section_tests.rs
@@ -12,6 +12,8 @@ // See the License for the specific language governing permissions and // limitations under the License. +#![allow(clippy::unwrap_used)] + extern crate std; use super::*; @@ -143,7 +145,7 @@ // Verify that the decrypted metadata contains the chosen index let decrypted_metadata = matched_section.decrypt_metadata::<CryptoProviderImpl>().unwrap(); - assert_eq!(&[chosen_index as u8] as &[u8], &decrypted_metadata); + assert_eq!([chosen_index as u8].as_slice(), &decrypted_metadata); // Verify that the section contents passed through unaltered let section = matched_section.contents(); @@ -254,7 +256,7 @@ // Verify that the decrypted metadata contains the chosen index let decrypted_metadata = matched_section.decrypt_metadata::<CryptoProviderImpl>().unwrap(); - assert_eq!(&[chosen_index as u8] as &[u8], &decrypted_metadata); + assert_eq!([chosen_index as u8].as_slice(), &decrypted_metadata); // Verify that the section contents passed through unaltered let section = matched_section.contents(); @@ -411,7 +413,7 @@ } else { panic!("incorrect header"); }; - parse_sections(v1_header, remaining).expect_err("Expected an error"); + let _ = parse_sections(v1_header, remaining).expect_err("Expected an error"); } #[test] @@ -424,7 +426,7 @@ } else { panic!("incorrect header"); }; - parse_sections(v1_header, remaining).expect_err("Expected an error"); + let _ = parse_sections(v1_header, remaining).expect_err("Expected an error"); } #[test] @@ -463,7 +465,7 @@ } else { panic!("incorrect header"); }; - parse_sections(v1_header, remaining) + let _ = parse_sections(v1_header, remaining) .expect_err("Expected an error because number of sections is over limit"); } @@ -557,7 +559,7 @@ des.iter() .map(|de| { let mut buf = vec![]; - de.write_de_contents(&mut buf); + let _ = de.write_de_contents(&mut buf); de.de_header().serialize().len() + buf.len() }) .sum()
diff --git a/nearby/presence/np_adv/src/extended/serialize/adv_tests.rs b/nearby/presence/np_adv/src/extended/serialize/adv_tests.rs index 36e4e5f..ee44c75 100644 --- a/nearby/presence/np_adv/src/extended/serialize/adv_tests.rs +++ b/nearby/presence/np_adv/src/extended/serialize/adv_tests.rs
@@ -12,6 +12,8 @@ // See the License for the specific language governing permissions and // limitations under the License. +#![allow(clippy::unwrap_used)] + extern crate std; use super::*; use crate::extended::serialize::section_tests::{fill_section_builder, DummyDataElement};
diff --git a/nearby/presence/np_adv/src/extended/serialize/de_header_tests.rs b/nearby/presence/np_adv/src/extended/serialize/de_header_tests.rs index eda8b69..cb6c9ef 100644 --- a/nearby/presence/np_adv/src/extended/serialize/de_header_tests.rs +++ b/nearby/presence/np_adv/src/extended/serialize/de_header_tests.rs
@@ -12,6 +12,8 @@ // See the License for the specific language governing permissions and // limitations under the License. +#![allow(clippy::unwrap_used)] + use super::*; use crate::extended::deserialize; use core::cmp;
diff --git a/nearby/presence/np_adv/src/extended/serialize/mod.rs b/nearby/presence/np_adv/src/extended/serialize/mod.rs index f736dea..e753281 100644 --- a/nearby/presence/np_adv/src/extended/serialize/mod.rs +++ b/nearby/presence/np_adv/src/extended/serialize/mod.rs
@@ -537,7 +537,7 @@ before_sig.split_at(EncryptionInfo::TOTAL_DE_LEN); let encryption_info: &[u8; EncryptionInfo::TOTAL_DE_LEN] = - encryption_info.try_into().unwrap(); + encryption_info.try_into().expect("encryption info should always be the correct size"); // we need to sign the 16-byte IV, which doesn't have to actually fit in the adv, but we // don't need a bigger buffer here since we won't be including the 66 bytes for the sig +
diff --git a/nearby/presence/np_adv/src/extended/serialize/section_tests.rs b/nearby/presence/np_adv/src/extended/serialize/section_tests.rs index d418a50..68e5b60 100644 --- a/nearby/presence/np_adv/src/extended/serialize/section_tests.rs +++ b/nearby/presence/np_adv/src/extended/serialize/section_tests.rs
@@ -12,6 +12,8 @@ // See the License for the specific language governing permissions and // limitations under the License. +#![allow(clippy::unwrap_used)] + extern crate std; use super::*; @@ -129,7 +131,7 @@ for de in extra_des { plaintext.extend_from_slice(de.de_header().serialize().as_slice()); - de.write_de_contents(&mut plaintext); + let _ = de.write_de_contents(&mut plaintext); } cipher.apply_keystream(&mut plaintext); @@ -232,7 +234,7 @@ let mut section_body = Vec::new(); for de in extra_des { section_body.extend_from_slice(de.de_header().serialize().as_slice()); - de.write_de_contents(&mut section_body); + let _ = de.write_de_contents(&mut section_body); } let sig_payload = SectionSignaturePayload::from_deserialized_parts( @@ -692,7 +694,7 @@ for de in extra_des { plaintext.extend_from_slice(de.de_header().serialize().as_slice()); - de.write_de_contents(&mut plaintext); + let _ = de.write_de_contents(&mut plaintext); } cipher.apply_keystream(&mut plaintext); @@ -765,7 +767,7 @@ let mut section_body = Vec::new(); for de in extra_des { section_body.extend_from_slice(de.de_header().serialize().as_slice()); - de.write_de_contents(&mut section_body); + let _ = de.write_de_contents(&mut section_body); } let nonce = section_salt.derive(Some(1.into())).unwrap();
diff --git a/nearby/presence/np_adv/src/extended/serialize/test_vectors.rs b/nearby/presence/np_adv/src/extended/serialize/test_vectors.rs index 24e6046..d03ad9b 100644 --- a/nearby/presence/np_adv/src/extended/serialize/test_vectors.rs +++ b/nearby/presence/np_adv/src/extended/serialize/test_vectors.rs
@@ -12,6 +12,8 @@ // See the License for the specific language governing permissions and // limitations under the License. +#![allow(clippy::unwrap_used)] + extern crate std; use crate::extended::serialize::AdvertisementType; @@ -42,7 +44,7 @@ ); let mut file = fs::File::open(full_path)?; let mut data = String::new(); - file.read_to_string(&mut data)?; + let _ = file.read_to_string(&mut data)?; let test_cases = match serde_json::de::from_str(&data)? { serde_json::Value::Array(a) => a,
diff --git a/nearby/presence/np_adv/src/filter/tests/actions_filter_tests.rs b/nearby/presence/np_adv/src/filter/tests/actions_filter_tests.rs index 946c969..ffdeeb9 100644 --- a/nearby/presence/np_adv/src/filter/tests/actions_filter_tests.rs +++ b/nearby/presence/np_adv/src/filter/tests/actions_filter_tests.rs
@@ -12,6 +12,8 @@ // See the License for the specific language governing permissions and // limitations under the License. +#![allow(clippy::unwrap_used)] + use super::super::*; use crate::legacy::actions::{ActionBits, InstantTethering, NearbyShare}; use crate::legacy::{Ciphertext, Plaintext};
diff --git a/nearby/presence/np_adv/src/header_parse_tests.rs b/nearby/presence/np_adv/src/header_parse_tests.rs index 2da1b4a..f6b5533 100644 --- a/nearby/presence/np_adv/src/header_parse_tests.rs +++ b/nearby/presence/np_adv/src/header_parse_tests.rs
@@ -12,6 +12,8 @@ // See the License for the specific language governing permissions and // limitations under the License. +#![allow(clippy::unwrap_used)] + use super::*; extern crate std;
diff --git a/nearby/presence/np_adv/src/legacy/actions/tests.rs b/nearby/presence/np_adv/src/legacy/actions/tests.rs index 86b7a08..d45d968 100644 --- a/nearby/presence/np_adv/src/legacy/actions/tests.rs +++ b/nearby/presence/np_adv/src/legacy/actions/tests.rs
@@ -12,6 +12,8 @@ // See the License for the specific language governing permissions and // limitations under the License. +#![allow(clippy::unwrap_used)] + extern crate std; use crate::legacy::{
diff --git a/nearby/presence/np_adv/src/legacy/de_type/mod.rs b/nearby/presence/np_adv/src/legacy/de_type/mod.rs index 5c7ebfa..304347d 100644 --- a/nearby/presence/np_adv/src/legacy/de_type/mod.rs +++ b/nearby/presence/np_adv/src/legacy/de_type/mod.rs
@@ -47,6 +47,7 @@ } } +/// The DE type code is out of range for v0 DE types. #[derive(Debug, PartialEq, Eq, Clone, Copy)] pub(crate) struct DeTypeCodeOutOfRange;
diff --git a/nearby/presence/np_adv/src/legacy/de_type/tests.rs b/nearby/presence/np_adv/src/legacy/de_type/tests.rs index 59075aa..fc64321 100644 --- a/nearby/presence/np_adv/src/legacy/de_type/tests.rs +++ b/nearby/presence/np_adv/src/legacy/de_type/tests.rs
@@ -12,6 +12,8 @@ // See the License for the specific language governing permissions and // limitations under the License. +#![allow(clippy::unwrap_used)] + extern crate std; use super::*;
diff --git a/nearby/presence/np_adv/src/legacy/deserialize/mod.rs b/nearby/presence/np_adv/src/legacy/deserialize/mod.rs index 938045d..d6c6b36 100644 --- a/nearby/presence/np_adv/src/legacy/deserialize/mod.rs +++ b/nearby/presence/np_adv/src/legacy/deserialize/mod.rs
@@ -359,6 +359,33 @@ TxPower(TxPowerDataElement), } +impl<F: PacketFlavor> PlainDataElement<F> { + /// Returns the DE type as a u8 + #[cfg(feature = "devtools")] + pub fn de_type_code(&self) -> u8 { + match self { + PlainDataElement::Actions(_) => DataElementType::Actions.type_code().as_u8(), + PlainDataElement::TxPower(_) => DataElementType::TxPower.type_code().as_u8(), + } + } + + /// Returns the serialized contents of the DE + #[cfg(feature = "devtools")] + pub fn de_contents(&self) -> alloc::vec::Vec<u8> { + use crate::legacy::serialize::{DataElementBundle, ToDataElementBundle}; + match self { + PlainDataElement::Actions(a) => { + let bundle: DataElementBundle<F> = a.to_de_bundle(); + bundle.contents_as_slice().to_vec() + } + PlainDataElement::TxPower(t) => { + let bundle: DataElementBundle<F> = t.to_de_bundle(); + bundle.contents_as_slice().to_vec() + } + } + } +} + /// The contents of a plaintext advertisement after deserializing DE contents #[derive(Debug, PartialEq, Eq)] pub struct PlaintextAdvContents<'d> {
diff --git a/nearby/presence/np_adv/src/legacy/deserialize/tests.rs b/nearby/presence/np_adv/src/legacy/deserialize/tests.rs index 5725e0b..3dfecd1 100644 --- a/nearby/presence/np_adv/src/legacy/deserialize/tests.rs +++ b/nearby/presence/np_adv/src/legacy/deserialize/tests.rs
@@ -12,32 +12,32 @@ // See the License for the specific language governing permissions and // limitations under the License. +#![allow(unused_results, clippy::unwrap_used)] + extern crate alloc; extern crate std; use super::*; -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}, de_type::IdentityDataElementType, legacy::{ - actions, + actions::{self, ActionBits, ActionsDataElement, Finder, NearbyShare}, de_type::DeActualLength, random_data_elements::{random_de_ciphertext, random_de_plaintext}, serialize::{ - encode_de_header_actual_len, AdvBuilder, DataElementBundle, Identity, LdtIdentity, - ToDataElementBundle as _, + encode_de_header_actual_len, id_de_type_as_generic_de_type, AdvBuilder, + DataElementBundle, Identity, LdtIdentity, ToDataElementBundle, }, PacketFlavorEnum, BLE_ADV_SVC_CONTENT_LEN, }, - parse_adv_header, shared_data, AdvHeader, PublicIdentity, + parse_adv_header, shared_data, + shared_data::TxPower, + 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::{self, ErrorKind}; use rand_ext::rand::{prelude::SliceRandom, Rng as _}; @@ -357,7 +357,10 @@ #[test] fn parse_encrypted_identity_contents_too_short_error() { // 2 byte salt + 15 byte ciphertext: 1 too short - let input = <[u8; 17]>::init_with_indices(|i| i as u8); + let mut input = [0u8; 17]; + for (pos, e) in input.iter_mut().enumerate() { + *e = pos as u8 + } assert_eq!( nom::Err::Error(error::Error { input: &input[2..], code: error::ErrorKind::TakeWhileMN }), parse_encrypted_identity_de_contents(&input).unwrap_err() @@ -367,7 +370,10 @@ #[test] fn parse_encrypted_identity_contents_ok() { // 2 byte salt + minimum 16 byte ciphertext - let input = <[u8; 18]>::init_with_indices(|i| i as u8); + let mut input = [0u8; 18]; + for (pos, e) in input.iter_mut().enumerate() { + *e = pos as u8 + } assert_eq!( ([].as_slice(), (ldt_np_adv::LegacySalt::from([0, 1]), &input[2..])), parse_encrypted_identity_de_contents(&input).unwrap()
diff --git a/nearby/presence/np_adv/src/legacy/random_data_elements.rs b/nearby/presence/np_adv/src/legacy/random_data_elements.rs index f416ffe..00a2e93 100644 --- a/nearby/presence/np_adv/src/legacy/random_data_elements.rs +++ b/nearby/presence/np_adv/src/legacy/random_data_elements.rs
@@ -12,6 +12,8 @@ // See the License for the specific language governing permissions and // limitations under the License. +#![allow(clippy::unwrap_used)] + extern crate std; use crate::{
diff --git a/nearby/presence/np_adv/src/legacy/serialize/mod.rs b/nearby/presence/np_adv/src/legacy/serialize/mod.rs index 2920b67..3746796 100644 --- a/nearby/presence/np_adv/src/legacy/serialize/mod.rs +++ b/nearby/presence/np_adv/src/legacy/serialize/mod.rs
@@ -111,7 +111,7 @@ lazy_static::lazy_static! { // Avoid either a panic-able code path or an error case that never happens by precalculating. static ref PUBLIC_IDENTITY_DE_HEADER: u8 = - encode_de_header_actual_len(DataElementType::PublicIdentity, DeActualLength::ZERO).unwrap(); + encode_de_header_actual_len(DataElementType::PublicIdentity, DeActualLength::ZERO).expect("de length is in range"); } impl Identity for PublicIdentity {
diff --git a/nearby/presence/np_adv/src/legacy/serialize/tests.rs b/nearby/presence/np_adv/src/legacy/serialize/tests.rs index b4adc9f..7be30ba 100644 --- a/nearby/presence/np_adv/src/legacy/serialize/tests.rs +++ b/nearby/presence/np_adv/src/legacy/serialize/tests.rs
@@ -12,6 +12,8 @@ // See the License for the specific language governing permissions and // limitations under the License. +#![allow(clippy::unwrap_used)] + extern crate std; use crate::legacy::actions::FastPairSass;
diff --git a/nearby/presence/np_adv/src/lib.rs b/nearby/presence/np_adv/src/lib.rs index 1c42796..0b6d7ae 100644 --- a/nearby/presence/np_adv/src/lib.rs +++ b/nearby/presence/np_adv/src/lib.rs
@@ -11,6 +11,7 @@ // 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. + //! Serialization and deserialization for v0 (legacy) and v1 (extended) Nearby Presence //! advertisements. //! @@ -18,16 +19,13 @@ //! deserialization scenarios. #![no_std] -#![forbid(unsafe_code)] -#![deny(missing_docs)] +#![allow(clippy::expect_used, clippy::indexing_slicing, clippy::panic)] -#[cfg(any(test, feature = "alloc"))] -extern crate alloc; -extern crate core; use crate::{ credential::{ - book::CredentialBook, v0::V0DiscoveryCryptoMaterial, v1::V1DiscoveryCryptoMaterial, - DiscoveryCryptoMaterial, MatchedCredential, ProtocolVersion, + book::CredentialBook, v0::V0DiscoveryCryptoMaterial, v0::V0, v1::V1DiscoveryCryptoMaterial, + v1::V1, DiscoveryCryptoMaterial, MatchedCredential, ProtocolVersion, + ReferencedMatchedCredential, }, deserialization_arena::ArenaOutOfSpace, extended::deserialize::{ @@ -41,7 +39,10 @@ }; #[cfg(any(test, feature = "alloc"))] +extern crate alloc; +#[cfg(any(test, feature = "alloc"))] use alloc::vec::Vec; + use array_vec::ArrayVecOption; #[cfg(feature = "devtools")] use array_view::ArrayView; @@ -307,7 +308,7 @@ // By default, if we have an identity match, assume that decrypting the section worked, // or that the section was somehow invalid. // We don't care about maintaining order, so use O(1) remove - self.encrypted_sections.swap_remove(i); + let _ = self.encrypted_sections.swap_remove(i); // don't advance i -- it now points to a new element } } @@ -564,6 +565,20 @@ fn metadata_key(&self) -> <Self::Version as ProtocolVersion>::MetadataKey; } +impl HasIdentityMatch for legacy::ShortMetadataKey { + type Version = V0; + fn metadata_key(&self) -> Self { + *self + } +} + +impl HasIdentityMatch for MetadataKey { + type Version = V1; + fn metadata_key(&self) -> Self { + *self + } +} + #[cfg(any(test, feature = "alloc"))] /// Type for errors from [`WithMatchedCredential#decrypt_metadata`] #[derive(Debug)] @@ -589,10 +604,34 @@ metadata_nonce: [u8; 12], contents: T, } +impl<'a, M: MatchedCredential + Clone, T: HasIdentityMatch> + WithMatchedCredential<ReferencedMatchedCredential<'a, M>, T> +{ + /// Clones the referenced match-data to update this container + /// so that the match-data is owned, rather than borrowed. + pub fn clone_match_data(self) -> WithMatchedCredential<M, T> { + let matched = self.matched.as_ref().clone(); + let metadata_nonce = self.metadata_nonce; + let contents = self.contents; + + WithMatchedCredential { matched, metadata_nonce, contents } + } +} impl<M: MatchedCredential, T: HasIdentityMatch> WithMatchedCredential<M, T> { fn new(matched: M, metadata_nonce: [u8; 12], contents: T) -> Self { Self { matched, metadata_nonce, contents } } + /// Applies the given function to the wrapped contents, yielding + /// a new instance with the same matched-credential. + pub fn map<R: HasIdentityMatch>( + self, + mapping: impl FnOnce(T) -> R, + ) -> WithMatchedCredential<M, R> { + let contents = mapping(self.contents); + let matched = self.matched; + let metadata_nonce = self.metadata_nonce; + WithMatchedCredential { matched, metadata_nonce, contents } + } /// Credential data for the credential that decrypted the content. pub fn matched_credential(&self) -> &M { &self.matched
diff --git a/nearby/presence/np_adv/tests/examples_v0.rs b/nearby/presence/np_adv/tests/examples_v0.rs index 8310531..f7b7d5e 100644 --- a/nearby/presence/np_adv/tests/examples_v0.rs +++ b/nearby/presence/np_adv/tests/examples_v0.rs
@@ -11,6 +11,8 @@ // See the License for the specific language governing permissions and // limitations under the License. +#![allow(clippy::unwrap_used, clippy::expect_used, clippy::indexing_slicing, clippy::panic)] + use crypto_provider_default::CryptoProviderImpl; use ldt_np_adv::*; use np_adv::legacy::data_elements::TxPowerDataElement;
diff --git a/nearby/presence/np_adv/tests/examples_v1.rs b/nearby/presence/np_adv/tests/examples_v1.rs index bf633e5..94b9d75 100644 --- a/nearby/presence/np_adv/tests/examples_v1.rs +++ b/nearby/presence/np_adv/tests/examples_v1.rs
@@ -12,6 +12,8 @@ // See the License for the specific language governing permissions and // limitations under the License. +#![allow(clippy::unwrap_used, clippy::expect_used, clippy::indexing_slicing, clippy::panic)] + use crypto_provider::{CryptoProvider, CryptoRng}; use crypto_provider_default::CryptoProviderImpl; use np_adv::extended::data_elements::TxPowerDataElement; @@ -144,7 +146,7 @@ key_seed, [0; 32], // Zeroing out MIC HMAC, since it's unused in examples here. hkdf.extended_signed_metadata_key_hmac_key().calculate_hmac(&metadata_key.0), - key_pair.public(), + key_pair.public().to_bytes(), ); let credentials: [MatchableCredential<V1, MetadataMatchedCredential<_>>; 1] =
diff --git a/nearby/presence/np_adv_dynamic/Cargo.toml b/nearby/presence/np_adv_dynamic/Cargo.toml new file mode 100644 index 0000000..74d6e3a --- /dev/null +++ b/nearby/presence/np_adv_dynamic/Cargo.toml
@@ -0,0 +1,15 @@ +[package] +name = "np_adv_dynamic" +version.workspace = true +edition.workspace = true +publish.workspace = true + +[lints] +workspace = true + +[dependencies] +array_view.workspace = true +np_adv = { workspace = true, features = ["alloc"] } +crypto_provider.workspace = true +thiserror.workspace = true +sink.workspace = true
diff --git a/nearby/presence/np_adv_dynamic/src/extended.rs b/nearby/presence/np_adv_dynamic/src/extended.rs new file mode 100644 index 0000000..f961bd6 --- /dev/null +++ b/nearby/presence/np_adv_dynamic/src/extended.rs
@@ -0,0 +1,265 @@ +// 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. + +use crypto_provider::CryptoProvider; +use np_adv::{extended::data_elements::*, extended::serialize::*, shared_data::*}; +use sink::Sink; +use thiserror::Error; + +/// An advertisement builder for V1 advertisements where the +/// presence/absence of salt is determined at run-time instead of compile-time. +pub struct BoxedAdvBuilder { + adv_builder: AdvBuilder, +} + +impl From<AdvBuilder> for BoxedAdvBuilder { + fn from(adv_builder: AdvBuilder) -> Self { + BoxedAdvBuilder { adv_builder } + } +} + +/// Error possibly generated when attempting to add a section to +/// a BoxedAdvBuilder. +#[derive(Debug, Error)] +pub enum BoxedAddSectionError { + /// An error which was generated by the underlying AdvBuilder wrapped by the BoxedAdvBuilder + #[error("{0}")] + Underlying(AddSectionError), + /// An error generated when the boxed advertisement builder is unsalted, but the section + /// identity requires salt. + #[error("Error generated when the BoxedAdvBuilder is unsalted, but the section identity requires salt.")] + IdentityRequiresSaltError, +} + +impl From<AddSectionError> for BoxedAddSectionError { + fn from(wrapped: AddSectionError) -> Self { + BoxedAddSectionError::Underlying(wrapped) + } +} + +fn wrap_section_builder<'a, C: CryptoProvider, S: Into<BoxedSectionBuilder<'a, C>>>( + maybe_section_builder: Result<S, AddSectionError>, +) -> Result<BoxedSectionBuilder<'a, C>, BoxedAddSectionError> { + let section_builder = maybe_section_builder?; + Ok(section_builder.into()) +} + +impl BoxedAdvBuilder { + /// Create a section builder using the given identity. + /// + /// Returns `Err` if the underlying advertisement builder + /// yields an error when attempting to add a new section + /// (typically because there's no more available adv space), + /// or if the requested identity requires salt, and the + /// advertisement builder is salt-less. + pub fn section_builder<C: CryptoProvider>( + &mut self, + identity: BoxedIdentity<C>, + ) -> Result<BoxedSectionBuilder<C>, BoxedAddSectionError> { + match identity { + BoxedIdentity::PublicIdentity => wrap_section_builder( + self.adv_builder.section_builder(PublicSectionEncoder::default()), + ), + BoxedIdentity::MicEncrypted(ident) => { + wrap_section_builder(self.adv_builder.section_builder(ident)) + } + BoxedIdentity::SignedEncrypted(ident) => { + wrap_section_builder(self.adv_builder.section_builder(ident)) + } + } + } + + /// Convert the builder into an encoded advertisement. + pub fn into_advertisement(self) -> EncodedAdvertisement { + self.adv_builder.into_advertisement() + } +} + +/// A wrapped v1 identity whose type is given at run-time. +pub enum BoxedIdentity<C: CryptoProvider> { + /// Public identity. + PublicIdentity, + /// An encrypted identity leveraging MIC for verification. + MicEncrypted(MicEncryptedSectionEncoder<C>), + /// An encrypted identity leveraging signatures for verification. + SignedEncrypted(SignedEncryptedSectionEncoder<C>), +} + +/// A `SectionBuilder` whose corresponding Identity +/// and salted-ness is given at run-time instead of +/// at compile-time. +pub enum BoxedSectionBuilder<'a, C: CryptoProvider> { + /// A builder for a public section. + Public(SectionBuilder<'a, PublicSectionEncoder>), + /// A builder for a MIC-verified section. + MicEncrypted(SectionBuilder<'a, MicEncryptedSectionEncoder<C>>), + /// A builder for a signature-verified section. + SignedEncrypted(SectionBuilder<'a, SignedEncryptedSectionEncoder<C>>), +} + +impl<'a, C: CryptoProvider> BoxedSectionBuilder<'a, C> { + /// Returns true if this wraps a section builder which + /// leverages some encrypted identity. + pub fn is_encrypted(&self) -> bool { + match self { + BoxedSectionBuilder::Public(_) => false, + BoxedSectionBuilder::MicEncrypted(_) => true, + BoxedSectionBuilder::SignedEncrypted(_) => true, + } + } + /// Add this builder to the advertisement that created it. + pub fn add_to_advertisement(self) { + match self { + BoxedSectionBuilder::Public(x) => x.add_to_advertisement(), + BoxedSectionBuilder::MicEncrypted(x) => x.add_to_advertisement(), + BoxedSectionBuilder::SignedEncrypted(x) => x.add_to_advertisement(), + } + } + /// Add a data element to the section with a closure that returns a `Result`. + /// + /// The provided `build_de` closure will be invoked with the derived salt for this DE, + /// if any salt has been specified for the surrounding advertisement. + pub fn add_de_res<E>( + &mut self, + build_de: impl FnOnce(Option<DeSalt<C>>) -> Result<BoxedWriteDataElement, E>, + ) -> Result<(), AddDataElementError<E>> { + match self { + BoxedSectionBuilder::Public(x) => { + let build_de_modified = |()| build_de(None); + x.add_de_res(build_de_modified) + } + BoxedSectionBuilder::MicEncrypted(x) => { + let build_de_modified = |de_salt: DeSalt<C>| build_de(Some(de_salt)); + x.add_de_res(build_de_modified) + } + BoxedSectionBuilder::SignedEncrypted(x) => { + let build_de_modified = |de_salt: DeSalt<C>| build_de(Some(de_salt)); + x.add_de_res(build_de_modified) + } + } + } + /// Like add_de_res, but for infalliable closures + pub fn add_de( + &mut self, + build_de: impl FnOnce(Option<DeSalt<C>>) -> BoxedWriteDataElement, + ) -> Result<(), AddDataElementError<()>> { + self.add_de_res(|derived_salt| Ok::<_, ()>(build_de(derived_salt))) + } +} + +impl<'a, C: CryptoProvider> From<SectionBuilder<'a, PublicSectionEncoder>> + for BoxedSectionBuilder<'a, C> +{ + fn from(section_builder: SectionBuilder<'a, PublicSectionEncoder>) -> Self { + BoxedSectionBuilder::Public(section_builder) + } +} + +impl<'a, C: CryptoProvider> From<SectionBuilder<'a, MicEncryptedSectionEncoder<C>>> + for BoxedSectionBuilder<'a, C> +{ + fn from(section_builder: SectionBuilder<'a, MicEncryptedSectionEncoder<C>>) -> Self { + BoxedSectionBuilder::MicEncrypted(section_builder) + } +} + +impl<'a, C: CryptoProvider> From<SectionBuilder<'a, SignedEncryptedSectionEncoder<C>>> + for BoxedSectionBuilder<'a, C> +{ + fn from(section_builder: SectionBuilder<'a, SignedEncryptedSectionEncoder<C>>) -> Self { + BoxedSectionBuilder::SignedEncrypted(section_builder) + } +} + +/// Mutable trait object reference to a `Sink<u8>` +pub struct DynSink<'a> { + wrapped: &'a mut dyn Sink<u8>, +} + +impl<'a> Sink<u8> for DynSink<'a> { + fn try_extend_from_slice(&mut self, items: &[u8]) -> Option<()> { + self.wrapped.try_extend_from_slice(items) + } + fn try_push(&mut self, item: u8) -> Option<()> { + self.wrapped.try_push(item) + } +} + +impl<'a> From<&'a mut dyn Sink<u8>> for DynSink<'a> { + fn from(wrapped: &'a mut dyn Sink<u8>) -> Self { + DynSink { wrapped } + } +} + +/// A version of the WriteDataElement trait which is object-safe +pub trait DynWriteDataElement { + /// Gets the data-element header for the data element + fn de_header(&self) -> DeHeader; + /// Writes the contents of the DE payload to the given DynSink. + /// Returns Some(()) if the write operation was successful, + /// and None if it was unsuccessful + fn write_de_contents(&self, sink: DynSink) -> Option<()>; +} + +impl<T: WriteDataElement> DynWriteDataElement for T { + fn de_header(&self) -> DeHeader { + WriteDataElement::de_header(self) + } + fn write_de_contents(&self, mut sink: DynSink) -> Option<()> { + WriteDataElement::write_de_contents(self, &mut sink) + } +} + +/// Trait object wrapper for DynWriteDataElement instances +pub struct BoxedWriteDataElement { + wrapped: Box<dyn DynWriteDataElement>, +} + +impl BoxedWriteDataElement { + /// Constructs a new `BoxedWriteDataElement` from a `WriteDataElement` + /// whose trait impl is valid for a `'static` lifetime. + pub fn new<D: WriteDataElement + 'static>(wrapped: D) -> Self { + let wrapped = Box::new(wrapped); + Self { wrapped } + } +} + +impl WriteDataElement for BoxedWriteDataElement { + fn de_header(&self) -> DeHeader { + self.wrapped.de_header() + } + fn write_de_contents<S: Sink<u8>>(&self, sink: &mut S) -> Option<()> { + let sink: &mut dyn Sink<u8> = sink; + let dyn_sink = DynSink::from(sink); + self.wrapped.write_de_contents(dyn_sink) + } +} + +impl From<TxPower> for BoxedWriteDataElement { + fn from(tx_power: TxPower) -> Self { + BoxedWriteDataElement::new::<TxPowerDataElement>(tx_power.into()) + } +} + +impl From<ContextSyncSeqNum> for BoxedWriteDataElement { + fn from(context_sync_sequence_num: ContextSyncSeqNum) -> Self { + BoxedWriteDataElement::new::<ContextSyncSeqNumDataElement>(context_sync_sequence_num.into()) + } +} + +impl From<ActionsDataElement> for BoxedWriteDataElement { + fn from(data: ActionsDataElement) -> Self { + BoxedWriteDataElement::new(data) + } +}
diff --git a/nearby/presence/np_adv_dynamic/src/legacy.rs b/nearby/presence/np_adv_dynamic/src/legacy.rs new file mode 100644 index 0000000..191ef75 --- /dev/null +++ b/nearby/presence/np_adv_dynamic/src/legacy.rs
@@ -0,0 +1,349 @@ +// 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. + +use array_view::ArrayView; +use crypto_provider::CryptoProvider; +use np_adv::legacy::actions::*; +use np_adv::legacy::data_elements::*; +use np_adv::legacy::serialize::*; +use np_adv::legacy::*; +use np_adv::shared_data::*; +use np_adv::PublicIdentity; +use thiserror::Error; + +/// Wrapper around a V0 advertisement builder which +/// is agnostic to the kind of identity used in the advertisement. +/// Instead of compile-time errors for non-matching packet flavors, +/// this builder instead defers any generated errors to run-time. +/// Generic over the Aes algorithm used for any encrypted identities, +/// since that is generally specified at compile-time. +pub enum BoxedAdvBuilder<C: CryptoProvider> { + /// Builder for public advertisements. + Public(AdvBuilder<PublicIdentity>), + /// Builder for LDT-encryptedadvertisements. + Ldt(AdvBuilder<LdtIdentity<C>>), +} + +/// Wrapper around possible errors which occur only during +/// advertisement construction from a builder. +#[derive(Debug)] +pub enum BoxedAdvConstructionError { + /// An error originating from a problem with LDT + /// encryption of the advertisement contents. + Ldt(LdtPostprocessError), +} + +impl<C: CryptoProvider> BoxedAdvBuilder<C> { + /// Returns true if this wraps an adv builder which + /// leverages some encrypted identity. + pub fn is_encrypted(&self) -> bool { + match self { + BoxedAdvBuilder::Public(_) => false, + BoxedAdvBuilder::Ldt(_) => true, + } + } + /// Constructs a new BoxedAdvBuilder from the given BoxedIdentity + pub fn new(identity: BoxedIdentity<C>) -> Self { + match identity { + BoxedIdentity::Public(identity) => BoxedAdvBuilder::Public(AdvBuilder::new(identity)), + BoxedIdentity::LdtIdentity(identity) => BoxedAdvBuilder::Ldt(AdvBuilder::new(identity)), + } + } + /// Attempts to add a data element to the advertisement + /// being built by this BoxedAdvBuilder. Returns + /// nothing if successful, and a BoxedAddDataElementError + /// if something went wrong in the attempt to add the DE. + pub fn add_data_element( + &mut self, + data_element: ToBoxedDataElementBundle, + ) -> Result<(), BoxedAddDataElementError> { + match self { + BoxedAdvBuilder::Public(public_builder) => { + //Verify that we can get the data element as plaintext + let maybe_plaintext_data_element = data_element.to_plaintext(); + match maybe_plaintext_data_element { + Some(plaintext_data_element) => public_builder + .add_data_element(plaintext_data_element) + .map_err(|e| e.into()), + None => Err(BoxedAddDataElementError::FlavorMismatchError), + } + } + BoxedAdvBuilder::Ldt(private_builder) => { + //Verify that we can get the data element as ciphertext + let maybe_ciphertext_data_element = data_element.to_ciphertext(); + match maybe_ciphertext_data_element { + Some(ciphertext_data_element) => private_builder + .add_data_element(ciphertext_data_element) + .map_err(|e| e.into()), + None => Err(BoxedAddDataElementError::FlavorMismatchError), + } + } + } + } + /// Consume this BoxedAdvBuilder and attempt to create + /// a serialized advertisement including the added DEs. + pub fn into_advertisement( + self, + ) -> Result<ArrayView<u8, BLE_ADV_SVC_CONTENT_LEN>, BoxedAdvConstructionError> { + match self { + BoxedAdvBuilder::Public(x) => { + match x.into_advertisement() { + Ok(x) => Ok(x), + Err(x) => match x {}, //Infallible + } + } + BoxedAdvBuilder::Ldt(x) => { + x.into_advertisement().map_err(BoxedAdvConstructionError::Ldt) + } + } + } +} + +/// Possible errors generated when trying to add a DE to a +/// BoxedAdvBuilder. +#[derive(Debug, Error)] +pub enum BoxedAddDataElementError { + /// Some kind of error in adding the data element which + /// is not an issue with trying to add a DE of the incorrect + /// packet flavoring. + #[error("{0:?}")] + UnderlyingError(AddDataElementError), + #[error( + "Expected packet flavoring for added DEs does not match the actual packet flavor of the DE" + )] + /// Error when attempting to add a DE which requires one + /// of an (encrypted/plaintext) advertisement, but the + /// advertisement builder doesn't match this requirement. + FlavorMismatchError, +} + +impl From<AddDataElementError> for BoxedAddDataElementError { + fn from(add_data_element_error: AddDataElementError) -> Self { + BoxedAddDataElementError::UnderlyingError(add_data_element_error) + } +} + +/// Trait object reference to a `ToDataElementBundle<I>` with lifetime `'a`. +/// Implements `ToDataElementBundle<I>` by deferring to the wrapped trait object. +pub struct DynamicToDataElementBundle<'a, I: PacketFlavor> { + wrapped: &'a dyn ToDataElementBundle<I>, +} + +impl<'a, I: PacketFlavor> From<&'a dyn ToDataElementBundle<I>> + for DynamicToDataElementBundle<'a, I> +{ + fn from(wrapped: &'a dyn ToDataElementBundle<I>) -> Self { + DynamicToDataElementBundle { wrapped } + } +} + +impl<'a, I: PacketFlavor> ToDataElementBundle<I> for DynamicToDataElementBundle<'a, I> { + fn to_de_bundle(&self) -> DataElementBundle<I> { + self.wrapped.to_de_bundle() + } +} + +/// Trait for types which can provide trait object +/// references to either plaintext or ciphertext [ToDataElementBundle] +pub trait ToMultiFlavorElementBundle { + /// Gets the associated trait object reference to a `ToDataElementBundle<Plaintext>` + /// with the same lifetime as a reference to the implementor. + fn to_plaintext(&self) -> DynamicToDataElementBundle<Plaintext>; + + /// Gets the associated trait object reference to a `ToDataElementBundle<Ciphertext>` + /// with the same lifetime as a reference to the implementor. + fn to_ciphertext(&self) -> DynamicToDataElementBundle<Ciphertext>; +} + +/// Blanket impl of [ToMultiFlavorElementBundle] for implementors of [ToDataElementBundle] +/// for both [Plaintext] and [Ciphertext] packet flavors. +impl<T: ToDataElementBundle<Plaintext> + ToDataElementBundle<Ciphertext>> ToMultiFlavorElementBundle + for T +{ + fn to_plaintext(&self) -> DynamicToDataElementBundle<Plaintext> { + let reference: &dyn ToDataElementBundle<Plaintext> = self; + reference.into() + } + fn to_ciphertext(&self) -> DynamicToDataElementBundle<Ciphertext> { + let reference: &dyn ToDataElementBundle<Ciphertext> = self; + reference.into() + } +} + +/// Boxed trait object version of [ToDataElementBundle] which incorporates +/// all possible variants on generatable packet flavoring +/// (`Plaintext`, `Ciphertext`, or both, as a [ToMultiFlavorElementBundle]) +pub enum ToBoxedDataElementBundle { + /// The underlying DE is plaintext-only. + Plaintext(Box<dyn ToDataElementBundle<Plaintext>>), + /// The underlying DE is ciphertext-only. + Ciphertext(Box<dyn ToDataElementBundle<Ciphertext>>), + /// The underlying DE may exist in plaintext or + /// in ciphertext advertisements. + Both(Box<dyn ToMultiFlavorElementBundle>), +} + +impl ToBoxedDataElementBundle { + /// If this [ToBoxedDataElementBundle] can generate plaintext, returns + /// a trait object reference to a `ToDataElementBundle<Plaintext>` + pub fn to_plaintext(&self) -> Option<DynamicToDataElementBundle<Plaintext>> { + match &self { + ToBoxedDataElementBundle::Plaintext(x) => Some(x.as_ref().into()), + ToBoxedDataElementBundle::Ciphertext(_) => None, + ToBoxedDataElementBundle::Both(x) => Some(x.as_ref().to_plaintext()), + } + } + /// If this [ToBoxedDataElementBundle] can generate ciphertext, returns + /// a trait object reference to a `ToDataElementBundle<Ciphertext>` + pub fn to_ciphertext(&self) -> Option<DynamicToDataElementBundle<Ciphertext>> { + match &self { + ToBoxedDataElementBundle::Plaintext(_) => None, + ToBoxedDataElementBundle::Ciphertext(x) => Some(x.as_ref().into()), + ToBoxedDataElementBundle::Both(x) => Some(x.as_ref().to_ciphertext()), + } + } +} + +/// Boxed version of implementors of the Identity trait. +/// A is the underlying Aes algorithm leveraged by ciphertext-based identities. +pub enum BoxedIdentity<C: CryptoProvider> { + /// Public Identity. + Public(PublicIdentity), + /// An encrypted identity, using LDT encryption. + LdtIdentity(LdtIdentity<C>), +} + +impl<C: CryptoProvider> From<PublicIdentity> for BoxedIdentity<C> { + fn from(public_identity: PublicIdentity) -> BoxedIdentity<C> { + BoxedIdentity::Public(public_identity) + } +} + +impl<C: CryptoProvider> From<LdtIdentity<C>> for BoxedIdentity<C> { + fn from(ldt_identity: LdtIdentity<C>) -> BoxedIdentity<C> { + BoxedIdentity::LdtIdentity(ldt_identity) + } +} + +impl From<TxPower> for ToBoxedDataElementBundle { + fn from(data: TxPower) -> Self { + ToBoxedDataElementBundle::Both(Box::new(TxPowerDataElement::from(data))) + } +} + +impl From<BoxedActionBits> for ToBoxedDataElementBundle { + fn from(action_bits: BoxedActionBits) -> Self { + match action_bits { + BoxedActionBits::Plaintext(action_bits) => { + ToBoxedDataElementBundle::Plaintext(Box::new(ActionsDataElement::from(action_bits))) + } + BoxedActionBits::Ciphertext(action_bits) => ToBoxedDataElementBundle::Ciphertext( + Box::new(ActionsDataElement::from(action_bits)), + ), + } + } +} + +/// Boxed version of `ToActionElement` which allows abstracting over +/// what packet flavors are supported by a given action. +pub enum ToBoxedActionElement { + /// A context-sync sequence number. + ContextSyncSeqNum(ContextSyncSeqNum), + /// Action bit for active unlock. + ActiveUnlock(bool), + /// Action bit for nearby share. + NearbyShare(bool), + /// Action bit for instant tethering. + InstantTethering(bool), + /// Action bit for PhoneHub. + PhoneHub(bool), + /// Action bit for Finder. + Finder(bool), + /// Action bit for Fast Pair/SASS + FastPairSass(bool), + /// Action bit for Presence Manager. + PresenceManager(bool), +} + +/// [`ActionBits`] with runtime-determined packet flavoring +pub enum BoxedActionBits { + /// Action-bits for a plaintext advertisement. + Plaintext(ActionBits<Plaintext>), + /// Action-bits for a ciphertext advertisement. + Ciphertext(ActionBits<Ciphertext>), +} + +/// Error which is raised when the flavor of a [`BoxedActionBits`] +/// does not match the supported flavors of a [`ToBoxedActionElement`] +/// upon attempting to add the action to the bit-field. +#[derive(Debug)] +pub struct BoxedSetActionFlavorError; + +impl BoxedActionBits { + /// Constructs the [`BoxedActionBits`] variant with the specified packet + /// flavor variant, and no bits set. + pub fn new(packet_flavor: PacketFlavorEnum) -> Self { + match packet_flavor { + PacketFlavorEnum::Plaintext => BoxedActionBits::Plaintext(ActionBits::default()), + PacketFlavorEnum::Ciphertext => BoxedActionBits::Ciphertext(ActionBits::default()), + } + } + + fn set<F: PacketFlavor, E: ToActionElement<F>>( + action_bits: &mut ActionBits<F>, + to_element: E, + ) -> Result<(), BoxedSetActionFlavorError> { + action_bits.set_action(to_element); + Ok(()) + } + + /// Attempts to set the specified [`ToBoxedActionElement`], yielding + /// a [`BoxedSetActionFlavorError`] if the flavor of this + /// [`BoxedActionBits`] isn't supported by the passed [`ToBoxedActionElement`]. + pub fn set_action( + &mut self, + to_element: ToBoxedActionElement, + ) -> Result<(), BoxedSetActionFlavorError> { + match self { + BoxedActionBits::Plaintext(action_bits) => match to_element { + ToBoxedActionElement::ContextSyncSeqNum(x) => Self::set(action_bits, x), + ToBoxedActionElement::NearbyShare(b) => { + Self::set(action_bits, NearbyShare::from(b)) + } + ToBoxedActionElement::Finder(b) => Self::set(action_bits, Finder::from(b)), + ToBoxedActionElement::FastPairSass(b) => { + Self::set(action_bits, FastPairSass::from(b)) + } + _ => Err(BoxedSetActionFlavorError), + }, + BoxedActionBits::Ciphertext(action_bits) => match to_element { + ToBoxedActionElement::ContextSyncSeqNum(x) => Self::set(action_bits, x), + ToBoxedActionElement::ActiveUnlock(b) => { + Self::set(action_bits, ActiveUnlock::from(b)) + } + ToBoxedActionElement::NearbyShare(b) => { + Self::set(action_bits, NearbyShare::from(b)) + } + ToBoxedActionElement::InstantTethering(b) => { + Self::set(action_bits, InstantTethering::from(b)) + } + ToBoxedActionElement::PhoneHub(b) => Self::set(action_bits, PhoneHub::from(b)), + ToBoxedActionElement::PresenceManager(b) => { + Self::set(action_bits, PresenceManager::from(b)) + } + _ => Err(BoxedSetActionFlavorError), + }, + } + } +}
diff --git a/nearby/presence/np_adv_dynamic/src/lib.rs b/nearby/presence/np_adv_dynamic/src/lib.rs new file mode 100644 index 0000000..4ba8f8b --- /dev/null +++ b/nearby/presence/np_adv_dynamic/src/lib.rs
@@ -0,0 +1,21 @@ +// 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. + +//! A no_std-friendly wrapper around `np_adv` to allow dynamic-style +//! advertisement serialization, with correctness checked at run-time. + +/// Dynamic wrappers around extended adv serialization. +pub mod extended; +/// Dynamic wrappers around legacy adv serialization. +pub mod legacy;
diff --git a/nearby/presence/np_c_ffi/Cargo.lock b/nearby/presence/np_c_ffi/Cargo.lock index 796292b..836d35b 100644 --- a/nearby/presence/np_c_ffi/Cargo.lock +++ b/nearby/presence/np_c_ffi/Cargo.lock
@@ -668,8 +668,10 @@ "crypto_provider_default", "handle_map", "lazy_static", + "ldt_np_adv", "lock_adapter", "np_adv", + "np_hkdf", ] [[package]]
diff --git a/nearby/presence/np_c_ffi/include/c/np_c_ffi.h b/nearby/presence/np_c_ffi/include/c/np_c_ffi.h index 03e5821..81914c9 100644 --- a/nearby/presence/np_c_ffi/include/c/np_c_ffi.h +++ b/nearby/presence/np_c_ffi/include/c/np_c_ffi.h
@@ -33,6 +33,21 @@ #include <stdlib.h> /** + * Result type for trying to add a credential to a credential-slab. + */ +enum np_ffi_AddCredentialToSlabResult { + /** + * We succeeded in adding the credential to the slab. + */ + NP_FFI_ADD_CREDENTIAL_TO_SLAB_RESULT_SUCCESS = 0, + /** + * The handle to the slab was actually invalid. + */ + NP_FFI_ADD_CREDENTIAL_TO_SLAB_RESULT_INVALID_HANDLE = 1, +}; +typedef uint8_t np_ffi_AddCredentialToSlabResult; + +/** * The possible boolean action types which can be present in an Actions data element */ enum np_ffi_BooleanActionType { @@ -51,19 +66,41 @@ */ enum np_ffi_CreateCredentialBookResultKind { /** - * There was no space left to create a new credential book - */ - NP_FFI_CREATE_CREDENTIAL_BOOK_RESULT_KIND_NO_SPACE_LEFT = 0, - /** * We created a new credential book behind the given handle. * The associated payload may be obtained via * `CreateCredentialBookResult#into_success()`. */ - NP_FFI_CREATE_CREDENTIAL_BOOK_RESULT_KIND_SUCCESS = 1, + NP_FFI_CREATE_CREDENTIAL_BOOK_RESULT_KIND_SUCCESS = 0, + /** + * There was no space left to create a new credential book + */ + NP_FFI_CREATE_CREDENTIAL_BOOK_RESULT_KIND_NO_SPACE_LEFT = 1, + /** + * The slab that we tried to create a credential-book from + * actually was an invalid handle. + */ + NP_FFI_CREATE_CREDENTIAL_BOOK_RESULT_KIND_INVALID_SLAB_HANDLE = 2, }; typedef uint8_t np_ffi_CreateCredentialBookResultKind; /** + * Discriminant for `CreateCredentialSlabResult` + */ +enum np_ffi_CreateCredentialSlabResultKind { + /** + * There was no space left to create a new credential slab + */ + NP_FFI_CREATE_CREDENTIAL_SLAB_RESULT_KIND_NO_SPACE_LEFT = 0, + /** + * We created a new credential slab behind the given handle. + * The associated payload may be obtained via + * `CreateCredentialSlabResult#into_success()`. + */ + NP_FFI_CREATE_CREDENTIAL_SLAB_RESULT_KIND_SUCCESS = 1, +}; +typedef uint8_t np_ffi_CreateCredentialSlabResultKind; + +/** * A result-type enum which tells the caller whether/not a deallocation * succeeded or failed due to the requested handle not being present. */ @@ -122,16 +159,8 @@ typedef uint8_t np_ffi_DeserializedV0AdvertisementKind; /** - * Represents deserialized information about the V0 identity utilized - * by a deserialized V0 advertisement - */ -typedef enum { - NP_FFI_DESERIALIZED_V0_IDENTITY_PLAINTEXT, - NP_FFI_DESERIALIZED_V0_IDENTITY_DECRYPTED, -} np_ffi_DeserializedV0Identity; - -/** - * Discriminant for `DeserializedV0Identity`. + * Discriminant for deserialized information about the V0 + * identity utilized by a deserialized V0 advertisement. */ enum np_ffi_DeserializedV0IdentityKind { /** @@ -277,8 +306,9 @@ * Result type for `create_credential_book` */ enum np_ffi_CreateCredentialBookResult_Tag { - NP_FFI_CREATE_CREDENTIAL_BOOK_RESULT_NO_SPACE_LEFT = 0, - NP_FFI_CREATE_CREDENTIAL_BOOK_RESULT_SUCCESS = 1, + NP_FFI_CREATE_CREDENTIAL_BOOK_RESULT_SUCCESS = 0, + NP_FFI_CREATE_CREDENTIAL_BOOK_RESULT_NO_SPACE_LEFT = 1, + NP_FFI_CREATE_CREDENTIAL_BOOK_RESULT_INVALID_SLAB_HANDLE = 2, }; typedef uint8_t np_ffi_CreateCredentialBookResult_Tag; @@ -291,6 +321,79 @@ } np_ffi_CreateCredentialBookResult; /** + *A `#[repr(C)]` handle to a value of type `super::CredentialSlabInternals`. + */ +typedef struct { + uint64_t handle_id; +} np_ffi_CredentialSlab; + +/** + * Result type for `create_credential_slab` + */ +typedef enum { + NP_FFI_CREATE_CREDENTIAL_SLAB_RESULT_NO_SPACE_LEFT, + NP_FFI_CREATE_CREDENTIAL_SLAB_RESULT_SUCCESS, +} np_ffi_CreateCredentialSlabResult_Tag; + +typedef struct { + np_ffi_CreateCredentialSlabResult_Tag tag; + union { + struct { + np_ffi_CredentialSlab success; + }; + }; +} np_ffi_CreateCredentialSlabResult; + +/** + * Cryptographic information about a particular V0 discovery credential + * necessary to match and decrypt encrypted V0 advertisements. + */ +typedef struct { + uint8_t key_seed[32]; + uint8_t legacy_metadata_key_hmac[32]; +} np_ffi_V0DiscoveryCredential; + +/** + * A representation of a MatchedCredential which is passable across the FFI boundary + */ +typedef struct { + uint32_t cred_id; + const uint8_t *encrypted_metadata_bytes_buffer; + uintptr_t encrypted_metadata_bytes_len; +} np_ffi_FfiMatchedCredential; + +/** + * Representation of a V0 credential that contains additional data to provide back to caller once it + * is matched. The credential_id can be used by the caller to correlate it back to the full + * credentials details. + */ +typedef struct { + np_ffi_V0DiscoveryCredential discovery_cred; + np_ffi_FfiMatchedCredential matched_cred; +} np_ffi_V0MatchableCredential; + +/** + * Cryptographic information about a particular V1 discovery credential + * necessary to match and decrypt encrypted V1 advertisement sections. + */ +typedef struct { + uint8_t key_seed[32]; + uint8_t expected_unsigned_metadata_key_hmac[32]; + uint8_t expected_signed_metadata_key_hmac[32]; + uint8_t pub_key[32]; +} np_ffi_V1DiscoveryCredential; + +/** + * Representation of a V1 credential that contains additional data to provide back to caller once it + * is matched. The credential_id can be used by the caller to correlate it back to the full + * credentials details. + */ +typedef struct { + np_ffi_V1DiscoveryCredential discovery_cred; + np_ffi_FfiMatchedCredential matched_cred; +} np_ffi_V1MatchableCredential; + +/** *A `#[repr(C)]` handle to a value of type `super::V0PayloadInternals`. */ typedef struct { @@ -303,7 +406,7 @@ typedef struct { uint8_t num_des; np_ffi_V0Payload payload; - np_ffi_DeserializedV0Identity identity; + np_ffi_DeserializedV0IdentityKind identity_kind; } np_ffi_LegibleDeserializedV0Advertisement; /** @@ -527,6 +630,10 @@ */ typedef struct { /** + * The offset of this generic data-element. + */ + uint8_t offset; + /** * The DE type code of this generic data-element. */ np_ffi_V1DEType de_type; @@ -616,6 +723,22 @@ void np_ffi_global_config_set_num_shards(uint8_t num_shards); /** + * Sets the maximum number of active handles to credential slabs + * which may be active at any one time. + * Default value: Max value. + * Max value: `u32::MAX - 1`. + * + * Useful for bounding the maximum memory used by the client application + * on credential slabs in constrained-memory environments. + * + * Setting this value will have no effect if the handle-maps for the + * API have already begun being used by the client code, and any + * values set will take effect upon the first usage of any API + * call utilizing credential slabs. + */ +void np_ffi_global_config_set_max_num_credential_slabs(uint32_t max_num_credential_slabs); + +/** * Sets the maximum number of active handles to credential books * which may be active at any one time. * Default value: Max value. @@ -666,9 +789,10 @@ void np_ffi_global_config_set_max_num_deserialized_v1_advertisements(uint32_t max_num_deserialized_v1_advertisements); /** - * Allocates a new credential-book, returning a handle to the created object + * Allocates a new credential-book from the given slab, returning a handle + * to the created object. The slab will be deallocated by this call. */ -np_ffi_CreateCredentialBookResult np_ffi_create_credential_book(void); +np_ffi_CreateCredentialBookResult np_ffi_create_credential_book_from_slab(np_ffi_CredentialSlab slab); /** * Gets the tag of a `CreateCredentialBookResult` tagged enum. @@ -682,11 +806,54 @@ np_ffi_CredentialBook np_ffi_CreateCredentialBookResult_into_SUCCESS(np_ffi_CreateCredentialBookResult result); /** + * Deallocates a credential-slab by its handle. + */ +np_ffi_DeallocateResult np_ffi_deallocate_credential_slab(np_ffi_CredentialSlab credential_slab); + +/** * Deallocates a credential-book by its handle */ np_ffi_DeallocateResult np_ffi_deallocate_credential_book(np_ffi_CredentialBook credential_book); /** + * Allocates a new credential-slab, returning a handle to the created object + */ +np_ffi_CreateCredentialSlabResult np_ffi_create_credential_slab(void); + +/** + * Gets the tag of a `CreateCredentialSlabResult` tagged enum. + */ +np_ffi_CreateCredentialSlabResultKind np_ffi_CreateCredentialSlabResult_kind(np_ffi_CreateCredentialSlabResult result); + +/** + * Casts a `CreateCredentialSlabResult` to the `SUCCESS` variant, panicking in the + * case where the passed value is of a different enum variant. + */ +np_ffi_CredentialSlab np_ffi_CreateCredentialSlabResult_into_SUCCESS(np_ffi_CreateCredentialSlabResult result); + +/** + * Adds the given V0 discovery credential with some associated + * match-data to this credential slab. + * + * Safety: this is safe if the provided pointer points to a valid memory address + * which contains the correct len amount of bytes. The copy from the memory address isn't atomic, + * so concurrent modification of the array from another thread would cause undefined behavior. + */ +np_ffi_AddCredentialToSlabResult np_ffi_CredentialSlab_add_v0_credential(np_ffi_CredentialSlab credential_slab, + np_ffi_V0MatchableCredential v0_cred); + +/** + * Adds the given V1 discovery credential with some associated + * match-data to this credential slab. + * + * Safety: this is safe if the provided pointer points to a valid memory address + * which contains the correct len amount of bytes. The copy from the memory address isn't atomic, + * so concurrent modification of the array from another thread would cause undefined behavior. + */ +np_ffi_AddCredentialToSlabResult np_ffi_CredentialSlab_add_v1_credential(np_ffi_CredentialSlab credential_slab, + np_ffi_V1MatchableCredential v1_cred); + +/** * Attempts to deserialize an advertisement with the given service-data * payload (presumed to be under the NP service UUID) using credentials * pulled from the given credential-book. @@ -751,9 +918,9 @@ np_ffi_V0Payload np_ffi_LegibleDeserializedV0Advertisement_into_payload(np_ffi_LegibleDeserializedV0Advertisement adv); /** - * Gets just the identity information associated with a `LegibleDeserializedV0Advertisement`. + * Gets just the identity kind associated with a `LegibleDeserializedV0Advertisement`. */ -np_ffi_DeserializedV0Identity np_ffi_LegibleDeserializedV0Advertisement_into_identity(np_ffi_LegibleDeserializedV0Advertisement adv); +np_ffi_DeserializedV0IdentityKind np_ffi_LegibleDeserializedV0Advertisement_get_identity_kind(np_ffi_LegibleDeserializedV0Advertisement adv); /** * Deallocates any internal data of a `LegibleDeserializedV0Advertisement` @@ -761,11 +928,6 @@ np_ffi_DeallocateResult np_ffi_deallocate_legible_v0_advertisement(np_ffi_LegibleDeserializedV0Advertisement adv); /** - * Gets the tag of the `DeserializedV0Identity` tagged-union. - */ -np_ffi_DeserializedV0IdentityKind np_ffi_DeserializedV0Identity_kind(np_ffi_DeserializedV0Identity identity); - -/** * Attempts to get the data-element with the given index in the passed v0 adv payload */ np_ffi_GetV0DEResult np_ffi_V0Payload_get_de(np_ffi_V0Payload payload, uint8_t index);
diff --git a/nearby/presence/np_c_ffi/include/cpp/np_cpp_ffi_functions.h b/nearby/presence/np_c_ffi/include/cpp/np_cpp_ffi_functions.h index 3361c5d..ad3ec24 100644 --- a/nearby/presence/np_c_ffi/include/cpp/np_cpp_ffi_functions.h +++ b/nearby/presence/np_c_ffi/include/cpp/np_cpp_ffi_functions.h
@@ -75,6 +75,20 @@ /// API call. void np_ffi_global_config_set_num_shards(uint8_t num_shards); +/// Sets the maximum number of active handles to credential slabs +/// which may be active at any one time. +/// Default value: Max value. +/// Max value: `u32::MAX - 1`. +/// +/// Useful for bounding the maximum memory used by the client application +/// on credential slabs in constrained-memory environments. +/// +/// Setting this value will have no effect if the handle-maps for the +/// API have already begun being used by the client code, and any +/// values set will take effect upon the first usage of any API +/// call utilizing credential slabs. +void np_ffi_global_config_set_max_num_credential_slabs(uint32_t max_num_credential_slabs); + /// Sets the maximum number of active handles to credential books /// which may be active at any one time. /// Default value: Max value. @@ -119,8 +133,9 @@ /// call which references or returns a deserialized V1 advertisement. void np_ffi_global_config_set_max_num_deserialized_v1_advertisements(uint32_t max_num_deserialized_v1_advertisements); -/// Allocates a new credential-book, returning a handle to the created object -CreateCredentialBookResult np_ffi_create_credential_book(); +/// Allocates a new credential-book from the given slab, returning a handle +/// to the created object. The slab will be deallocated by this call. +CreateCredentialBookResult np_ffi_create_credential_book_from_slab(CredentialSlab slab); /// Gets the tag of a `CreateCredentialBookResult` tagged enum. CreateCredentialBookResultKind np_ffi_CreateCredentialBookResult_kind(CreateCredentialBookResult result); @@ -129,9 +144,40 @@ /// case where the passed value is of a different enum variant. CredentialBook np_ffi_CreateCredentialBookResult_into_SUCCESS(CreateCredentialBookResult result); +/// Deallocates a credential-slab by its handle. +DeallocateResult np_ffi_deallocate_credential_slab(CredentialSlab credential_slab); + /// Deallocates a credential-book by its handle DeallocateResult np_ffi_deallocate_credential_book(CredentialBook credential_book); +/// Allocates a new credential-slab, returning a handle to the created object +CreateCredentialSlabResult np_ffi_create_credential_slab(); + +/// Gets the tag of a `CreateCredentialSlabResult` tagged enum. +CreateCredentialSlabResultKind np_ffi_CreateCredentialSlabResult_kind(CreateCredentialSlabResult result); + +/// Casts a `CreateCredentialSlabResult` to the `SUCCESS` variant, panicking in the +/// case where the passed value is of a different enum variant. +CredentialSlab np_ffi_CreateCredentialSlabResult_into_SUCCESS(CreateCredentialSlabResult result); + +/// Adds the given V0 discovery credential with some associated +/// match-data to this credential slab. +/// +/// Safety: this is safe if the provided pointer points to a valid memory address +/// which contains the correct len amount of bytes. The copy from the memory address isn't atomic, +/// so concurrent modification of the array from another thread would cause undefined behavior. +AddCredentialToSlabResult np_ffi_CredentialSlab_add_v0_credential(CredentialSlab credential_slab, + V0MatchableCredential v0_cred); + +/// Adds the given V1 discovery credential with some associated +/// match-data to this credential slab. +/// +/// Safety: this is safe if the provided pointer points to a valid memory address +/// which contains the correct len amount of bytes. The copy from the memory address isn't atomic, +/// so concurrent modification of the array from another thread would cause undefined behavior. +AddCredentialToSlabResult np_ffi_CredentialSlab_add_v1_credential(CredentialSlab credential_slab, + V1MatchableCredential v1_cred); + /// Attempts to deserialize an advertisement with the given service-data /// payload (presumed to be under the NP service UUID) using credentials /// pulled from the given credential-book. @@ -174,15 +220,12 @@ /// Gets just the data-element payload of a `LegibleDeserializedV0Advertisement`. V0Payload np_ffi_LegibleDeserializedV0Advertisement_into_payload(LegibleDeserializedV0Advertisement adv); -/// Gets just the identity information associated with a `LegibleDeserializedV0Advertisement`. -DeserializedV0Identity np_ffi_LegibleDeserializedV0Advertisement_into_identity(LegibleDeserializedV0Advertisement adv); +/// Gets just the identity kind associated with a `LegibleDeserializedV0Advertisement`. +DeserializedV0IdentityKind np_ffi_LegibleDeserializedV0Advertisement_get_identity_kind(LegibleDeserializedV0Advertisement adv); /// Deallocates any internal data of a `LegibleDeserializedV0Advertisement` DeallocateResult np_ffi_deallocate_legible_v0_advertisement(LegibleDeserializedV0Advertisement adv); -/// Gets the tag of the `DeserializedV0Identity` tagged-union. -DeserializedV0IdentityKind np_ffi_DeserializedV0Identity_kind(DeserializedV0Identity identity); - /// Attempts to get the data-element with the given index in the passed v0 adv payload GetV0DEResult np_ffi_V0Payload_get_de(V0Payload payload, uint8_t index);
diff --git a/nearby/presence/np_c_ffi/include/cpp/np_cpp_ffi_types.h b/nearby/presence/np_c_ffi/include/cpp/np_cpp_ffi_types.h index 4d66308..d39bb70 100644 --- a/nearby/presence/np_c_ffi/include/cpp/np_cpp_ffi_types.h +++ b/nearby/presence/np_c_ffi/include/cpp/np_cpp_ffi_types.h
@@ -36,6 +36,14 @@ namespace np_ffi { namespace internal { +/// Result type for trying to add a credential to a credential-slab. +enum class AddCredentialToSlabResult : uint8_t { + /// We succeeded in adding the credential to the slab. + Success = 0, + /// The handle to the slab was actually invalid. + InvalidHandle = 1, +}; + /// The possible boolean action types which can be present in an Actions data element enum class BooleanActionType : uint8_t { ActiveUnlock = 8, @@ -49,11 +57,24 @@ /// Discriminant for `CreateCredentialBookResult` enum class CreateCredentialBookResultKind : uint8_t { - /// There was no space left to create a new credential book - NoSpaceLeft = 0, /// We created a new credential book behind the given handle. /// The associated payload may be obtained via /// `CreateCredentialBookResult#into_success()`. + Success = 0, + /// There was no space left to create a new credential book + NoSpaceLeft = 1, + /// The slab that we tried to create a credential-book from + /// actually was an invalid handle. + InvalidSlabHandle = 2, +}; + +/// Discriminant for `CreateCredentialSlabResult` +enum class CreateCredentialSlabResultKind : uint8_t { + /// There was no space left to create a new credential slab + NoSpaceLeft = 0, + /// We created a new credential slab behind the given handle. + /// The associated payload may be obtained via + /// `CreateCredentialSlabResult#into_success()`. Success = 1, }; @@ -93,14 +114,8 @@ NoMatchingCredentials = 1, }; -/// Represents deserialized information about the V0 identity utilized -/// by a deserialized V0 advertisement -enum class DeserializedV0Identity { - Plaintext, - Decrypted, -}; - -/// Discriminant for `DeserializedV0Identity`. +/// Discriminant for deserialized information about the V0 +/// identity utilized by a deserialized V0 advertisement. enum class DeserializedV0IdentityKind : uint8_t { /// The deserialized identity was a plaintext identity. Plaintext = 0, @@ -193,8 +208,9 @@ /// Result type for `create_credential_book` union CreateCredentialBookResult { enum class Tag : uint8_t { - NoSpaceLeft = 0, - Success = 1, + Success = 0, + NoSpaceLeft = 1, + InvalidSlabHandle = 2, }; struct Success_Body { @@ -208,6 +224,67 @@ Success_Body success; }; +///A `#[repr(C)]` handle to a value of type `super::CredentialSlabInternals`. +struct CredentialSlab { + uint64_t handle_id; +}; + +/// Result type for `create_credential_slab` +struct CreateCredentialSlabResult { + enum class Tag { + NoSpaceLeft, + Success, + }; + + struct Success_Body { + CredentialSlab _0; + }; + + Tag tag; + union { + Success_Body success; + }; +}; + +/// Cryptographic information about a particular V0 discovery credential +/// necessary to match and decrypt encrypted V0 advertisements. +struct V0DiscoveryCredential { + uint8_t key_seed[32]; + uint8_t legacy_metadata_key_hmac[32]; +}; + +/// A representation of a MatchedCredential which is passable across the FFI boundary +struct FfiMatchedCredential { + uint32_t cred_id; + const uint8_t *encrypted_metadata_bytes_buffer; + uintptr_t encrypted_metadata_bytes_len; +}; + +/// Representation of a V0 credential that contains additional data to provide back to caller once it +/// is matched. The credential_id can be used by the caller to correlate it back to the full +/// credentials details. +struct V0MatchableCredential { + V0DiscoveryCredential discovery_cred; + FfiMatchedCredential matched_cred; +}; + +/// Cryptographic information about a particular V1 discovery credential +/// necessary to match and decrypt encrypted V1 advertisement sections. +struct V1DiscoveryCredential { + uint8_t key_seed[32]; + uint8_t expected_unsigned_metadata_key_hmac[32]; + uint8_t expected_signed_metadata_key_hmac[32]; + uint8_t pub_key[32]; +}; + +/// Representation of a V1 credential that contains additional data to provide back to caller once it +/// is matched. The credential_id can be used by the caller to correlate it back to the full +/// credentials details. +struct V1MatchableCredential { + V1DiscoveryCredential discovery_cred; + FfiMatchedCredential matched_cred; +}; + ///A `#[repr(C)]` handle to a value of type `super::V0PayloadInternals`. struct V0Payload { uint64_t handle_id; @@ -217,7 +294,7 @@ struct LegibleDeserializedV0Advertisement { uint8_t num_des; V0Payload payload; - DeserializedV0Identity identity; + DeserializedV0IdentityKind identity_kind; }; /// Represents a deserialized V0 advertisement @@ -409,6 +486,8 @@ /// This representation is stable, and so you may directly /// reference this struct's fields if you wish. struct GenericV1DataElement { + /// The offset of this generic data-element. + uint8_t offset; /// The DE type code of this generic data-element. V1DEType de_type; /// The raw data-element byte payload, up to
diff --git a/nearby/presence/np_c_ffi/src/credentials.rs b/nearby/presence/np_c_ffi/src/credentials.rs index 6b60545..70949a6 100644 --- a/nearby/presence/np_c_ffi/src/credentials.rs +++ b/nearby/presence/np_c_ffi/src/credentials.rs
@@ -14,15 +14,20 @@ //! Credential-related data-types and functions use crate::{unwrap, PanicReason}; +use core::slice; use np_ffi_core::common::*; use np_ffi_core::credentials::credential_book::CredentialBook; +use np_ffi_core::credentials::credential_slab::CredentialSlab; use np_ffi_core::credentials::*; use np_ffi_core::utils::FfiEnum; -/// Allocates a new credential-book, returning a handle to the created object +/// Allocates a new credential-book from the given slab, returning a handle +/// to the created object. The slab will be deallocated by this call. #[no_mangle] -pub extern "C" fn np_ffi_create_credential_book() -> CreateCredentialBookResult { - create_credential_book() +pub extern "C" fn np_ffi_create_credential_book_from_slab( + slab: CredentialSlab, +) -> CreateCredentialBookResult { + create_credential_book_from_slab(slab) } /// Gets the tag of a `CreateCredentialBookResult` tagged enum. @@ -42,6 +47,14 @@ unwrap(result.into_success(), PanicReason::EnumCastFailed) } +/// Deallocates a credential-slab by its handle. +#[no_mangle] +pub extern "C" fn np_ffi_deallocate_credential_slab( + credential_slab: CredentialSlab, +) -> DeallocateResult { + deallocate_credential_slab(credential_slab) +} + /// Deallocates a credential-book by its handle #[no_mangle] pub extern "C" fn np_ffi_deallocate_credential_book( @@ -49,3 +62,98 @@ ) -> DeallocateResult { deallocate_credential_book(credential_book) } + +/// Allocates a new credential-slab, returning a handle to the created object +#[no_mangle] +pub extern "C" fn np_ffi_create_credential_slab() -> CreateCredentialSlabResult { + create_credential_slab() +} + +/// Gets the tag of a `CreateCredentialSlabResult` tagged enum. +#[no_mangle] +pub extern "C" fn np_ffi_CreateCredentialSlabResult_kind( + result: CreateCredentialSlabResult, +) -> CreateCredentialSlabResultKind { + result.kind() +} + +/// Casts a `CreateCredentialSlabResult` to the `SUCCESS` variant, panicking in the +/// case where the passed value is of a different enum variant. +#[no_mangle] +pub extern "C" fn np_ffi_CreateCredentialSlabResult_into_SUCCESS( + result: CreateCredentialSlabResult, +) -> CredentialSlab { + unwrap(result.into_success(), PanicReason::EnumCastFailed) +} + +/// Representation of a V0 credential that contains additional data to provide back to caller once it +/// is matched. The credential_id can be used by the caller to correlate it back to the full +/// credentials details. +#[repr(C)] +pub struct V0MatchableCredential { + discovery_cred: V0DiscoveryCredential, + matched_cred: FfiMatchedCredential, +} + +/// Representation of a V1 credential that contains additional data to provide back to caller once it +/// is matched. The credential_id can be used by the caller to correlate it back to the full +/// credentials details. +#[repr(C)] +pub struct V1MatchableCredential { + discovery_cred: V1DiscoveryCredential, + matched_cred: FfiMatchedCredential, +} + +/// A representation of a MatchedCredential which is passable across the FFI boundary +#[repr(C)] +pub struct FfiMatchedCredential { + cred_id: u32, + encrypted_metadata_bytes_buffer: *const u8, + encrypted_metadata_bytes_len: usize, +} + +/// Adds the given V0 discovery credential with some associated +/// match-data to this credential slab. +/// +/// Safety: this is safe if the provided pointer points to a valid memory address +/// which contains the correct len amount of bytes. The copy from the memory address isn't atomic, +/// so concurrent modification of the array from another thread would cause undefined behavior. +#[no_mangle] +pub extern "C" fn np_ffi_CredentialSlab_add_v0_credential( + credential_slab: CredentialSlab, + v0_cred: V0MatchableCredential, +) -> AddCredentialToSlabResult { + #[allow(unsafe_code)] + let metadata_slice = unsafe { + slice::from_raw_parts( + v0_cred.matched_cred.encrypted_metadata_bytes_buffer, + v0_cred.matched_cred.encrypted_metadata_bytes_len, + ) + }; + + let matched_credential = MatchedCredential::new(v0_cred.matched_cred.cred_id, metadata_slice); + credential_slab.add_v0(v0_cred.discovery_cred, matched_credential) +} + +/// Adds the given V1 discovery credential with some associated +/// match-data to this credential slab. +/// +/// Safety: this is safe if the provided pointer points to a valid memory address +/// which contains the correct len amount of bytes. The copy from the memory address isn't atomic, +/// so concurrent modification of the array from another thread would cause undefined behavior. +#[no_mangle] +pub extern "C" fn np_ffi_CredentialSlab_add_v1_credential( + credential_slab: CredentialSlab, + v1_cred: V1MatchableCredential, +) -> AddCredentialToSlabResult { + #[allow(unsafe_code)] + let metadata_slice = unsafe { + slice::from_raw_parts( + v1_cred.matched_cred.encrypted_metadata_bytes_buffer, + v1_cred.matched_cred.encrypted_metadata_bytes_len, + ) + }; + + let matched_credential = MatchedCredential::new(v1_cred.matched_cred.cred_id, metadata_slice); + credential_slab.add_v1(v1_cred.discovery_cred, matched_credential) +}
diff --git a/nearby/presence/np_c_ffi/src/deserialize/v0.rs b/nearby/presence/np_c_ffi/src/deserialize/v0.rs index 24c4c2a..28bec8a 100644 --- a/nearby/presence/np_c_ffi/src/deserialize/v0.rs +++ b/nearby/presence/np_c_ffi/src/deserialize/v0.rs
@@ -52,12 +52,12 @@ adv.payload() } -/// Gets just the identity information associated with a `LegibleDeserializedV0Advertisement`. +/// Gets just the identity kind associated with a `LegibleDeserializedV0Advertisement`. #[no_mangle] -pub extern "C" fn np_ffi_LegibleDeserializedV0Advertisement_into_identity( +pub extern "C" fn np_ffi_LegibleDeserializedV0Advertisement_get_identity_kind( adv: LegibleDeserializedV0Advertisement, -) -> DeserializedV0Identity { - adv.identity() +) -> DeserializedV0IdentityKind { + adv.identity_kind() } /// Deallocates any internal data of a `LegibleDeserializedV0Advertisement` @@ -68,14 +68,6 @@ adv.deallocate() } -/// Gets the tag of the `DeserializedV0Identity` tagged-union. -#[no_mangle] -pub extern "C" fn np_ffi_DeserializedV0Identity_kind( - identity: DeserializedV0Identity, -) -> DeserializedV0IdentityKind { - identity.kind() -} - /// Attempts to get the data-element with the given index in the passed v0 adv payload #[no_mangle] pub extern "C" fn np_ffi_V0Payload_get_de(payload: V0Payload, index: u8) -> GetV0DEResult {
diff --git a/nearby/presence/np_c_ffi/src/lib.rs b/nearby/presence/np_c_ffi/src/lib.rs index 0dd6d33..84e9859 100644 --- a/nearby/presence/np_c_ffi/src/lib.rs +++ b/nearby/presence/np_c_ffi/src/lib.rs
@@ -147,6 +147,23 @@ np_ffi_core::common::global_config_set_num_shards(num_shards) } +/// Sets the maximum number of active handles to credential slabs +/// which may be active at any one time. +/// Default value: Max value. +/// Max value: `u32::MAX - 1`. +/// +/// Useful for bounding the maximum memory used by the client application +/// on credential slabs in constrained-memory environments. +/// +/// Setting this value will have no effect if the handle-maps for the +/// API have already begun being used by the client code, and any +/// values set will take effect upon the first usage of any API +/// call utilizing credential slabs. +#[no_mangle] +pub extern "C" fn np_ffi_global_config_set_max_num_credential_slabs(max_num_credential_slabs: u32) { + np_ffi_core::common::global_config_set_max_num_credential_slabs(max_num_credential_slabs) +} + /// Sets the maximum number of active handles to credential books /// which may be active at any one time. /// Default value: Max value.
diff --git a/nearby/presence/np_cpp_ffi/benchmarks/np_ffi_bench.cc b/nearby/presence/np_cpp_ffi/benchmarks/np_ffi_bench.cc index 72a3f91..164bab3 100644 --- a/nearby/presence/np_cpp_ffi/benchmarks/np_ffi_bench.cc +++ b/nearby/presence/np_cpp_ffi/benchmarks/np_ffi_bench.cc
@@ -40,7 +40,9 @@ BENCHMARK_DEFINE_F(NpCppBenchmark, V0PlaintextAdvertisement) (benchmark::State &state) { - auto cred_book = nearby_protocol::CredentialBook::TryCreate(); + auto cred_slab = nearby_protocol::CredentialSlab::TryCreate(); + assert(cred_slab.ok()); + auto cred_book = nearby_protocol::CredentialBook::TryCreateFromSlab(cred_slab.value()); assert(cred_book.ok()); auto num_ciphers = state.range(0); @@ -68,7 +70,14 @@ BENCHMARK_DEFINE_F(NpCBenchmark, V0PlaintextAdvertisement) (benchmark::State &state) { auto num_ciphers = state.range(0); - auto book_result = np_ffi::internal::np_ffi_create_credential_book(); + auto slab_result = np_ffi::internal::np_ffi_create_credential_slab(); + assert( + np_ffi::internal::np_ffi_CreateCredentialSlabResult_kind(slab_result) == + np_ffi::internal::CreateCredentialSlabResultKind::Success); + auto slab = np_ffi::internal::np_ffi_CreateCredentialSlabResult_into_SUCCESS( + slab_result); + + auto book_result = np_ffi::internal::np_ffi_create_credential_book_from_slab(slab); assert( np_ffi::internal::np_ffi_CreateCredentialBookResult_kind(book_result) == np_ffi::internal::CreateCredentialBookResultKind::Success); @@ -99,4 +108,4 @@ ->Range(1, 1000) ->Unit(benchmark::kMicrosecond); -BENCHMARK_MAIN(); \ No newline at end of file +BENCHMARK_MAIN();
diff --git a/nearby/presence/np_cpp_ffi/fuzz/fuzzer_np_cpp_deserialize.cc b/nearby/presence/np_cpp_ffi/fuzz/fuzzer_np_cpp_deserialize.cc index 84d7247..3c409e7 100644 --- a/nearby/presence/np_cpp_ffi/fuzz/fuzzer_np_cpp_deserialize.cc +++ b/nearby/presence/np_cpp_ffi/fuzz/fuzzer_np_cpp_deserialize.cc
@@ -34,7 +34,13 @@ nearby_protocol::RawAdvertisementPayload payload( (nearby_protocol::ByteBuffer<255>(raw_bytes))); - auto credential_book = nearby_protocol::CredentialBook::TryCreate(); + auto credential_slab = nearby_protocol::CredentialSlab::TryCreate(); + if (!credential_slab.ok()) { + printf("Error: create Credential slab failed\n"); + __builtin_trap(); + } + + auto credential_book = nearby_protocol::CredentialBook::TryCreateFromSlab(credential_slab.value()); if (!credential_book.ok()) { printf("Error: create Credential book failed\n"); __builtin_trap(); @@ -45,4 +51,4 @@ payload, credential_book.value()); return 0; -} \ No newline at end of file +}
diff --git a/nearby/presence/np_cpp_ffi/fuzz/fuzzer_np_cpp_valid_header.cc b/nearby/presence/np_cpp_ffi/fuzz/fuzzer_np_cpp_valid_header.cc index c08b6cd..2475b5e 100644 --- a/nearby/presence/np_cpp_ffi/fuzz/fuzzer_np_cpp_valid_header.cc +++ b/nearby/presence/np_cpp_ffi/fuzz/fuzzer_np_cpp_valid_header.cc
@@ -32,7 +32,13 @@ nearby_protocol::RawAdvertisementPayload payload( (nearby_protocol::ByteBuffer<255>(raw_bytes))); - auto credential_book = nearby_protocol::CredentialBook::TryCreate(); + auto credential_slab = nearby_protocol::CredentialSlab::TryCreate(); + if (!credential_slab.ok()) { + printf("Error: create Credential slab failed\n"); + __builtin_trap(); + } + + auto credential_book = nearby_protocol::CredentialBook::TryCreateFromSlab(credential_slab.value()); if (!credential_book.ok()) { printf("Error: create Credential book failed\n"); __builtin_trap(); @@ -51,4 +57,4 @@ payload, credential_book.value()); return 0; -} \ No newline at end of file +}
diff --git a/nearby/presence/np_cpp_ffi/include/nearby_protocol.h b/nearby/presence/np_cpp_ffi/include/nearby_protocol.h index e7e46e7..1e16eef 100644 --- a/nearby/presence/np_cpp_ffi/include/nearby_protocol.h +++ b/nearby/presence/np_cpp_ffi/include/nearby_protocol.h
@@ -51,6 +51,7 @@ // Re-exporting cbindgen generated types which are used in the public API using np_ffi::internal::BooleanActionType; +using np_ffi::internal::CreateCredentialSlabResultKind; using np_ffi::internal::CreateCredentialBookResultKind; using np_ffi::internal::DeserializeAdvertisementResultKind; using np_ffi::internal::DeserializedV0AdvertisementKind; @@ -71,7 +72,6 @@ // V0 Classes class DeserializedV0Advertisement; -class DeserializedV0Identity; class LegibleDeserializedV0Advertisement; class V0DataElement; class V0Payload; @@ -109,6 +109,12 @@ // np_ffi_global_config_set_num_shards in np_cpp_ffi_functions.h for more info static void SetNumShards(uint8_t num_shards); + // Sets the maximum number of active handles to credential slabs which may be + // active at any one time. See + // np_ffi_global_config_set_max_num_credential_slabs in np_cpp_ffi_functions.h + // for more info + static void SetMaxNumCredentialSlabs(uint32_t max_num_credential_slabs); + // Sets the maximum number of active handles to credential books which may be // active at any one time. See // np_ffi_global_config_set_max_num_credential_books in np_cpp_ffi_functions.h @@ -128,6 +134,37 @@ uint32_t max_num_deserialized_v1_advertisements); }; +// Holds the credentials used in the construction of a credential book +// using CredentialBook::TryCreateFromSlab() +class CredentialSlab { +public: + // Don't allow copy constructor or copy assignment, since that would result in + // the underlying handle being freed multiple times + CredentialSlab(const CredentialSlab &other) = delete; + CredentialSlab &operator=(const CredentialSlab &other) = delete; + + // Move constructor and move assignment are needed in order to wrap this class + // in absl::StatusOr + CredentialSlab(CredentialSlab &&other) noexcept; + CredentialSlab &operator=(CredentialSlab &&other) noexcept; + + // The destructor for a CredentialSlab, this will be called when a + // CredentialSlab instance goes out of scope and will free the underlying + // resources + ~CredentialSlab(); + + // Creates a new instance of a CredentialSlab, returns the CredentialSlab on + // success or a Status code on failure + [[nodiscard]] static absl::StatusOr<CredentialSlab> TryCreate(); +private: + friend class CredentialBook; + explicit CredentialSlab(np_ffi::internal::CredentialSlab credential_slab) + : credential_slab_(credential_slab), moved_(false) {} + + np_ffi::internal::CredentialSlab credential_slab_; + bool moved_; +}; + // Holds the credentials used when decrypting data of an advertisement. // This needs to be passed to Deserializer::DeserializeAdvertisement() when // attempting to deserialize a payload @@ -148,9 +185,11 @@ // resources ~CredentialBook(); - // Creates a new instance of a CredentialBook, returns the CredentialBook on - // success or a Status code on failure - [[nodiscard]] static absl::StatusOr<CredentialBook> TryCreate(); + // Creates a new instance of a CredentialBook from a CredentialSlab, + // returning the CredentialBook on success or a Status code on failure. + // The passed credential-slab will be deallocated if this operation + // is successful. + [[nodiscard]] static absl::StatusOr<CredentialBook> TryCreateFromSlab(CredentialSlab &slab); private: friend class Deserializer; @@ -339,8 +378,9 @@ // and will free the underlying parent handle. ~LegibleDeserializedV0Advertisement(); - // Returns just the identity information associated with the advertisement - [[nodiscard]] DeserializedV0Identity GetIdentity(); + // Returns just the kind of identity (public/encrypted) + // associated with the advertisement + [[nodiscard]] DeserializedV0IdentityKind GetIdentityKind(); // Returns the number of data elements in the advertisement [[nodiscard]] uint8_t GetNumberOfDataElements(); // Returns just the data-element payload of the advertisement @@ -358,20 +398,6 @@ bool moved_; }; -// A V0 identity of an advertisement -class DeserializedV0Identity { -public: - // Returns the DeserializedV0IdentityKind of the advertisement - [[nodiscard]] DeserializedV0IdentityKind GetKind(); - -private: - friend class LegibleDeserializedV0Advertisement; - explicit DeserializedV0Identity( - np_ffi::internal::DeserializedV0Identity v0_identity) - : v0_identity_(v0_identity) {} - np_ffi::internal::DeserializedV0Identity v0_identity_; -}; - // A data element payload of a Deserialized V0 Advertisement. class V0Payload { public:
diff --git a/nearby/presence/np_cpp_ffi/nearby_protocol.cc b/nearby/presence/np_cpp_ffi/nearby_protocol.cc index 3ee5c63..106eb77 100644 --- a/nearby/presence/np_cpp_ffi/nearby_protocol.cc +++ b/nearby/presence/np_cpp_ffi/nearby_protocol.cc
@@ -67,6 +67,11 @@ np_ffi::internal::np_ffi_global_config_set_num_shards(num_shards); } +void GlobalConfig::SetMaxNumCredentialSlabs(uint32_t max_num_credential_slabs) { + np_ffi::internal::np_ffi_global_config_set_max_num_credential_slabs( + max_num_credential_slabs); +} + void GlobalConfig::SetMaxNumCredentialBooks(uint32_t max_num_credential_books) { np_ffi::internal::np_ffi_global_config_set_max_num_credential_books( max_num_credential_books); @@ -86,21 +91,73 @@ max_num_deserialized_v1_advertisements); } -absl::StatusOr<CredentialBook> CredentialBook::TryCreate() { - auto result = np_ffi::internal::np_ffi_create_credential_book(); - auto kind = np_ffi::internal::np_ffi_CreateCredentialBookResult_kind(result); +absl::StatusOr<CredentialSlab> CredentialSlab::TryCreate() { + auto result = np_ffi::internal::np_ffi_create_credential_slab(); + auto kind = np_ffi::internal::np_ffi_CreateCredentialSlabResult_kind(result); switch (kind) { - case CreateCredentialBookResultKind::Success: { - auto book = CredentialBook( - np_ffi::internal::np_ffi_CreateCredentialBookResult_into_SUCCESS( + case CreateCredentialSlabResultKind::Success: { + auto slab = CredentialSlab( + np_ffi::internal::np_ffi_CreateCredentialSlabResult_into_SUCCESS( result)); - return book; + return slab; + } + case CreateCredentialSlabResultKind::NoSpaceLeft: { + return absl::ResourceExhaustedError( + "No space left to create credential slab"); + } + } +} + +CredentialSlab::~CredentialSlab() { + if (!this->moved_) { + auto result = + np_ffi::internal::np_ffi_deallocate_credential_slab(credential_slab_); + assert_panic(result == np_ffi::internal::DeallocateResult::Success); + } +} + +CredentialSlab::CredentialSlab(CredentialSlab &&other) noexcept + : credential_slab_(other.credential_slab_), moved_(other.moved_) { + other.credential_slab_ = {}; + other.moved_ = true; +} + +CredentialSlab &CredentialSlab::operator=(CredentialSlab &&other) noexcept { + if (this != &other) { + if (!this->moved_) { + auto result = np_ffi::internal::np_ffi_deallocate_credential_slab( + this->credential_slab_); + assert_panic(result == np_ffi::internal::DeallocateResult::Success); + } + + this->credential_slab_ = other.credential_slab_; + this->moved_ = other.moved_; + + other.credential_slab_ = {}; + other.moved_ = true; + } + return *this; +} + +absl::StatusOr<CredentialBook> CredentialBook::TryCreateFromSlab(CredentialSlab &slab) { + assert_panic(!slab.moved_); + auto result = np_ffi::internal::np_ffi_create_credential_book_from_slab(slab.credential_slab_); + auto kind = np_ffi::internal::np_ffi_CreateCredentialBookResult_kind(result); + switch (kind) { + case CreateCredentialBookResultKind::Success: { + auto book = np_ffi::internal::np_ffi_CreateCredentialBookResult_into_SUCCESS(result); + slab.moved_ = true; + return CredentialBook(book); } case CreateCredentialBookResultKind::NoSpaceLeft: { return absl::ResourceExhaustedError( "No space left to create credential book"); } + case CreateCredentialBookResultKind::InvalidSlabHandle: { + return absl::NotFoundError( + "The slab referenced by the given handle was not found."); + } } } @@ -286,12 +343,12 @@ } } -DeserializedV0Identity LegibleDeserializedV0Advertisement::GetIdentity() { +DeserializedV0IdentityKind LegibleDeserializedV0Advertisement::GetIdentityKind() { assert_panic(!this->moved_); auto result = - np_ffi::internal::np_ffi_LegibleDeserializedV0Advertisement_into_identity( + np_ffi::internal::np_ffi_LegibleDeserializedV0Advertisement_get_identity_kind( legible_v0_advertisement_); - return DeserializedV0Identity(result); + return result; } uint8_t LegibleDeserializedV0Advertisement::GetNumberOfDataElements() { @@ -309,10 +366,6 @@ return V0Payload(result); } -np_ffi::internal::DeserializedV0IdentityKind DeserializedV0Identity::GetKind() { - return np_ffi::internal::np_ffi_DeserializedV0Identity_kind(v0_identity_); -} - V0Payload::V0Payload(V0Payload &&other) noexcept : v0_payload_(other.v0_payload_), moved_(other.moved_) { other.v0_payload_ = {};
diff --git a/nearby/presence/np_cpp_ffi/sample/main.cc b/nearby/presence/np_cpp_ffi/sample/main.cc index a18b996..a02a523 100644 --- a/nearby/presence/np_cpp_ffi/sample/main.cc +++ b/nearby/presence/np_cpp_ffi/sample/main.cc
@@ -25,7 +25,7 @@ void HandleV0Adv(nearby_protocol::DeserializedV0Advertisement); void HandleLegibleV0Adv(nearby_protocol::LegibleDeserializedV0Advertisement); -void HandleV0Identity(nearby_protocol::DeserializedV0Identity); +void HandleV0IdentityKind(nearby_protocol::DeserializedV0IdentityKind); void HandleDataElement(nearby_protocol::V0DataElement); void HandleV1Adv(nearby_protocol::DeserializedV1Advertisement); @@ -46,8 +46,15 @@ std::cout << "\n========= Example V0 Adv ==========\n"; std::cout << "Hex bytes: 00031503260046\n\n"; - // Create an empty credential book and verify that is is successful - auto cred_book_result = nearby_protocol::CredentialBook::TryCreate(); + // Create an empty credential slab and verify that it is successful + auto cred_slab_result = nearby_protocol::CredentialSlab::TryCreate(); + if (!cred_slab_result.ok()) { + std::cout << cred_slab_result.status().ToString(); + return -1; + } + + // Create an empty credential book from the empty slab, and verify success. + auto cred_book_result = nearby_protocol::CredentialBook::TryCreateFromSlab(cred_slab_result.value()); if (!cred_book_result.ok()) { std::cout << cred_book_result.status().ToString(); return -1; @@ -171,7 +178,7 @@ void HandleLegibleV0Adv( nearby_protocol::LegibleDeserializedV0Advertisement legible_adv) { - HandleV0Identity(legible_adv.GetIdentity()); + HandleV0IdentityKind(legible_adv.GetIdentityKind()); auto num_des = legible_adv.GetNumberOfDataElements(); std::cout << "\t\tAdv contains " << unsigned(num_des) << " data elements \n"; @@ -187,8 +194,8 @@ } } -void HandleV0Identity(nearby_protocol::DeserializedV0Identity identity) { - switch (identity.GetKind()) { +void HandleV0IdentityKind(nearby_protocol::DeserializedV0IdentityKind identity) { + switch (identity) { case np_ffi::internal::DeserializedV0IdentityKind::Plaintext: { std::cout << "\t\tIdentity is Plaintext\n"; break;
diff --git a/nearby/presence/np_cpp_ffi/tests/CMakeLists.txt b/nearby/presence/np_cpp_ffi/tests/CMakeLists.txt index 7ca9eb0..b5ff838 100644 --- a/nearby/presence/np_cpp_ffi/tests/CMakeLists.txt +++ b/nearby/presence/np_cpp_ffi/tests/CMakeLists.txt
@@ -17,6 +17,7 @@ deserialize_result_tests.cc deserialize_v0_tests.cc deserialize_v1_tests.cc + credential_slab_tests.cc credential_book_tests.cc byte_buffer_tests.cc np_cpp_test.h
diff --git a/nearby/presence/np_cpp_ffi/tests/byte_buffer_tests.cc b/nearby/presence/np_cpp_ffi/tests/byte_buffer_tests.cc index 4bca8ec..15f262e 100644 --- a/nearby/presence/np_cpp_ffi/tests/byte_buffer_tests.cc +++ b/nearby/presence/np_cpp_ffi/tests/byte_buffer_tests.cc
@@ -70,9 +70,10 @@ nearby_protocol::RawAdvertisementPayload adv(buffer.value()); - auto credential_book = nearby_protocol::CredentialBook::TryCreate(); + auto credential_slab = nearby_protocol::CredentialSlab::TryCreate().value(); + auto credential_book = nearby_protocol::CredentialBook::TryCreateFromSlab(credential_slab).value(); auto str = nearby_protocol::Deserializer::DeserializeAdvertisement( - adv, credential_book.value()) + adv, credential_book) .IntoV1() .TryGetSection(0) .value()
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 3a32904..dcaf421 100644 --- a/nearby/presence/np_cpp_ffi/tests/credential_book_tests.cc +++ b/nearby/presence/np_cpp_ffi/tests/credential_book_tests.cc
@@ -19,19 +19,27 @@ #include "gtest/gtest.h" TEST_F(NpCppTest, TestSetMaxCredBooks) { - auto book1_result = nearby_protocol::CredentialBook::TryCreate(); + auto slab1_result = nearby_protocol::CredentialSlab::TryCreate(); + ASSERT_TRUE(slab1_result.ok()); + auto book1_result = nearby_protocol::CredentialBook::TryCreateFromSlab(slab1_result.value()); ASSERT_TRUE(book1_result.ok()); - auto book2_result = nearby_protocol::CredentialBook::TryCreate(); + auto slab2_result = nearby_protocol::CredentialSlab::TryCreate(); + ASSERT_TRUE(slab2_result.ok()); + auto book2_result = nearby_protocol::CredentialBook::TryCreateFromSlab(slab2_result.value()); ASSERT_TRUE(book2_result.ok()); - auto book3_result = nearby_protocol::CredentialBook::TryCreate(); + auto slab3_result = nearby_protocol::CredentialSlab::TryCreate(); + ASSERT_TRUE(slab3_result.ok()); + auto book3_result = nearby_protocol::CredentialBook::TryCreateFromSlab(slab3_result.value()); + ASSERT_FALSE(book3_result.ok()); ASSERT_TRUE(absl::IsResourceExhausted(book3_result.status())); } TEST_F(NpCppTest, TestMoveConstructor) { - auto book = nearby_protocol::CredentialBook::TryCreate().value(); + auto slab = nearby_protocol::CredentialSlab::TryCreate().value(); + auto book = nearby_protocol::CredentialBook::TryCreateFromSlab(slab).value(); auto deserialize_result = nearby_protocol::Deserializer::DeserializeAdvertisement(V0AdvSimple, book); @@ -62,7 +70,8 @@ } TEST_F(NpCppTest, TestMoveAssignment) { - auto book = nearby_protocol::CredentialBook::TryCreate().value(); + auto slab = nearby_protocol::CredentialSlab::TryCreate().value(); + auto book = nearby_protocol::CredentialBook::TryCreateFromSlab(slab).value(); auto deserialize_result = nearby_protocol::Deserializer::DeserializeAdvertisement(V0AdvSimple, book); @@ -70,7 +79,8 @@ np_ffi::internal::DeserializeAdvertisementResultKind::V0); // create a second empty credential book - auto other_book = nearby_protocol::CredentialBook::TryCreate().value(); + auto other_slab = nearby_protocol::CredentialSlab::TryCreate().value(); + auto other_book = nearby_protocol::CredentialBook::TryCreateFromSlab(other_slab).value(); other_book = std::move(book); // new credential book should still be successful @@ -92,4 +102,4 @@ nearby_protocol::Deserializer::DeserializeAdvertisement( V0AdvSimple, another_moved_book), ""); -} \ No newline at end of file +}
diff --git a/nearby/presence/np_cpp_ffi/tests/credential_slab_tests.cc b/nearby/presence/np_cpp_ffi/tests/credential_slab_tests.cc new file mode 100644 index 0000000..f715361 --- /dev/null +++ b/nearby/presence/np_cpp_ffi/tests/credential_slab_tests.cc
@@ -0,0 +1,56 @@ +// 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 "np_cpp_test.h" + +#include "gtest/gtest.h" + +TEST_F(NpCppTest, TestSetMaxCredSlabs) { + auto slab1_result = nearby_protocol::CredentialSlab::TryCreate(); + ASSERT_TRUE(slab1_result.ok()); + + auto slab2_result = nearby_protocol::CredentialSlab::TryCreate(); + ASSERT_TRUE(slab2_result.ok()); + + auto slab3_result = nearby_protocol::CredentialSlab::TryCreate(); + ASSERT_TRUE(slab3_result.ok()); + + auto slab4_result = nearby_protocol::CredentialSlab::TryCreate(); + + ASSERT_FALSE(slab4_result.ok()); + ASSERT_TRUE(absl::IsResourceExhausted(slab4_result.status())); +} + +TEST_F(NpCppTest, TestSlabMoveConstructor) { + auto slab = nearby_protocol::CredentialSlab::TryCreate().value(); + // It should be possible to move the slab into a new object + // and use the moved version to successfully construct a + // credential-book. + nearby_protocol::CredentialSlab next_slab(std::move(slab)); + + auto maybe_book = nearby_protocol::CredentialBook::TryCreateFromSlab(next_slab); + ASSERT_TRUE(maybe_book.ok()); + + // Now, both slabs should be moved-out-of, since `TryCreateFromSlab` takes + // ownership. Verify that this is the case, and attempts to re-use the slabs + // result in an assert failure. + ASSERT_DEATH([[maybe_unused]] auto failure = + nearby_protocol::CredentialBook::TryCreateFromSlab(slab), //NOLINT(bugprone-use-after-move) + ""); + ASSERT_DEATH([[maybe_unused]] auto failure = + nearby_protocol::CredentialBook::TryCreateFromSlab(next_slab), + ""); +}
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 737fd18..0469933 100644 --- a/nearby/presence/np_cpp_ffi/tests/deserialize_result_tests.cc +++ b/nearby/presence/np_cpp_ffi/tests/deserialize_result_tests.cc
@@ -20,7 +20,8 @@ #include "gtest/gtest.h" TEST_F(NpCppTest, TestResultMoveConstructor) { - auto book = nearby_protocol::CredentialBook::TryCreate().value(); + auto slab = nearby_protocol::CredentialSlab::TryCreate().value(); + auto book = nearby_protocol::CredentialBook::TryCreateFromSlab(slab).value(); auto result = nearby_protocol::Deserializer::DeserializeAdvertisement( V0AdvSimple, book); ASSERT_EQ(result.GetKind(), @@ -57,8 +58,12 @@ nearby_protocol::RawAdvertisementPayload adv(buffer.value()); - auto maybe_credential_book = nearby_protocol::CredentialBook::TryCreate(); + auto maybe_credential_slab = nearby_protocol::CredentialSlab::TryCreate(); + ASSERT_TRUE(maybe_credential_slab.ok()); + + auto maybe_credential_book = nearby_protocol::CredentialBook::TryCreateFromSlab(maybe_credential_slab.value()); ASSERT_TRUE(maybe_credential_book.ok()); + auto deserialize_result = nearby_protocol::Deserializer::DeserializeAdvertisement( adv, maybe_credential_book.value()); @@ -70,8 +75,8 @@ ASSERT_EQ(v0_adv.GetKind(), nearby_protocol::DeserializedV0AdvertisementKind::Legible); auto legible_adv = v0_adv.IntoLegible(); - auto identity = legible_adv.GetIdentity(); - ASSERT_EQ(identity.GetKind(), + auto identity = legible_adv.GetIdentityKind(); + ASSERT_EQ(identity, nearby_protocol::DeserializedV0IdentityKind::Plaintext); auto num_des = legible_adv.GetNumberOfDataElements(); @@ -88,7 +93,8 @@ } TEST_F(NpCppTest, TestResultMoveAssignment) { - auto book = nearby_protocol::CredentialBook::TryCreate().value(); + auto slab = nearby_protocol::CredentialSlab::TryCreate().value(); + auto book = nearby_protocol::CredentialBook::TryCreateFromSlab(slab).value(); auto result = nearby_protocol::Deserializer::DeserializeAdvertisement( V0AdvSimple, book); ASSERT_EQ(result.GetKind(), @@ -124,8 +130,13 @@ // An invalid header result should result in error nearby_protocol::RawAdvertisementPayload InvalidHeaderPayload( nearby_protocol::ByteBuffer<255>({1, {0xFF}})); - auto maybe_credential_book = nearby_protocol::CredentialBook::TryCreate(); + + auto maybe_credential_slab = nearby_protocol::CredentialSlab::TryCreate(); + ASSERT_TRUE(maybe_credential_slab.ok()); + + auto maybe_credential_book = nearby_protocol::CredentialBook::TryCreateFromSlab(maybe_credential_slab.value()); ASSERT_TRUE(maybe_credential_book.ok()); + auto deserialize_result = nearby_protocol::Deserializer::DeserializeAdvertisement( InvalidHeaderPayload, maybe_credential_book.value()); @@ -140,7 +151,12 @@ } TEST_F(NpCppTest, TestInvalidV0Cast) { - auto maybe_credential_book = nearby_protocol::CredentialBook::TryCreate(); + auto maybe_credential_slab = nearby_protocol::CredentialSlab::TryCreate(); + ASSERT_TRUE(maybe_credential_slab.ok()); + + auto maybe_credential_book = nearby_protocol::CredentialBook::TryCreateFromSlab(maybe_credential_slab.value()); + ASSERT_TRUE(maybe_credential_book.ok()); + auto deserialize_result = nearby_protocol::Deserializer::DeserializeAdvertisement( V1AdvSimple, maybe_credential_book.value()); @@ -153,8 +169,13 @@ TEST_F(NpCppTest, TestInvalidV1Cast) { // Create an empty credential book and verify that is is successful - auto maybe_credential_book = nearby_protocol::CredentialBook::TryCreate(); + auto maybe_credential_slab = nearby_protocol::CredentialSlab::TryCreate(); + ASSERT_TRUE(maybe_credential_slab.ok()); + + auto maybe_credential_book = nearby_protocol::CredentialBook::TryCreateFromSlab(maybe_credential_slab.value()); ASSERT_TRUE(maybe_credential_book.ok()); + + auto deserialize_result = nearby_protocol::Deserializer::DeserializeAdvertisement( V0AdvSimple, maybe_credential_book.value()); @@ -166,12 +187,15 @@ } TEST_F(NpCppTest, V0UseResultTwice) { - auto book_result = nearby_protocol::CredentialBook::TryCreate(); - ASSERT_TRUE(book_result.ok()); + auto maybe_credential_slab = nearby_protocol::CredentialSlab::TryCreate(); + ASSERT_TRUE(maybe_credential_slab.ok()); + + auto maybe_credential_book = nearby_protocol::CredentialBook::TryCreateFromSlab(maybe_credential_slab.value()); + ASSERT_TRUE(maybe_credential_book.ok()); auto deserialize_result = nearby_protocol::Deserializer::DeserializeAdvertisement( - V0AdvSimple, book_result.value()); + V0AdvSimple, maybe_credential_book.value()); ASSERT_EQ(deserialize_result.GetKind(), np_ffi::internal::DeserializeAdvertisementResultKind::V0); @@ -184,12 +208,15 @@ } TEST_F(NpCppTest, V1UseResultTwice) { - auto book_result = nearby_protocol::CredentialBook::TryCreate(); - ASSERT_TRUE(book_result.ok()); + auto maybe_credential_slab = nearby_protocol::CredentialSlab::TryCreate(); + ASSERT_TRUE(maybe_credential_slab.ok()); + + auto maybe_credential_book = nearby_protocol::CredentialBook::TryCreateFromSlab(maybe_credential_slab.value()); + ASSERT_TRUE(maybe_credential_book.ok()); auto deserialize_result = nearby_protocol::Deserializer::DeserializeAdvertisement( - V1AdvSimple, book_result.value()); + V1AdvSimple, maybe_credential_book.value()); ASSERT_EQ(deserialize_result.GetKind(), np_ffi::internal::DeserializeAdvertisementResultKind::V1); @@ -202,12 +229,15 @@ } TEST_F(NpCppTest, IntoV0AfterOutOfScope) { - auto book_result = nearby_protocol::CredentialBook::TryCreate(); - ASSERT_TRUE(book_result.ok()); + auto maybe_credential_slab = nearby_protocol::CredentialSlab::TryCreate(); + ASSERT_TRUE(maybe_credential_slab.ok()); + + auto maybe_credential_book = nearby_protocol::CredentialBook::TryCreateFromSlab(maybe_credential_slab.value()); + ASSERT_TRUE(maybe_credential_book.ok()); auto deserialize_result = nearby_protocol::Deserializer::DeserializeAdvertisement( - V0AdvSimple, book_result.value()); + V0AdvSimple, maybe_credential_book.value()); ASSERT_EQ(deserialize_result.GetKind(), np_ffi::internal::DeserializeAdvertisementResultKind::V0); @@ -221,12 +251,15 @@ } TEST_F(NpCppTest, IntoV1AfterOutOfScope) { - auto book_result = nearby_protocol::CredentialBook::TryCreate(); - ASSERT_TRUE(book_result.ok()); + auto maybe_credential_slab = nearby_protocol::CredentialSlab::TryCreate(); + ASSERT_TRUE(maybe_credential_slab.ok()); + + auto maybe_credential_book = nearby_protocol::CredentialBook::TryCreateFromSlab(maybe_credential_slab.value()); + ASSERT_TRUE(maybe_credential_book.ok()); auto deserialize_result = nearby_protocol::Deserializer::DeserializeAdvertisement( - V1AdvSimple, book_result.value()); + V1AdvSimple, maybe_credential_book.value()); ASSERT_EQ(deserialize_result.GetKind(), np_ffi::internal::DeserializeAdvertisementResultKind::V1); @@ -240,12 +273,15 @@ } TEST_F(NpCppTest, V0ResultKindAfterOutOfScope) { - auto book_result = nearby_protocol::CredentialBook::TryCreate(); - ASSERT_TRUE(book_result.ok()); + auto maybe_credential_slab = nearby_protocol::CredentialSlab::TryCreate(); + ASSERT_TRUE(maybe_credential_slab.ok()); + + auto maybe_credential_book = nearby_protocol::CredentialBook::TryCreateFromSlab(maybe_credential_slab.value()); + ASSERT_TRUE(maybe_credential_book.ok()); auto deserialize_result = nearby_protocol::Deserializer::DeserializeAdvertisement( - V0AdvSimple, book_result.value()); + V0AdvSimple, maybe_credential_book.value()); ASSERT_EQ(deserialize_result.GetKind(), np_ffi::internal::DeserializeAdvertisementResultKind::V0); @@ -259,12 +295,15 @@ } TEST_F(NpCppTest, V1ResultKindAfterOutOfScope) { - auto book_result = nearby_protocol::CredentialBook::TryCreate(); - ASSERT_TRUE(book_result.ok()); + auto maybe_credential_slab = nearby_protocol::CredentialSlab::TryCreate(); + ASSERT_TRUE(maybe_credential_slab.ok()); + + auto maybe_credential_book = nearby_protocol::CredentialBook::TryCreateFromSlab(maybe_credential_slab.value()); + ASSERT_TRUE(maybe_credential_book.ok()); auto deserialize_result = nearby_protocol::Deserializer::DeserializeAdvertisement( - V1AdvSimple, book_result.value()); + V1AdvSimple, maybe_credential_book.value()); ASSERT_EQ(deserialize_result.GetKind(), np_ffi::internal::DeserializeAdvertisementResultKind::V1); @@ -275,4 +314,4 @@ // in a crash. ASSERT_DEATH( { [[maybe_unused]] auto failure = deserialize_result.GetKind(); }, ""); -} \ No newline at end of file +}
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 e2a10a0..54b17ab 100644 --- a/nearby/presence/np_cpp_ffi/tests/deserialize_v0_tests.cc +++ b/nearby/presence/np_cpp_ffi/tests/deserialize_v0_tests.cc
@@ -19,7 +19,8 @@ #include "gtest/gtest.h" TEST_F(NpCppTest, InvalidCast) { - auto book = nearby_protocol::CredentialBook::TryCreate().value(); + auto slab = nearby_protocol::CredentialSlab::TryCreate().value(); + auto book = nearby_protocol::CredentialBook::TryCreateFromSlab(slab).value(); auto deserialize_result = nearby_protocol::Deserializer::DeserializeAdvertisement(V0AdvSimple, book); ASSERT_EQ(deserialize_result.GetKind(), @@ -40,7 +41,8 @@ 0x15, 0x03} // Length 1 Tx Power DE with value 3 })); - auto maybe_credential_book = nearby_protocol::CredentialBook::TryCreate(); + auto slab = nearby_protocol::CredentialSlab::TryCreate().value(); + auto maybe_credential_book = nearby_protocol::CredentialBook::TryCreateFromSlab(slab); ASSERT_TRUE(maybe_credential_book.ok()); auto deserialize_result = nearby_protocol::Deserializer::DeserializeAdvertisement( @@ -53,8 +55,8 @@ ASSERT_EQ(v0_adv.GetKind(), nearby_protocol::DeserializedV0AdvertisementKind::Legible); auto legible_adv = v0_adv.IntoLegible(); - auto identity = legible_adv.GetIdentity(); - ASSERT_EQ(identity.GetKind(), + auto identity = legible_adv.GetIdentityKind(); + ASSERT_EQ(identity, nearby_protocol::DeserializedV0IdentityKind::Plaintext); auto num_des = legible_adv.GetNumberOfDataElements(); @@ -79,7 +81,9 @@ 0x16, 0x00} // Length 1 Actions DE })); - auto maybe_credential_book = nearby_protocol::CredentialBook::TryCreate(); + auto maybe_credential_slab = nearby_protocol::CredentialSlab::TryCreate(); + ASSERT_TRUE(maybe_credential_slab.ok()); + auto maybe_credential_book = nearby_protocol::CredentialBook::TryCreateFromSlab(maybe_credential_slab.value()); ASSERT_TRUE(maybe_credential_book.ok()); auto deserialize_result = nearby_protocol::Deserializer::DeserializeAdvertisement( @@ -92,8 +96,8 @@ ASSERT_EQ(v0_adv.GetKind(), nearby_protocol::DeserializedV0AdvertisementKind::Legible); auto legible_adv = v0_adv.IntoLegible(); - auto identity = legible_adv.GetIdentity(); - ASSERT_EQ(identity.GetKind(), + auto identity = legible_adv.GetIdentityKind(); + ASSERT_EQ(identity, nearby_protocol::DeserializedV0IdentityKind::Plaintext); auto num_des = legible_adv.GetNumberOfDataElements(); @@ -118,7 +122,9 @@ 0x26, 0xD0, 0x46} // Length 2 Actions DE })); - auto maybe_credential_book = nearby_protocol::CredentialBook::TryCreate(); + auto maybe_credential_slab = nearby_protocol::CredentialSlab::TryCreate(); + ASSERT_TRUE(maybe_credential_slab.ok()); + auto maybe_credential_book = nearby_protocol::CredentialBook::TryCreateFromSlab(maybe_credential_slab.value()); ASSERT_TRUE(maybe_credential_book.ok()); auto deserialize_result = nearby_protocol::Deserializer::DeserializeAdvertisement( @@ -131,8 +137,8 @@ ASSERT_EQ(v0_adv.GetKind(), nearby_protocol::DeserializedV0AdvertisementKind::Legible); auto legible_adv = v0_adv.IntoLegible(); - auto identity = legible_adv.GetIdentity(); - ASSERT_EQ(identity.GetKind(), + auto identity = legible_adv.GetIdentityKind(); + ASSERT_EQ(identity, nearby_protocol::DeserializedV0IdentityKind::Plaintext); auto num_des = legible_adv.GetNumberOfDataElements(); @@ -174,7 +180,9 @@ 0x26, 0x00, 0x46, // Length 2 Actions }})); - auto maybe_credential_book = nearby_protocol::CredentialBook::TryCreate(); + auto maybe_credential_slab = nearby_protocol::CredentialSlab::TryCreate(); + ASSERT_TRUE(maybe_credential_slab.ok()); + auto maybe_credential_book = nearby_protocol::CredentialBook::TryCreateFromSlab(maybe_credential_slab.value()); ASSERT_TRUE(maybe_credential_book.ok()); auto deserialize_result = nearby_protocol::Deserializer::DeserializeAdvertisement( @@ -187,8 +195,8 @@ ASSERT_EQ(v0_adv.GetKind(), nearby_protocol::DeserializedV0AdvertisementKind::Legible); auto legible_adv = v0_adv.IntoLegible(); - auto identity = legible_adv.GetIdentity(); - ASSERT_EQ(identity.GetKind(), + auto identity = legible_adv.GetIdentityKind(); + ASSERT_EQ(identity, nearby_protocol::DeserializedV0IdentityKind::Plaintext); auto num_des = legible_adv.GetNumberOfDataElements(); @@ -214,7 +222,9 @@ } TEST_F(NpCppTest, V0EmptyPayload) { - auto maybe_credential_book = nearby_protocol::CredentialBook::TryCreate(); + auto maybe_credential_slab = nearby_protocol::CredentialSlab::TryCreate(); + ASSERT_TRUE(maybe_credential_slab.ok()); + auto maybe_credential_book = nearby_protocol::CredentialBook::TryCreateFromSlab(maybe_credential_slab.value()); ASSERT_TRUE(maybe_credential_book.ok()); auto deserialize_result = nearby_protocol::Deserializer::DeserializeAdvertisement( @@ -225,7 +235,8 @@ } TEST_F(NpCppTest, TestV0AdvMoveConstructor) { - auto book = nearby_protocol::CredentialBook::TryCreate().value(); + auto slab = nearby_protocol::CredentialSlab::TryCreate().value(); + auto book = nearby_protocol::CredentialBook::TryCreateFromSlab(slab).value(); auto result = nearby_protocol::Deserializer::DeserializeAdvertisement( V0AdvSimple, book); ASSERT_EQ(result.GetKind(), @@ -252,7 +263,8 @@ } TEST_F(NpCppTest, TestV0AdvMoveAssignment) { - auto book = nearby_protocol::CredentialBook::TryCreate().value(); + auto slab = nearby_protocol::CredentialSlab::TryCreate().value(); + auto book = nearby_protocol::CredentialBook::TryCreateFromSlab(slab).value(); auto result = nearby_protocol::Deserializer::DeserializeAdvertisement( V0AdvSimple, book); ASSERT_EQ(result.GetKind(), @@ -292,7 +304,9 @@ } TEST_F(NpCppTest, V0AdvDestructor) { - auto book_result = nearby_protocol::CredentialBook::TryCreate(); + auto maybe_credential_slab = nearby_protocol::CredentialSlab::TryCreate(); + ASSERT_TRUE(maybe_credential_slab.ok()); + auto book_result = nearby_protocol::CredentialBook::TryCreateFromSlab(maybe_credential_slab.value()); ASSERT_TRUE(book_result.ok()); { auto deserialize_result = CreateAdv(book_result.value()); @@ -326,7 +340,9 @@ } TEST_F(NpCppTest, V0AdvUseAfterMove) { - auto maybe_credential_book = nearby_protocol::CredentialBook::TryCreate(); + auto maybe_credential_slab = nearby_protocol::CredentialSlab::TryCreate(); + ASSERT_TRUE(maybe_credential_slab.ok()); + auto maybe_credential_book = nearby_protocol::CredentialBook::TryCreateFromSlab(maybe_credential_slab.value()); ASSERT_TRUE(maybe_credential_book.ok()); auto deserialize_result = nearby_protocol::Deserializer::DeserializeAdvertisement( @@ -346,7 +362,8 @@ } TEST_F(NpCppTest, TestLegibleAdvMoveConstructor) { - auto book = nearby_protocol::CredentialBook::TryCreate().value(); + auto slab = nearby_protocol::CredentialSlab::TryCreate().value(); + auto book = nearby_protocol::CredentialBook::TryCreateFromSlab(slab).value(); auto result = nearby_protocol::Deserializer::DeserializeAdvertisement( V0AdvSimple, book); ASSERT_EQ(result.GetKind(), @@ -356,13 +373,13 @@ // Now move the adv into a new value, and make sure its still valid nearby_protocol::LegibleDeserializedV0Advertisement moved(std::move(legible)); ASSERT_EQ(moved.GetNumberOfDataElements(), 1); - ASSERT_EQ(moved.GetIdentity().GetKind(), + ASSERT_EQ(moved.GetIdentityKind(), np_ffi::internal::DeserializedV0IdentityKind::Plaintext); // trying to use the moved object should result in a use after free which // triggers an abort ASSERT_DEATH([[maybe_unused]] auto failure = - legible.GetIdentity(), // NOLINT(bugprone-use-after-move + legible.GetIdentityKind(), // NOLINT(bugprone-use-after-move ""); ASSERT_DEATH( [[maybe_unused]] auto failure = legible.GetNumberOfDataElements(), ""); @@ -372,7 +389,7 @@ // abort nearby_protocol::LegibleDeserializedV0Advertisement moved_again( std::move(legible)); - ASSERT_DEATH([[maybe_unused]] auto failure = moved_again.GetIdentity(), ""); + ASSERT_DEATH([[maybe_unused]] auto failure = moved_again.GetIdentityKind(), ""); ASSERT_DEATH([[maybe_unused]] auto failure = moved_again.GetNumberOfDataElements(), ""); @@ -380,7 +397,8 @@ } TEST_F(NpCppTest, TestLegibleAdvMoveAssignment) { - auto book = nearby_protocol::CredentialBook::TryCreate().value(); + auto slab = nearby_protocol::CredentialSlab::TryCreate().value(); + auto book = nearby_protocol::CredentialBook::TryCreateFromSlab(slab).value(); auto result = nearby_protocol::Deserializer::DeserializeAdvertisement( V0AdvSimple, book); ASSERT_EQ(result.GetKind(), @@ -396,13 +414,13 @@ // move adv2 into adv, the original should be deallocated by assignment legible2 = std::move(legible); - ASSERT_EQ(legible2.GetIdentity().GetKind(), + ASSERT_EQ(legible2.GetIdentityKind(), np_ffi::internal::DeserializedV0IdentityKind::Plaintext); // original result should now be invalid, using it will trigger a use after // free abort. ASSERT_DEATH([[maybe_unused]] auto failure = - legible.GetIdentity(), // NOLINT(bugprone-use-after-move) + legible.GetIdentityKind(), // NOLINT(bugprone-use-after-move) ""); ASSERT_DEATH( [[maybe_unused]] auto failure = legible.GetNumberOfDataElements(), ""); @@ -411,7 +429,7 @@ // moving again should still lead to an error auto moved_again = std::move(legible); ASSERT_DEATH([[maybe_unused]] auto failure = moved_again.IntoPayload(), ""); - ASSERT_DEATH([[maybe_unused]] auto failure = moved_again.GetIdentity(), ""); + ASSERT_DEATH([[maybe_unused]] auto failure = moved_again.GetIdentityKind(), ""); ASSERT_DEATH([[maybe_unused]] auto failure = moved_again.GetNumberOfDataElements(), ""); @@ -426,19 +444,20 @@ } TEST_F(NpCppTest, V0LegibleAdvUseAfterMove) { - auto book = nearby_protocol::CredentialBook::TryCreate().value(); + auto slab = nearby_protocol::CredentialSlab::TryCreate().value(); + auto book = nearby_protocol::CredentialBook::TryCreateFromSlab(slab).value(); auto legible_adv = CreateLegibleAdv(book); // Should be able to use the valid legible adv even though its original parent // is now out of scope. - ASSERT_EQ(legible_adv.GetIdentity().GetKind(), + ASSERT_EQ(legible_adv.GetIdentityKind(), nearby_protocol::DeserializedV0IdentityKind::Plaintext); ASSERT_EQ(legible_adv.GetNumberOfDataElements(), 1); [[maybe_unused]] auto payload = legible_adv.IntoPayload(); // now that the legible adv has moved into the payload it should no longer be // valid - ASSERT_DEATH([[maybe_unused]] auto failure = legible_adv.GetIdentity(), ""); + ASSERT_DEATH([[maybe_unused]] auto failure = legible_adv.GetIdentityKind(), ""); ASSERT_DEATH([[maybe_unused]] auto failure = legible_adv.GetNumberOfDataElements(), ""); @@ -446,16 +465,17 @@ } TEST_F(NpCppTest, LegibleAdvDestructor) { - auto book = nearby_protocol::CredentialBook::TryCreate().value(); + auto slab = nearby_protocol::CredentialSlab::TryCreate().value(); + auto book = nearby_protocol::CredentialBook::TryCreateFromSlab(slab).value(); { auto legible_adv = CreateLegibleAdv(book); auto legible_adv2 = CreateLegibleAdv(book); // check that legible advs are valid. - ASSERT_EQ(legible_adv.GetIdentity().GetKind(), + ASSERT_EQ(legible_adv.GetIdentityKind(), nearby_protocol::DeserializedV0IdentityKind::Plaintext); ASSERT_EQ(legible_adv.GetNumberOfDataElements(), 1); - ASSERT_EQ(legible_adv2.GetIdentity().GetKind(), + ASSERT_EQ(legible_adv2.GetIdentityKind(), nearby_protocol::DeserializedV0IdentityKind::Plaintext); ASSERT_EQ(legible_adv2.GetNumberOfDataElements(), 1); @@ -480,7 +500,8 @@ } TEST_F(NpCppTest, V0PayloadDestructor) { - auto book = nearby_protocol::CredentialBook::TryCreate().value(); + auto slab = nearby_protocol::CredentialSlab::TryCreate().value(); + auto book = nearby_protocol::CredentialBook::TryCreateFromSlab(slab).value(); { auto payload = CreatePayload(book); auto payload2 = CreatePayload(book); @@ -505,7 +526,8 @@ } TEST_F(NpCppTest, TestV0PayloadMoveConstructor) { - auto book = nearby_protocol::CredentialBook::TryCreate().value(); + auto slab = nearby_protocol::CredentialSlab::TryCreate().value(); + auto book = nearby_protocol::CredentialBook::TryCreateFromSlab(slab).value(); auto result = nearby_protocol::Deserializer::DeserializeAdvertisement( V0AdvSimple, book); ASSERT_EQ(result.GetKind(), @@ -531,7 +553,8 @@ } TEST_F(NpCppTest, TestV0PayloadMoveAssignment) { - auto book = nearby_protocol::CredentialBook::TryCreate().value(); + auto slab = nearby_protocol::CredentialSlab::TryCreate().value(); + auto book = nearby_protocol::CredentialBook::TryCreateFromSlab(slab).value(); auto result = nearby_protocol::Deserializer::DeserializeAdvertisement( V0AdvSimple, book); ASSERT_EQ(result.GetKind(), @@ -562,7 +585,9 @@ } TEST_F(NpCppTest, InvalidDataElementCast) { - auto maybe_credential_book = nearby_protocol::CredentialBook::TryCreate(); + auto maybe_credential_slab = nearby_protocol::CredentialSlab::TryCreate(); + ASSERT_TRUE(maybe_credential_slab.ok()); + auto maybe_credential_book = nearby_protocol::CredentialBook::TryCreateFromSlab(maybe_credential_slab.value()); ASSERT_TRUE(maybe_credential_book.ok()); auto deserialize_result = nearby_protocol::Deserializer::DeserializeAdvertisement( @@ -574,8 +599,8 @@ ASSERT_EQ(v0_adv.GetKind(), nearby_protocol::DeserializedV0AdvertisementKind::Legible); auto legible_adv = v0_adv.IntoLegible(); - auto identity = legible_adv.GetIdentity(); - ASSERT_EQ(identity.GetKind(), + auto identity = legible_adv.GetIdentityKind(); + ASSERT_EQ(identity, nearby_protocol::DeserializedV0IdentityKind::Plaintext); auto num_des = legible_adv.GetNumberOfDataElements(); @@ -591,7 +616,9 @@ } TEST_F(NpCppTest, InvalidDataElementIndex) { - auto maybe_credential_book = nearby_protocol::CredentialBook::TryCreate(); + auto maybe_credential_slab = nearby_protocol::CredentialSlab::TryCreate(); + ASSERT_TRUE(maybe_credential_slab.ok()); + auto maybe_credential_book = nearby_protocol::CredentialBook::TryCreateFromSlab(maybe_credential_slab.value()); ASSERT_TRUE(maybe_credential_book.ok()); auto deserialize_result = nearby_protocol::Deserializer::DeserializeAdvertisement( @@ -604,8 +631,8 @@ ASSERT_EQ(v0_adv.GetKind(), nearby_protocol::DeserializedV0AdvertisementKind::Legible); auto legible_adv = v0_adv.IntoLegible(); - auto identity = legible_adv.GetIdentity(); - ASSERT_EQ(identity.GetKind(), + auto identity = legible_adv.GetIdentityKind(); + ASSERT_EQ(identity, nearby_protocol::DeserializedV0IdentityKind::Plaintext); auto num_des = legible_adv.GetNumberOfDataElements();
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 f46457e..aa32f7b 100644 --- a/nearby/presence/np_cpp_ffi/tests/deserialize_v1_tests.cc +++ b/nearby/presence/np_cpp_ffi/tests/deserialize_v1_tests.cc
@@ -19,7 +19,9 @@ #include "gtest/gtest.h" TEST_F(NpCppTest, V1SimpleTestCase) { - auto maybe_credential_book = nearby_protocol::CredentialBook::TryCreate(); + auto maybe_credential_slab = nearby_protocol::CredentialSlab::TryCreate(); + ASSERT_TRUE(maybe_credential_slab.ok()); + auto maybe_credential_book = nearby_protocol::CredentialBook::TryCreateFromSlab(maybe_credential_slab.value()); ASSERT_TRUE(maybe_credential_book.ok()); auto deserialize_result = @@ -57,7 +59,8 @@ } TEST_F(NpCppTest, TestV1AdvMoveConstructor) { - auto book = nearby_protocol::CredentialBook::TryCreate().value(); + auto slab = nearby_protocol::CredentialSlab::TryCreate().value(); + auto book = nearby_protocol::CredentialBook::TryCreateFromSlab(slab).value(); auto result = nearby_protocol::Deserializer::DeserializeAdvertisement( V1AdvSimple, book); ASSERT_EQ(result.GetKind(), @@ -93,7 +96,8 @@ } TEST_F(NpCppTest, TestV1AdvMoveAssignment) { - auto book = nearby_protocol::CredentialBook::TryCreate().value(); + auto slab = nearby_protocol::CredentialSlab::TryCreate().value(); + auto book = nearby_protocol::CredentialBook::TryCreateFromSlab(slab).value(); auto result = nearby_protocol::Deserializer::DeserializeAdvertisement( V1AdvSimple, book); ASSERT_EQ(result.GetKind(), @@ -153,7 +157,9 @@ } TEST_F(NpCppTest, TestSectionOwnership) { - auto maybe_credential_book = nearby_protocol::CredentialBook::TryCreate(); + auto maybe_credential_slab = nearby_protocol::CredentialSlab::TryCreate(); + ASSERT_TRUE(maybe_credential_slab.ok()); + auto maybe_credential_book = nearby_protocol::CredentialBook::TryCreateFromSlab(maybe_credential_slab.value()); ASSERT_TRUE(maybe_credential_book.ok()); { @@ -223,4 +229,4 @@ nearby_protocol::DeserializedV1IdentityKind::Plaintext); ASSERT_EQ(section2.value().NumberOfDataElements(), 1); } -*/ \ 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 index 4fc20e1..03eb03a 100644 --- a/nearby/presence/np_cpp_ffi/tests/np_cpp_test.h +++ b/nearby/presence/np_cpp_ffi/tests/np_cpp_test.h
@@ -29,6 +29,7 @@ panic_handler_set = true; nearby_protocol::GlobalConfig::SetMaxNumDeserializedV0Advertisements(2); nearby_protocol::GlobalConfig::SetMaxNumDeserializedV1Advertisements(2); + nearby_protocol::GlobalConfig::SetMaxNumCredentialSlabs(3); nearby_protocol::GlobalConfig::SetMaxNumCredentialBooks(2); } else { ASSERT_FALSE(
diff --git a/nearby/presence/np_ed25519/Cargo.toml b/nearby/presence/np_ed25519/Cargo.toml index 601fc82..bd42c65 100644 --- a/nearby/presence/np_ed25519/Cargo.toml +++ b/nearby/presence/np_ed25519/Cargo.toml
@@ -4,9 +4,12 @@ edition.workspace = true publish.workspace = true +[lints] +workspace = true + [dependencies] array_view.workspace = true -crypto_provider = {workspace = true, features = ["raw_private_key_permit"]} +crypto_provider = { workspace = true, features = ["raw_private_key_permit"] } sink.workspace = true tinyvec.workspace = true
diff --git a/nearby/presence/np_ed25519/src/lib.rs b/nearby/presence/np_ed25519/src/lib.rs index 81a1a10..fe2ab5a 100644 --- a/nearby/presence/np_ed25519/src/lib.rs +++ b/nearby/presence/np_ed25519/src/lib.rs
@@ -19,10 +19,6 @@ //! or verified. These "context" bytes allow for usage of the //! same base key-pair for different purposes in the protocol. #![no_std] -#![forbid(unsafe_code)] -#![deny(missing_docs, clippy::indexing_slicing)] - -extern crate core; use array_view::ArrayView; use crypto_provider::ed25519::{ @@ -191,7 +187,8 @@ impl<C: CryptoProvider> Clone for PublicKey<C> { fn clone(&self) -> Self { - Self::from_bytes(&self.to_bytes()).unwrap() + #[allow(clippy::expect_used)] + Self::from_bytes(&self.to_bytes()).expect("This should always succeed since self will always contain valid public key bytes, which is verified on creation") } } @@ -250,6 +247,7 @@ /// [`SignatureContext#write_length_prefixed`]. fn create_signature_buffer(&self) -> impl Sink<u8> + AsRef<[u8]> { let mut buffer = ArrayVec::<[u8; MAX_SIGNATURE_BUFFER_LEN]>::new(); + #[allow(clippy::expect_used)] self.write_length_prefixed(&mut buffer).expect("Context should always fit into sig buffer"); buffer }
diff --git a/nearby/presence/np_ffi_core/Cargo.toml b/nearby/presence/np_ffi_core/Cargo.toml index 126302f..0c14d1c 100644 --- a/nearby/presence/np_ffi_core/Cargo.toml +++ b/nearby/presence/np_ffi_core/Cargo.toml
@@ -4,12 +4,17 @@ edition.workspace = true publish.workspace = true +[lints] +workspace = true + [dependencies] array_view.workspace = true +ldt_np_adv.workspace = true np_adv = { workspace = true, features = ["alloc"] } +np_hkdf.workspace = true handle_map.workspace = true crypto_provider.workspace = true -crypto_provider_default = {workspace = true, default-features = false} +crypto_provider_default = { workspace = true, default-features = false } lock_adapter.workspace = true lazy_static.workspace = true
diff --git a/nearby/presence/np_ffi_core/src/common.rs b/nearby/presence/np_ffi_core/src/common.rs index 1683b68..6333db5 100644 --- a/nearby/presence/np_ffi_core/src/common.rs +++ b/nearby/presence/np_ffi_core/src/common.rs
@@ -40,6 +40,11 @@ /// - In all other cases, 16 shards will be used by default. num_shards: u8, + /// The maximum number of credential slabs which may be active + /// at any one time. By default, this value will be set to + /// `u32::MAX - 1`, which is the upper-bound on this value. + max_num_credential_slabs: u32, + /// The maximum number of credential books which may be active /// at any one time. By default, this value will be set to /// `u32::MAX - 1`, which is the upper-bound on this value. @@ -68,6 +73,7 @@ pub(crate) const fn new() -> Self { Self { num_shards: 0, + max_num_credential_slabs: DEFAULT_MAX_HANDLES, max_num_credential_books: DEFAULT_MAX_HANDLES, max_num_deserialized_v0_advertisements: DEFAULT_MAX_HANDLES, max_num_deserialized_v1_advertisements: DEFAULT_MAX_HANDLES, @@ -92,6 +98,9 @@ self.num_shards } } + pub(crate) fn max_num_credential_slabs(&self) -> u32 { + self.max_num_credential_slabs + } pub(crate) fn max_num_credential_books(&self) -> u32 { self.max_num_credential_books } @@ -112,6 +121,13 @@ self.max_num_credential_books = DEFAULT_MAX_HANDLES.min(max_num_credential_books) } + /// Sets the maximum number of active handles to credential-slabs + /// which may be active at any one time. + /// Max value: `u32::MAX - 1`. + pub fn set_max_num_credential_slabs(&mut self, max_num_credential_slabs: u32) { + self.max_num_credential_slabs = DEFAULT_MAX_HANDLES.min(max_num_credential_slabs) + } + /// Sets the maximum number of active handles to deserialized v0 /// advertisements which may be active at any one time. /// Max value: `u32::MAX - 1`. @@ -140,6 +156,9 @@ pub(crate) fn global_num_shards() -> u8 { COMMON_CONFIG.read().num_shards() } +pub(crate) fn global_max_num_credential_slabs() -> u32 { + COMMON_CONFIG.read().max_num_credential_slabs() +} pub(crate) fn global_max_num_credential_books() -> u32 { COMMON_CONFIG.read().max_num_credential_books() } @@ -169,6 +188,17 @@ config.set_num_shards(num_shards); } +/// Sets the maximum number of active handles to credential slabs +/// which may be active at any one time. Max value: `u32::MAX - 1`. +/// +/// Setting this value will have no effect if the handle-maps for the +/// API have already begun being used by the client code, and any +/// values set will take effect upon the first usage of any API +/// call utilizing credential slabs. +pub fn global_config_set_max_num_credential_slabs(max_num_credential_slabs: u32) { + let mut config = COMMON_CONFIG.write(); + config.set_max_num_credential_slabs(max_num_credential_slabs); +} /// Sets the maximum number of active handles to credential books /// which may be active at any one time. Max value: `u32::MAX - 1`. /// @@ -275,6 +305,32 @@ } } +/// The DE type for an encrypted identity +#[derive(Clone, Copy)] +#[repr(u8)] +pub enum EncryptedIdentityType { + /// Identity for broadcasts to nearby devices with the same + /// logged-in-account (for some account). + Private = 1, + /// Identity for broadcasts to nearby devices which this + /// device has declared to trust. + Trusted = 2, + /// Identity for broadcasts to devices which have been provisioned + /// offline with this device. + Provisioned = 4, +} + +impl From<np_adv::de_type::EncryptedIdentityDataElementType> for EncryptedIdentityType { + fn from(value: np_adv::de_type::EncryptedIdentityDataElementType) -> Self { + use np_adv::de_type::EncryptedIdentityDataElementType; + match value { + EncryptedIdentityDataElementType::Private => Self::Private, + EncryptedIdentityDataElementType::Trusted => Self::Trusted, + EncryptedIdentityDataElementType::Provisioned => Self::Provisioned, + } + } +} + /// Error raised when attempting to cast an enum to /// one of its variants, but the value is actually /// of a different variant than the requested one.
diff --git a/nearby/presence/np_ffi_core/src/credentials.rs b/nearby/presence/np_ffi_core/src/credentials.rs index 5392417..0972d45 100644 --- a/nearby/presence/np_ffi_core/src/credentials.rs +++ b/nearby/presence/np_ffi_core/src/credentials.rs
@@ -14,13 +14,282 @@ //! Credential-related data-types and functions use crate::common::*; -use crate::utils::FfiEnum; -use handle_map::{declare_handle_map, HandleLike, HandleMapDimensions, HandleMapFullError}; +use crate::utils::{FfiEnum, LocksLongerThan}; +use crypto_provider_default::CryptoProviderImpl; +use handle_map::{ + declare_handle_map, HandleLike, HandleMapDimensions, HandleMapFullError, + HandleMapTryAllocateError, +}; +use std::sync::Arc; + +/// Cryptographic information about a particular V0 discovery credential +/// necessary to match and decrypt encrypted V0 advertisements. +#[repr(C)] +pub struct V0DiscoveryCredential { + key_seed: [u8; 32], + legacy_metadata_key_hmac: [u8; 32], +} + +impl V0DiscoveryCredential { + /// Constructs a new V0 discovery credential with the given 32-byte key-seed + /// and the given 32-byte HMAC for the (14-byte) legacy metadata key. + pub fn new(key_seed: [u8; 32], legacy_metadata_key_hmac: [u8; 32]) -> Self { + Self { key_seed, legacy_metadata_key_hmac } + } + fn into_internal(self) -> np_adv::credential::v0::V0DiscoveryCredential { + np_adv::credential::v0::V0DiscoveryCredential::new( + self.key_seed, + self.legacy_metadata_key_hmac, + ) + } +} + +/// Cryptographic information about a particular V1 discovery credential +/// necessary to match and decrypt encrypted V1 advertisement sections. +#[repr(C)] +pub struct V1DiscoveryCredential { + key_seed: [u8; 32], + expected_unsigned_metadata_key_hmac: [u8; 32], + expected_signed_metadata_key_hmac: [u8; 32], + pub_key: [u8; 32], +} + +impl V1DiscoveryCredential { + /// Constructs a new V1 discovery credential with the given 32-byte key-seed, + /// unsigned-variant HMAC of the metadata key, the signed-variant HMAC of + /// the metadata key, and the given public key for signature verification. + pub fn new( + key_seed: [u8; 32], + expected_unsigned_metadata_key_hmac: [u8; 32], + expected_signed_metadata_key_hmac: [u8; 32], + pub_key: [u8; 32], + ) -> Self { + Self { + key_seed, + expected_unsigned_metadata_key_hmac, + expected_signed_metadata_key_hmac, + pub_key, + } + } + fn into_internal(self) -> np_adv::credential::v1::V1DiscoveryCredential { + np_adv::credential::v1::V1DiscoveryCredential::new( + self.key_seed, + self.expected_unsigned_metadata_key_hmac, + self.expected_signed_metadata_key_hmac, + self.pub_key, + ) + } +} + +/// A [`MatchedCredential`] implementation for the purpose of +/// capturing match-data details across the FFI boundary. +/// Since we can't know what plaintext match-data the client +/// wants to keep around, we just expose an ID for them to do +/// their own look-up. +/// +/// For the encrypted metadata, we need a slightly richer +/// representation, since we need to be able to decrypt +/// the metadata as part of an API call. Internally, we +/// keep this as an atomic-reference-counted pointer to +/// a byte array, and never expose this raw pointer across +/// the FFI boundary. +#[derive(Debug, Clone)] +pub struct MatchedCredential { + cred_id: u32, + encrypted_metadata_bytes: Arc<[u8]>, +} + +impl MatchedCredential { + /// Constructs a new matched credential from the given match-id + /// (some arbitrary `u32` identifier) and encrypted metadata bytes, + /// copied from the given slice. + pub fn new(cred_id: u32, encrypted_metadata_bytes: &[u8]) -> Self { + let encrypted_metadata_bytes = encrypted_metadata_bytes.to_vec().into_boxed_slice(); + let encrypted_metadata_bytes = Arc::from(encrypted_metadata_bytes); + Self { cred_id, encrypted_metadata_bytes } + } + /// Gets the pre-specified numerical identifier for this matched-credential. + pub(crate) fn id(&self) -> u32 { + self.cred_id + } +} + +impl PartialEq<MatchedCredential> for MatchedCredential { + fn eq(&self, other: &Self) -> bool { + self.id() == other.id() + } +} + +impl Eq for MatchedCredential {} + +impl np_adv::credential::MatchedCredential for MatchedCredential { + type EncryptedMetadata = Arc<[u8]>; + type EncryptedMetadataFetchError = core::convert::Infallible; + fn fetch_encrypted_metadata(&self) -> Result<Arc<[u8]>, core::convert::Infallible> { + Ok(self.encrypted_metadata_bytes.clone()) + } +} + +/// Internals of a credential slab, +/// an intermediate used in the construction +/// of a credential-book. +pub struct CredentialSlabInternals { + v0_creds: + Vec<np_adv::credential::MatchableCredential<np_adv::credential::v0::V0, MatchedCredential>>, + v1_creds: + Vec<np_adv::credential::MatchableCredential<np_adv::credential::v1::V1, MatchedCredential>>, +} + +impl CredentialSlabInternals { + pub(crate) fn new() -> Self { + Self { v0_creds: Vec::new(), v1_creds: Vec::new() } + } + /// Adds the given V0 discovery credential with the given + /// identity match-data onto the end of the V0 credentials + /// currently stored in this slab. + pub(crate) fn add_v0( + &mut self, + discovery_credential: V0DiscoveryCredential, + match_data: MatchedCredential, + ) { + let discovery_credential = discovery_credential.into_internal(); + let matchable_credential = + np_adv::credential::MatchableCredential { discovery_credential, match_data }; + self.v0_creds.push(matchable_credential); + } + /// Adds the given V1 discovery credential with the given + /// identity match-data onto the end of the V1 credentials + /// currently stored in this slab. + pub(crate) fn add_v1( + &mut self, + discovery_credential: V1DiscoveryCredential, + match_data: MatchedCredential, + ) { + let discovery_credential = discovery_credential.into_internal(); + let matchable_credential = + np_adv::credential::MatchableCredential { discovery_credential, match_data }; + self.v1_creds.push(matchable_credential); + } +} + +/// Discriminant for `CreateCredentialSlabResult` +#[repr(u8)] +pub enum CreateCredentialSlabResultKind { + /// There was no space left to create a new credential slab + NoSpaceLeft = 0, + /// We created a new credential slab behind the given handle. + /// The associated payload may be obtained via + /// `CreateCredentialSlabResult#into_success()`. + Success = 1, +} + +/// Result type for `create_credential_slab` +#[repr(C)] +#[allow(missing_docs)] +pub enum CreateCredentialSlabResult { + NoSpaceLeft, + Success(CredentialSlab), +} + +impl From<Result<CredentialSlab, HandleMapFullError>> for CreateCredentialSlabResult { + fn from(result: Result<CredentialSlab, HandleMapFullError>) -> Self { + match result { + Ok(slab) => CreateCredentialSlabResult::Success(slab), + Err(_) => CreateCredentialSlabResult::NoSpaceLeft, + } + } +} + +/// Result type for trying to add a credential to a credential-slab. +#[repr(u8)] +pub enum AddCredentialToSlabResult { + /// We succeeded in adding the credential to the slab. + Success = 0, + /// The handle to the slab was actually invalid. + InvalidHandle = 1, +} + +declare_handle_map! { + mod credential_slab { + #[dimensions = super::get_credential_slab_handle_map_dimensions()] + type CredentialSlab: HandleLike<Object = super::CredentialSlabInternals>; + } +} +use credential_slab::CredentialSlab; + +fn get_credential_slab_handle_map_dimensions() -> HandleMapDimensions { + HandleMapDimensions { + num_shards: global_num_shards(), + max_active_handles: global_max_num_credential_slabs(), + } +} + +impl CredentialSlab { + /// Adds the given V0 discovery credential with some associated + /// match-data to this credential slab. + pub fn add_v0( + &self, + discovery_credential: V0DiscoveryCredential, + match_data: MatchedCredential, + ) -> AddCredentialToSlabResult { + match self.get_mut() { + Ok(mut write_guard) => { + write_guard.add_v0(discovery_credential, match_data); + AddCredentialToSlabResult::Success + } + Err(_) => AddCredentialToSlabResult::InvalidHandle, + } + } + /// Adds the given V1 discovery credential with some associated + /// match-data to this credential slab. + pub fn add_v1( + &self, + discovery_credential: V1DiscoveryCredential, + match_data: MatchedCredential, + ) -> AddCredentialToSlabResult { + match self.get_mut() { + Ok(mut write_guard) => { + write_guard.add_v1(discovery_credential, match_data); + AddCredentialToSlabResult::Success + } + Err(_) => AddCredentialToSlabResult::InvalidHandle, + } + } +} + +/// Allocates a new credential-slab, returning a handle to the created object +pub fn create_credential_slab() -> CreateCredentialSlabResult { + CredentialSlab::allocate(CredentialSlabInternals::new).into() +} + +impl FfiEnum for CreateCredentialSlabResult { + type Kind = CreateCredentialSlabResultKind; + fn kind(&self) -> Self::Kind { + match self { + CreateCredentialSlabResult::NoSpaceLeft => CreateCredentialSlabResultKind::NoSpaceLeft, + CreateCredentialSlabResult::Success(_) => CreateCredentialSlabResultKind::Success, + } + } +} + +impl CreateCredentialSlabResult { + declare_enum_cast! {into_success, Success, CredentialSlab } +} /// Internal, Rust-side implementation of a credential-book. /// See [`CredentialBook`] for the FFI-side handles. -// TODO: Give this a real definition! -pub struct CredentialBookInternals; +pub struct CredentialBookInternals { + pub(crate) book: np_adv::credential::book::PrecalculatedOwnedCredentialBook<MatchedCredential>, +} + +impl CredentialBookInternals { + fn create_from_slab(credential_slab: CredentialSlabInternals) -> Self { + let book = np_adv::credential::book::CredentialBookBuilder::build_precalculated_owned_book::< + CryptoProviderImpl, + >(credential_slab.v0_creds, credential_slab.v1_creds); + Self { book } + } +} fn get_credential_book_handle_map_dimensions() -> HandleMapDimensions { HandleMapDimensions { @@ -40,42 +309,60 @@ /// Discriminant for `CreateCredentialBookResult` #[repr(u8)] pub enum CreateCredentialBookResultKind { - /// There was no space left to create a new credential book - NoSpaceLeft = 0, /// We created a new credential book behind the given handle. /// The associated payload may be obtained via /// `CreateCredentialBookResult#into_success()`. - Success = 1, + Success = 0, + /// There was no space left to create a new credential book + NoSpaceLeft = 1, + /// The slab that we tried to create a credential-book from + /// actually was an invalid handle. + InvalidSlabHandle = 2, } /// Result type for `create_credential_book` #[repr(u8)] #[allow(missing_docs)] pub enum CreateCredentialBookResult { - NoSpaceLeft = 0, - Success(CredentialBook) = 1, + Success(CredentialBook) = 0, + NoSpaceLeft = 1, + InvalidSlabHandle = 2, } -impl From<Result<CredentialBook, HandleMapFullError>> for CreateCredentialBookResult { - fn from(result: Result<CredentialBook, HandleMapFullError>) -> Self { - match result { - Ok(book) => CreateCredentialBookResult::Success(book), - Err(_) => CreateCredentialBookResult::NoSpaceLeft, - } - } -} +impl LocksLongerThan<CredentialSlab> for CredentialBook {} /// Allocates a new credential-book, returning a handle to the created object -pub fn create_credential_book() -> CreateCredentialBookResult { - CredentialBook::allocate(|| CredentialBookInternals).into() +pub fn create_credential_book_from_slab( + credential_slab: CredentialSlab, +) -> CreateCredentialBookResult { + // The credential-book allocation is on the outside, since we should ensure + // that we have a slot available for construction before we try to deallocate + // the credential-slab which was passed in. + let op_result = CredentialBook::try_allocate(|| { + credential_slab.deallocate().map(CredentialBookInternals::create_from_slab) + }); + match op_result { + Ok(book) => CreateCredentialBookResult::Success(book), + Err(HandleMapTryAllocateError::ValueProviderFailed(_)) => { + // Unable to deallocate the referenced credential-slab + CreateCredentialBookResult::InvalidSlabHandle + } + Err(HandleMapTryAllocateError::HandleMapFull) => { + // Unable to allocate space for a new credential-book + CreateCredentialBookResult::NoSpaceLeft + } + } } impl FfiEnum for CreateCredentialBookResult { type Kind = CreateCredentialBookResultKind; fn kind(&self) -> Self::Kind { match self { - CreateCredentialBookResult::NoSpaceLeft => CreateCredentialBookResultKind::NoSpaceLeft, CreateCredentialBookResult::Success(_) => CreateCredentialBookResultKind::Success, + CreateCredentialBookResult::NoSpaceLeft => CreateCredentialBookResultKind::NoSpaceLeft, + CreateCredentialBookResult::InvalidSlabHandle => { + CreateCredentialBookResultKind::InvalidSlabHandle + } } } } @@ -89,5 +376,7 @@ credential_book.deallocate().map(|_| ()).into() } -/// A handle on a particular v0 shared credential stored within a credential book -pub struct V0SharedCredential; +/// Deallocates a credential-slab by its handle +pub fn deallocate_credential_slab(credential_slab: CredentialSlab) -> DeallocateResult { + credential_slab.deallocate().map(|_| ()).into() +}
diff --git a/nearby/presence/np_ffi_core/src/deserialize/mod.rs b/nearby/presence/np_ffi_core/src/deserialize/mod.rs index 38f59ba..44510f6 100644 --- a/nearby/presence/np_ffi_core/src/deserialize/mod.rs +++ b/nearby/presence/np_ffi_core/src/deserialize/mod.rs
@@ -112,16 +112,13 @@ credential_book: CredentialBook, ) -> Result<DeserializeAdvertisementSuccess, DeserializeAdvertisementError> { // Deadlock Safety: Credential-book locks always live longer than deserialized advs. - let _credential_book_read_guard = credential_book.get()?; + let credential_book_read_guard = credential_book.get()?; - //TODO: Use an actual credential source - let cred_book = np_adv::credential::book::CredentialBookBuilder::< - np_adv::credential::EmptyMatchedCredential, - >::build_cached_slice_book::<0, 0, CryptoProviderImpl>(&[], &[]); + let cred_book = &credential_book_read_guard.book; let arena = deserialization_arena!(); let deserialized_advertisement = - np_adv::deserialize_advertisement::<_, CryptoProviderImpl>(arena, adv_payload, &cred_book)?; + np_adv::deserialize_advertisement::<_, CryptoProviderImpl>(arena, adv_payload, cred_book)?; match deserialized_advertisement { np_adv::DeserializedAdvertisement::V0(adv_contents) => { let adv_handle = DeserializedV0Advertisement::allocate_with_contents(adv_contents)?; @@ -156,3 +153,13 @@ ) -> DeserializeAdvertisementResult { deserialize_advertisement_from_slice(adv_payload.as_slice(), credential_book) } + +/// Errors returned from [`crate::deserialize::v0::v0_payload::V0Payload#decrypt_metadata`]. +pub enum DecryptMetadataError { + /// The advertisement payload handle was either deallocated + /// or corresponds to a public advertisement, and so we + /// don't have any metadata to decrypt. + EncryptedMetadataNotAvailable, + /// Decryption of the raw metadata bytes failed. + DecryptionFailed, +}
diff --git a/nearby/presence/np_ffi_core/src/deserialize/v0.rs b/nearby/presence/np_ffi_core/src/deserialize/v0.rs index d8c008a..a07b9d8 100644 --- a/nearby/presence/np_ffi_core/src/deserialize/v0.rs +++ b/nearby/presence/np_ffi_core/src/deserialize/v0.rs
@@ -15,10 +15,14 @@ use crate::common::*; use crate::credentials::credential_book::CredentialBook; +use crate::credentials::MatchedCredential; +use crate::deserialize::DecryptMetadataError; use crate::utils::{FfiEnum, LocksLongerThan}; +use crypto_provider_default::CryptoProviderImpl; use handle_map::{declare_handle_map, HandleLike, HandleMapDimensions, HandleMapFullError}; use np_adv::legacy::actions::ActionsDataElement; use np_adv::legacy::{data_elements as np_adv_de, Ciphertext, PacketFlavorEnum, Plaintext}; +use np_adv::HasIdentityMatch; use std::vec::Vec; /// Discriminant for possible results of V0 advertisement deserialization @@ -66,8 +70,10 @@ } } - pub(crate) fn allocate_with_contents<M: np_adv::credential::MatchedCredential>( - contents: np_adv::V0AdvertisementContents<M>, + pub(crate) fn allocate_with_contents( + contents: np_adv::V0AdvertisementContents< + np_adv::credential::ReferencedMatchedCredential<MatchedCredential>, + >, ) -> Result<Self, DeserializeAdvertisementError> { match contents { np_adv::V0AdvertisementContents::Plaintext(plaintext_contents) => { @@ -76,8 +82,12 @@ )?; Ok(Self::Legible(adv)) } - np_adv::V0AdvertisementContents::Decrypted(_) => { - unimplemented!(); + np_adv::V0AdvertisementContents::Decrypted(decrypted_contents) => { + let decrypted_contents = decrypted_contents.clone_match_data(); + let adv = LegibleDeserializedV0Advertisement::allocate_with_decrypted_contents( + decrypted_contents, + )?; + Ok(Self::Legible(adv)) } np_adv::V0AdvertisementContents::NoMatchingCredentials => { Ok(Self::NoMatchingCredentials) @@ -93,7 +103,7 @@ pub struct LegibleDeserializedV0Advertisement { num_des: u8, payload: V0Payload, - identity: DeserializedV0Identity, + identity_kind: DeserializedV0IdentityKind, } impl LegibleDeserializedV0Advertisement { @@ -105,8 +115,38 @@ .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 }) + let payload = V0Payload::allocate_with_plaintext_data_elements(data_elements)?; + Ok(Self { num_des, payload, identity_kind: DeserializedV0IdentityKind::Plaintext }) + } + pub(crate) fn allocate_with_decrypted_contents( + contents: np_adv::WithMatchedCredential< + MatchedCredential, + np_adv::legacy::deserialize::DecryptedAdvContents, + >, + ) -> Result<Self, DeserializeAdvertisementError> { + let data_elements = contents + .contents() + .data_elements() + .collect::<Result<Vec<_>, _>>() + .map_err(|_| DeserializeAdvertisementError)?; + let num_des = data_elements.len() as u8; + + let salt = contents.contents().salt(); + let identity_type = contents.contents().identity_type(); + + // Reduce the information contained in the contents to just + // the metadata key, since we're done copying over the DEs + // and other data into an FFI-friendly form. + let match_data = contents.map(|x| x.metadata_key()); + + let payload = V0Payload::allocate_with_decrypted_contents( + identity_type, + salt, + match_data, + data_elements, + )?; + + Ok(Self { num_des, payload, identity_kind: DeserializedV0IdentityKind::Decrypted }) } /// Gets the number of data-elements in this adv's payload /// Suitable as an iteration bound for `Self.into_payload().get_de(...)`. @@ -117,9 +157,10 @@ pub fn payload(&self) -> V0Payload { self.payload } - /// Destructures this legible advertisement into just the identity information - pub fn identity(&self) -> DeserializedV0Identity { - self.identity + /// Destructures this legible advertisement into just the discriminant + /// for the kind of identity (plaintext/encrypted) used for its contents. + pub fn identity_kind(&self) -> DeserializedV0IdentityKind { + self.identity_kind } /// Deallocates the underlying handle of the payload pub fn deallocate(self) -> DeallocateResult { @@ -127,7 +168,8 @@ } } -/// Discriminant for `DeserializedV0Identity`. +/// Discriminant for deserialized information about the V0 +/// identity utilized by a deserialized V0 advertisement. #[derive(Clone, Copy)] #[repr(u8)] pub enum DeserializedV0IdentityKind { @@ -137,30 +179,136 @@ Decrypted = 1, } -/// Represents deserialized information about the V0 identity utilized -/// by a deserialized V0 advertisement +/// Information about the identity which matched a +/// decrypted V0 advertisement. +#[derive(Clone, Copy)] #[repr(C)] -#[allow(missing_docs)] -#[derive(Copy, Clone, Eq, PartialEq)] -pub enum DeserializedV0Identity { - Plaintext, - // TODO: This gets a payload once we support creds - Decrypted, +pub struct DeserializedV0IdentityDetails { + /// The identity type (private/provisioned/trusted) + identity_type: EncryptedIdentityType, + /// The ID of the credential which + /// matched the deserialized adv + cred_id: u32, + /// The 14-byte legacy metadata key + metadata_key: [u8; 14], + /// The 2-byte advertisement salt + salt: [u8; 2], } -impl FfiEnum for DeserializedV0Identity { - type Kind = DeserializedV0IdentityKind; +impl DeserializedV0IdentityDetails { + pub(crate) fn new( + cred_id: u32, + identity_type: np_adv::de_type::EncryptedIdentityDataElementType, + salt: ldt_np_adv::LegacySalt, + metadata_key: np_adv::legacy::ShortMetadataKey, + ) -> Self { + let metadata_key = metadata_key.0; + let salt = *salt.bytes(); + let identity_type = identity_type.into(); + Self { identity_type, cred_id, salt, metadata_key } + } + /// Returns the ID of the credential which + /// matched the deserialized adv + pub fn cred_id(&self) -> u32 { + self.cred_id + } + /// Returns the identity type (private/provisioned/trusted) + pub fn identity_type(&self) -> EncryptedIdentityType { + self.identity_type + } + /// Returns the 14-byte legacy metadata key + pub fn metadata_key(&self) -> [u8; 14] { + self.metadata_key + } + /// Returns the 2-byte advertisement salt + pub fn salt(&self) -> [u8; 2] { + self.salt + } +} + +/// Discriminant for `GetV0IdentityDetailsResult` +#[derive(Clone, Copy)] +#[repr(u8)] +pub enum GetV0IdentityDetailsResultKind { + /// The attempt to get the identity details + /// for the advertisement failed, possibly + /// due to the advertisement being a public + /// advertisement, or the underlying + /// advertisement has already been deallocated. + Error = 0, + /// The attempt to get the identity details succeeded. + /// The wrapped identity details may be obtained via + /// `GetV0IdentityDetailsResult#into_success`. + Success = 1, +} + +/// The result of attempting to get the identity details +/// for a V0 advertisement via +/// `DeserializedV0Advertisement#get_identity_details`. +#[repr(C)] +#[allow(missing_docs)] +pub enum GetV0IdentityDetailsResult { + Error, + Success(DeserializedV0IdentityDetails), +} + +impl FfiEnum for GetV0IdentityDetailsResult { + type Kind = GetV0IdentityDetailsResultKind; fn kind(&self) -> Self::Kind { match self { - DeserializedV0Identity::Plaintext => DeserializedV0IdentityKind::Plaintext, - DeserializedV0Identity::Decrypted => DeserializedV0IdentityKind::Decrypted, + GetV0IdentityDetailsResult::Error => GetV0IdentityDetailsResultKind::Error, + GetV0IdentityDetailsResult::Success(_) => GetV0IdentityDetailsResultKind::Success, } } } +impl GetV0IdentityDetailsResult { + declare_enum_cast! {into_success, Success, DeserializedV0IdentityDetails} +} + +/// Internal implementation of a deserialized V0 identity. +pub(crate) struct DeserializedV0IdentityInternals { + /// The details about the identity, suitable + /// for direct communication over FFI + details: DeserializedV0IdentityDetails, + /// The metadata key, together with the matched + /// credential and enough information to decrypt + /// the credential metadata, if desired. + match_data: np_adv::WithMatchedCredential<MatchedCredential, np_adv::legacy::ShortMetadataKey>, +} + +impl DeserializedV0IdentityInternals { + pub(crate) fn new( + identity_type: np_adv::de_type::EncryptedIdentityDataElementType, + salt: ldt_np_adv::LegacySalt, + match_data: np_adv::WithMatchedCredential< + MatchedCredential, + np_adv::legacy::ShortMetadataKey, + >, + ) -> Self { + let cred_id = match_data.matched_credential().id(); + let metadata_key = match_data.contents(); + let details = + DeserializedV0IdentityDetails::new(cred_id, identity_type, salt, *metadata_key); + Self { details, match_data } + } + /// Gets the directly-transmissible details about + /// this deserialized V0 identity. Does not include + /// decrypted metadata bytes. + pub(crate) fn details(&self) -> DeserializedV0IdentityDetails { + self.details + } + /// Attempts to decrypt the metadata associated + /// with this identity. + pub(crate) fn decrypt_metadata(&self) -> Option<Vec<u8>> { + self.match_data.decrypt_metadata::<CryptoProviderImpl>().ok() + } +} + /// The internal data-structure used for storing /// the payload of a deserialized V0 advertisement. pub struct V0PayloadInternals { + identity: Option<DeserializedV0IdentityInternals>, des: Vec<V0DataElement>, } @@ -173,6 +321,24 @@ None => GetV0DEResult::Error, } } + /// Gets the identity details for this V0 payload, + /// if this payload was associated with an identity. + fn get_identity_details(&self) -> GetV0IdentityDetailsResult { + match &self.identity { + Some(x) => GetV0IdentityDetailsResult::Success(x.details()), + None => GetV0IdentityDetailsResult::Error, + } + } + /// Attempts to decrypt the metadata for the matched + /// credential for this V0 payload (if any) + fn decrypt_metadata(&self) -> Result<Vec<u8>, DecryptMetadataError> { + match &self.identity { + None => Err(DecryptMetadataError::EncryptedMetadataNotAvailable), + Some(identity) => { + identity.decrypt_metadata().ok_or(DecryptMetadataError::DecryptionFailed) + } + } + } } fn get_v0_payload_handle_map_dimensions() -> HandleMapDimensions { @@ -195,12 +361,33 @@ impl LocksLongerThan<V0Payload> for CredentialBook {} impl V0Payload { - pub(crate) fn allocate_with_data_elements<F: np_adv::legacy::PacketFlavor>( - data_elements: Vec<np_adv::legacy::deserialize::PlainDataElement<F>>, + pub(crate) fn allocate_with_plaintext_data_elements( + data_elements: Vec< + np_adv::legacy::deserialize::PlainDataElement<np_adv::legacy::Plaintext>, + >, ) -> Result<Self, HandleMapFullError> { Self::allocate(move || { let des = data_elements.into_iter().map(V0DataElement::from).collect(); - V0PayloadInternals { des } + let identity = None; + V0PayloadInternals { des, identity } + }) + } + pub(crate) fn allocate_with_decrypted_contents( + identity_type: np_adv::de_type::EncryptedIdentityDataElementType, + salt: ldt_np_adv::LegacySalt, + match_data: np_adv::WithMatchedCredential< + MatchedCredential, + np_adv::legacy::ShortMetadataKey, + >, + data_elements: Vec< + np_adv::legacy::deserialize::PlainDataElement<np_adv::legacy::Ciphertext>, + >, + ) -> Result<Self, HandleMapFullError> { + Self::allocate(move || { + let des = data_elements.into_iter().map(V0DataElement::from).collect(); + let identity = + Some(DeserializedV0IdentityInternals::new(identity_type, salt, match_data)); + V0PayloadInternals { des, identity } }) } /// Gets the data-element with the given index in this v0 adv payload @@ -211,6 +398,32 @@ } } + /// Gets the identity details for this V0 payload, + /// if this payload was associted with an identity + /// (i.e: non-public advertisements). + pub fn get_identity_details(&self) -> GetV0IdentityDetailsResult { + match self.get() { + Ok(read_guard) => read_guard.get_identity_details(), + Err(_) => GetV0IdentityDetailsResult::Error, + } + } + + /// Attempts to decrypt the metadata for the matched + /// credential for this V0 payload (if any) + /// + /// Note that while this method is publicly exposed + /// from `np_ffi_core`, since it involves the (FFI-layer-unexpressed) + /// type `Vec<u8>`, a direct wrapper will not suffice, + /// and instead a language-specific binding will need to + /// be generated for this method which respects the + /// expected memory-management semantics of the target language. + pub fn decrypt_metadata(&self) -> Result<Vec<u8>, DecryptMetadataError> { + match self.get() { + Ok(read_guard) => read_guard.decrypt_metadata(), + Err(_) => Err(DecryptMetadataError::EncryptedMetadataNotAvailable), + } + } + /// Deallocates any underlying data held by a V0Payload pub fn deallocate_payload(&self) -> DeallocateResult { self.deallocate().map(|_| ()).into()
diff --git a/nearby/presence/np_ffi_core/src/deserialize/v1.rs b/nearby/presence/np_ffi_core/src/deserialize/v1.rs index 819bb8c..4684f55 100644 --- a/nearby/presence/np_ffi_core/src/deserialize/v1.rs +++ b/nearby/presence/np_ffi_core/src/deserialize/v1.rs
@@ -16,11 +16,15 @@ use super::DeserializeAdvertisementError; use crate::common::*; use crate::credentials::credential_book::CredentialBook; +use crate::credentials::MatchedCredential; +use crate::deserialize::DecryptMetadataError; use crate::utils::*; use array_view::ArrayView; +use crypto_provider_default::CryptoProviderImpl; use handle_map::{declare_handle_map, HandleLike, HandleMapDimensions}; use legible_v1_sections::LegibleV1Sections; use np_adv::extended::deserialize::DataElementParseError; +use np_adv::HasIdentityMatch; use std::vec::Vec; /// Representation of a deserialized V1 advertisement @@ -59,8 +63,10 @@ self.legible_sections.deallocate().map(|_| ()).into() } - pub(crate) fn allocate_with_contents<M: np_adv::credential::MatchedCredential>( - contents: np_adv::V1AdvertisementContents<M>, + pub(crate) fn allocate_with_contents( + contents: np_adv::V1AdvertisementContents< + np_adv::credential::ReferencedMatchedCredential<MatchedCredential>, + >, ) -> Result<Self, DeserializeAdvertisementError> { // 16-section limit enforced by np_adv let num_undecryptable_sections = contents.invalid_sections_count() as u8; @@ -109,13 +115,25 @@ } } -impl<'adv, M: np_adv::credential::MatchedCredential> - TryFrom<Vec<np_adv::V1DeserializedSection<'adv, M>>> for LegibleV1SectionsInternals +impl<'adv> + TryFrom< + Vec< + np_adv::V1DeserializedSection< + 'adv, + np_adv::credential::ReferencedMatchedCredential<'adv, MatchedCredential>, + >, + >, + > for LegibleV1SectionsInternals { type Error = DataElementParseError; fn try_from( - contents: Vec<np_adv::V1DeserializedSection<'adv, M>>, + contents: Vec< + np_adv::V1DeserializedSection< + 'adv, + np_adv::credential::ReferencedMatchedCredential<'adv, MatchedCredential>, + >, + >, ) -> Result<Self, Self::Error> { let sections = contents .into_iter() @@ -142,8 +160,12 @@ impl LocksLongerThan<LegibleV1Sections> for CredentialBook {} impl LegibleV1Sections { - pub(crate) fn allocate_with_contents<M: np_adv::credential::MatchedCredential>( - contents: Vec<np_adv::V1DeserializedSection<M>>, + pub(crate) fn allocate_with_contents( + contents: Vec< + np_adv::V1DeserializedSection< + np_adv::credential::ReferencedMatchedCredential<MatchedCredential>, + >, + >, ) -> Result<Self, DeserializeAdvertisementError> { let section = LegibleV1SectionsInternals::try_from(contents) .map_err(|_| DeserializeAdvertisementError)?; @@ -190,10 +212,45 @@ declare_enum_cast! {into_success, Success, DeserializedV1Section} } +/// Discriminant for `GetV1DE16ByteSaltResult`. +#[derive(Clone, Copy)] +#[repr(u8)] +pub enum GetV1DE16ByteSaltResultKind { + /// The attempt to get the derived salt failed, possibly + /// because the passed DE offset was invalid (==255), + /// or because there was no salt included for the + /// referenced advertisement section (i.e: it was + /// a public advertisement section, or it was deallocated.) + Error = 0, + /// A 16-byte salt for the given DE offset was successfully + /// derived. + Success = 1, +} + +/// The result of attempting to get a derived 16-byte salt +/// for a given DE within a section. +#[derive(Copy, Clone)] +#[repr(C)] +#[allow(missing_docs)] +pub enum GetV1DE16ByteSaltResult { + Error, + Success([u8; 16]), +} + +impl FfiEnum for GetV1DE16ByteSaltResult { + type Kind = GetV1DE16ByteSaltResultKind; + fn kind(&self) -> Self::Kind { + match self { + GetV1DE16ByteSaltResult::Error => GetV1DE16ByteSaltResultKind::Error, + GetV1DE16ByteSaltResult::Success(_) => GetV1DE16ByteSaltResultKind::Success, + } + } +} + /// The internal FFI-friendly representation of a deserialized v1 section pub struct DeserializedV1SectionInternals { des: Vec<V1DataElement>, - identity: DeserializedV1Identity, + identity: Option<DeserializedV1IdentityInternals>, } impl DeserializedV1SectionInternals { @@ -203,7 +260,11 @@ } /// Gets the enum tag of the identity used for this section. fn identity_kind(&self) -> DeserializedV1IdentityKind { - self.identity.kind() + if self.identity.is_some() { + DeserializedV1IdentityKind::Decrypted + } else { + DeserializedV1IdentityKind::Plaintext + } } /// Attempts to get the DE with the given index in this section. fn get_de(&self, index: u8) -> GetV1DEResult { @@ -212,14 +273,53 @@ None => GetV1DEResult::Error, } } + /// Attempts to get the directly-transmissible details about + /// the deserialized V1 identity for this section. Does + /// not include decrypted metadata bytes nor the section salt. + pub(crate) fn get_identity_details(&self) -> GetV1IdentityDetailsResult { + match &self.identity { + Some(identity) => GetV1IdentityDetailsResult::Success(identity.details()), + None => GetV1IdentityDetailsResult::Error, + } + } + /// Attempts to decrypt the metadata for the matched + /// credential for this V1 section (if any). + pub(crate) fn decrypt_metadata(&self) -> Result<Vec<u8>, DecryptMetadataError> { + match &self.identity { + None => Err(DecryptMetadataError::EncryptedMetadataNotAvailable), + Some(identity) => { + identity.decrypt_metadata().ok_or(DecryptMetadataError::DecryptionFailed) + } + } + } + /// Attempts to derive a 16-byte DE salt for a DE in this section + /// with the given DE offset. This operation may fail if the + /// passed offset is 255 (causes overflow) or if the section + /// is leveraging a public identity, and hence, doesn't have + /// an associated salt. + pub(crate) fn derive_16_byte_salt_for_offset(&self, de_offset: u8) -> GetV1DE16ByteSaltResult { + self.identity + .as_ref() + .and_then(|x| x.derive_16_byte_salt_for_offset(de_offset)) + .map_or(GetV1DE16ByteSaltResult::Error, GetV1DE16ByteSaltResult::Success) + } } -impl<'adv, M: np_adv::credential::MatchedCredential> TryFrom<np_adv::V1DeserializedSection<'adv, M>> - for DeserializedV1SectionInternals +impl<'adv> + TryFrom< + np_adv::V1DeserializedSection< + 'adv, + np_adv::credential::ReferencedMatchedCredential<'adv, MatchedCredential>, + >, + > for DeserializedV1SectionInternals { type Error = DataElementParseError; - fn try_from(section: np_adv::V1DeserializedSection<M>) -> Result<Self, Self::Error> { + fn try_from( + section: np_adv::V1DeserializedSection< + np_adv::credential::ReferencedMatchedCredential<'adv, MatchedCredential>, + >, + ) -> Result<Self, Self::Error> { use np_adv::extended::deserialize::Section; use np_adv::V1DeserializedSection; match section { @@ -228,11 +328,30 @@ .iter_data_elements() .map(|r| r.map(|de| V1DataElement::from(&de))) .collect::<Result<Vec<_>, _>>()?; - let identity = DeserializedV1Identity::Plaintext; + let identity = None; Ok(Self { des, identity }) } - V1DeserializedSection::Decrypted(_) => { - unimplemented!(); + V1DeserializedSection::Decrypted(with_matched) => { + let section = with_matched.contents(); + let des = section + .iter_data_elements() + .map(|r| r.map(|de| V1DataElement::from(&de))) + .collect::<Result<Vec<_>, _>>()?; + + let identity_type = section.identity_type(); + let verification_mode = section.verification_mode(); + let salt = section.salt(); + + let match_data = with_matched.clone_match_data(); + let match_data = match_data.map(|x| x.metadata_key()); + + let identity = Some(DeserializedV1IdentityInternals::new( + identity_type, + verification_mode, + salt, + match_data, + )); + Ok(Self { des, identity }) } } } @@ -248,27 +367,168 @@ Decrypted = 1, } -/// Deserialized information about the identity -/// employed in a V1 adveritsement section. -#[derive(Clone, Copy)] -#[repr(C)] -#[allow(missing_docs)] -pub enum DeserializedV1Identity { - Plaintext, - // TODO: This gets a payload once we support creds - Decrypted, +/// Internals for the representation of a decrypted +/// V1 section identity. +pub(crate) struct DeserializedV1IdentityInternals { + /// The details about the identity, suitable + /// for direct communication over FFI + details: DeserializedV1IdentityDetails, + /// The metadata key, together with the matched + /// credential and enough information to decrypt + /// the credential metadata, if desired. + match_data: np_adv::WithMatchedCredential<MatchedCredential, np_adv::MetadataKey>, + /// The 16-byte section salt + salt: np_adv::extended::deserialize::RawV1Salt, } -impl FfiEnum for DeserializedV1Identity { - type Kind = DeserializedV1IdentityKind; +impl DeserializedV1IdentityInternals { + pub(crate) fn new( + identity_type: np_adv::de_type::EncryptedIdentityDataElementType, + verification_mode: np_adv::extended::deserialize::VerificationMode, + salt: np_adv::extended::deserialize::RawV1Salt, + match_data: np_adv::WithMatchedCredential<MatchedCredential, np_adv::MetadataKey>, + ) -> Self { + let cred_id = match_data.matched_credential().id(); + let metadata_key = match_data.contents(); + let details = DeserializedV1IdentityDetails::new( + cred_id, + identity_type, + verification_mode, + *metadata_key, + ); + Self { details, match_data, salt } + } + /// Gets the directly-transmissible details about + /// this deserialized V1 identity. Does not include + /// decrypted metadata bytes nor the section salt. + pub(crate) fn details(&self) -> DeserializedV1IdentityDetails { + self.details + } + /// Attempts to decrypt the metadata associated + /// with this identity. + pub(crate) fn decrypt_metadata(&self) -> Option<Vec<u8>> { + self.match_data.decrypt_metadata::<CryptoProviderImpl>().ok() + } + /// For a given data-element offset, derives a 16-byte DE salt + /// for a DE in that position within this section. + pub(crate) fn derive_16_byte_salt_for_offset(&self, de_offset: u8) -> Option<[u8; 16]> { + let section_salt = np_hkdf::v1_salt::V1Salt::<CryptoProviderImpl>::from(self.salt); + let de_offset = np_hkdf::v1_salt::DataElementOffset::from(de_offset); + section_salt.derive::<16>(Some(de_offset)) + } +} + +/// Information about the verification scheme used +/// for verifying the integrity of the contents +/// of a decrypted section. +#[derive(Clone, Copy)] +#[repr(u8)] +pub enum V1VerificationMode { + /// Message integrity code verification. + Mic = 0, + /// Signature verification. + Signature = 1, +} + +impl From<np_adv::extended::deserialize::VerificationMode> for V1VerificationMode { + fn from(verification_mode: np_adv::extended::deserialize::VerificationMode) -> Self { + use np_adv::extended::deserialize::VerificationMode; + match verification_mode { + VerificationMode::Mic => Self::Mic, + VerificationMode::Signature => Self::Signature, + } + } +} + +/// Discriminant for `GetV1IdentityDetailsResult` +#[derive(Clone, Copy)] +#[repr(u8)] +pub enum GetV1IdentityDetailsResultKind { + /// The attempt to get the identity details + /// for the section failed, possibly + /// due to the section being a public + /// section, or the underlying + /// advertisement has already been deallocated. + Error = 0, + /// The attempt to get the identity details succeeded. + /// The wrapped identity details may be obtained via + /// `GetV1IdentityDetailsResult#into_success`. + Success = 1, +} + +/// The result of attempting to get the identity details +/// for a V1 advertisement section via +/// `DeserializedV1Advertisement#get_identity_details`. +#[repr(C)] +#[allow(missing_docs)] +pub enum GetV1IdentityDetailsResult { + Error, + Success(DeserializedV1IdentityDetails), +} + +impl FfiEnum for GetV1IdentityDetailsResult { + type Kind = GetV1IdentityDetailsResultKind; fn kind(&self) -> Self::Kind { match self { - DeserializedV1Identity::Plaintext => DeserializedV1IdentityKind::Plaintext, - DeserializedV1Identity::Decrypted => DeserializedV1IdentityKind::Decrypted, + GetV1IdentityDetailsResult::Error => GetV1IdentityDetailsResultKind::Error, + GetV1IdentityDetailsResult::Success(_) => GetV1IdentityDetailsResultKind::Success, } } } +impl GetV1IdentityDetailsResult { + declare_enum_cast! {into_success, Success, DeserializedV1IdentityDetails} +} + +/// Information about the identity which matched +/// a decrypted V1 section. +#[derive(Clone, Copy)] +#[repr(C)] +pub struct DeserializedV1IdentityDetails { + /// The identity type (private/provisioned/trusted) + identity_type: EncryptedIdentityType, + /// The verification mode (MIC/Signature) which + /// was used to verify the decrypted adv contents. + verification_mode: V1VerificationMode, + /// The ID of the credential which + /// matched the deserialized section. + cred_id: u32, + /// The 16-byte metadata key. + metadata_key: [u8; 16], +} + +impl DeserializedV1IdentityDetails { + pub(crate) fn new( + cred_id: u32, + identity_type: np_adv::de_type::EncryptedIdentityDataElementType, + verification_mode: np_adv::extended::deserialize::VerificationMode, + metadata_key: np_adv::MetadataKey, + ) -> Self { + let metadata_key = metadata_key.0; + let identity_type = identity_type.into(); + let verification_mode = verification_mode.into(); + Self { cred_id, identity_type, verification_mode, metadata_key } + } + /// Returns the ID of the credential which + /// matched the deserialized section. + pub fn cred_id(&self) -> u32 { + self.cred_id + } + /// Returns the identity type (private/provisioned/trusted) + pub fn identity_type(&self) -> EncryptedIdentityType { + self.identity_type + } + /// Returns the verification mode (MIC/Signature) + /// employed for the decrypted section. + pub fn verification_mode(&self) -> V1VerificationMode { + self.verification_mode + } + /// Returns the 16-byte section metadata key. + pub fn metadata_key(&self) -> [u8; 16] { + self.metadata_key + } +} + /// Handle to a deserialized V1 section #[repr(C)] pub struct DeserializedV1Section { @@ -290,20 +550,60 @@ self.identity_tag } - /// Gets the DE with the given index in this section. - pub fn get_de(&self, de_index: u8) -> GetV1DEResult { + fn apply_to_section_internals<R>( + &self, + func: impl FnOnce(&DeserializedV1SectionInternals) -> R, + lookup_failure_result: R, + ) -> R { // TODO: Once the `FromResidual` trait is stabilized, this can be simplified. match self.legible_sections_handle.get() { Ok(legible_sections_read_guard) => { match legible_sections_read_guard.get_section_internals(self.legible_section_index) { - Some(section_ref) => section_ref.get_de(de_index), - None => GetV1DEResult::Error, + Some(section_ref) => func(section_ref), + None => lookup_failure_result, } } - Err(_) => GetV1DEResult::Error, + Err(_) => lookup_failure_result, } } + /// Gets the DE with the given index in this section. + pub fn get_de(&self, de_index: u8) -> GetV1DEResult { + self.apply_to_section_internals( + move |section_ref| section_ref.get_de(de_index), + GetV1DEResult::Error, + ) + } + /// Attempts to get the details of the identity employed + /// for the section referenced by this handle. May fail + /// if the handle is invalid, or if the advertisement + /// section leverages a public identity. + pub fn get_identity_details(&self) -> GetV1IdentityDetailsResult { + self.apply_to_section_internals( + DeserializedV1SectionInternals::get_identity_details, + GetV1IdentityDetailsResult::Error, + ) + } + /// Attempts to decrypt the metadata for the matched + /// credential for the V1 section referenced by + /// this handle (if any). + pub fn decrypt_metadata(&self) -> Result<Vec<u8>, DecryptMetadataError> { + self.apply_to_section_internals( + DeserializedV1SectionInternals::decrypt_metadata, + Err(DecryptMetadataError::EncryptedMetadataNotAvailable), + ) + } + /// Attempts to derive a 16-byte DE salt for a DE in this section + /// with the given DE offset. This operation may fail if the + /// passed offset is 255 (causes overflow) or if the section + /// is leveraging a public identity, and hence, doesn't have + /// an associated salt. + pub fn derive_16_byte_salt_for_offset(&self, de_offset: u8) -> GetV1DE16ByteSaltResult { + self.apply_to_section_internals( + move |section_ref| section_ref.derive_16_byte_salt_for_offset(de_offset), + GetV1DE16ByteSaltResult::Error, + ) + } } /// Discriminant for the `GetV1DEResult` enum. @@ -365,13 +665,14 @@ impl<'a> From<&'a np_adv::extended::deserialize::DataElement<'a>> for V1DataElement { fn from(de: &'a np_adv::extended::deserialize::DataElement<'a>) -> Self { + let offset = de.offset().as_u8(); let de_type = V1DEType::from(de.de_type()); let contents_as_slice = de.contents(); //Guaranteed not to panic due DE size limit. #[allow(clippy::unwrap_used)] let array_view: ArrayView<u8, 127> = ArrayView::try_from_slice(contents_as_slice).unwrap(); let payload = ByteBuffer::from_array_view(array_view); - Self::Generic(GenericV1DataElement { de_type, payload }) + Self::Generic(GenericV1DataElement { de_type, offset, payload }) } } @@ -381,6 +682,8 @@ #[derive(Clone)] #[repr(C)] pub struct GenericV1DataElement { + /// The offset of this generic data-element. + pub offset: u8, /// The DE type code of this generic data-element. pub de_type: V1DEType, /// The raw data-element byte payload, up to @@ -389,6 +692,10 @@ } impl GenericV1DataElement { + /// Gets the offset for this generic V1 data element. + pub fn offset(&self) -> u8 { + self.offset + } /// Gets the DE-type of this generic V1 data element. pub fn de_type(&self) -> V1DEType { self.de_type
diff --git a/nearby/presence/np_ffi_core/src/lib.rs b/nearby/presence/np_ffi_core/src/lib.rs index bba33e2..0c25168 100644 --- a/nearby/presence/np_ffi_core/src/lib.rs +++ b/nearby/presence/np_ffi_core/src/lib.rs
@@ -11,15 +11,8 @@ // 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. + //! Core functionality common to all NP Rust FFI layers -#![forbid(unsafe_code)] -#![deny( - missing_docs, - clippy::indexing_slicing, - clippy::unwrap_used, - clippy::panic, - clippy::expect_used -)] #[macro_use] extern crate lazy_static;
diff --git a/nearby/presence/np_hkdf/Cargo.toml b/nearby/presence/np_hkdf/Cargo.toml index 885913f..45e40da 100644 --- a/nearby/presence/np_hkdf/Cargo.toml +++ b/nearby/presence/np_hkdf/Cargo.toml
@@ -4,6 +4,9 @@ edition.workspace = true publish.workspace = true +[lints] +workspace = true + [features] default = [] std = []
diff --git a/nearby/presence/np_hkdf/benches/np_hkdf.rs b/nearby/presence/np_hkdf/benches/np_hkdf.rs index e3ce506..bec3b0e 100644 --- a/nearby/presence/np_hkdf/benches/np_hkdf.rs +++ b/nearby/presence/np_hkdf/benches/np_hkdf.rs
@@ -12,6 +12,8 @@ // See the License for the specific language governing permissions and // limitations under the License. +#![allow(missing_docs, unused_results)] + use criterion::{black_box, criterion_group, criterion_main, Criterion}; use crypto_provider::{CryptoProvider, CryptoRng}; use crypto_provider_default::CryptoProviderImpl;
diff --git a/nearby/presence/np_hkdf/src/lib.rs b/nearby/presence/np_hkdf/src/lib.rs index f4f98eb..8201f38 100644 --- a/nearby/presence/np_hkdf/src/lib.rs +++ b/nearby/presence/np_hkdf/src/lib.rs
@@ -16,17 +16,9 @@ //! //! All HKDF calls should happen in this module and expose the correct result type for //! each derived key use case. -#![no_std] -#![forbid(unsafe_code)] -#![deny( - missing_docs, - clippy::indexing_slicing, - clippy::unwrap_used, - clippy::panic, - clippy::expect_used -)] -extern crate core; +#![no_std] + #[cfg(feature = "std")] extern crate std;
diff --git a/nearby/presence/np_hkdf/src/v1_salt.rs b/nearby/presence/np_hkdf/src/v1_salt.rs index ae327db..94e6855 100644 --- a/nearby/presence/np_hkdf/src/v1_salt.rs +++ b/nearby/presence/np_hkdf/src/v1_salt.rs
@@ -45,7 +45,7 @@ &[ b"V1 derived salt", &de.and_then(|d| d.offset.checked_add(1)) - .and_then(|o| o.try_into().ok()) + .map(|o| o.into()) .unwrap_or(0_u32) .to_be_bytes(), ],
diff --git a/nearby/presence/np_hkdf/tests/test_vectors.rs b/nearby/presence/np_hkdf/tests/test_vectors.rs index 380c107..175e73e 100644 --- a/nearby/presence/np_hkdf/tests/test_vectors.rs +++ b/nearby/presence/np_hkdf/tests/test_vectors.rs
@@ -12,6 +12,8 @@ // See the License for the specific language governing permissions and // limitations under the License. +#![allow(clippy::indexing_slicing, clippy::unwrap_used)] + use anyhow::anyhow; use crypto_provider::aes::AesKey; use crypto_provider_default::CryptoProviderImpl; @@ -28,7 +30,7 @@ test_helper::get_data_file("presence/np_hkdf/resources/test/hkdf-test-vectors.json"); let mut file = fs::File::open(full_path)?; let mut data = String::new(); - file.read_to_string(&mut data)?; + let _ = file.read_to_string(&mut data)?; let test_cases = match serde_json::de::from_str(&data)? { serde_json::Value::Array(a) => a, _ => return Err(anyhow!("bad json")),
diff --git a/nearby/presence/rand_ext/Cargo.toml b/nearby/presence/rand_ext/Cargo.toml index bbdb5af..79a8ea8 100644 --- a/nearby/presence/rand_ext/Cargo.toml +++ b/nearby/presence/rand_ext/Cargo.toml
@@ -4,6 +4,9 @@ edition.workspace = true publish.workspace = true +[lints] +workspace = true + [dependencies] crypto_provider.workspace = true log.workspace = true
diff --git a/nearby/presence/rand_ext/src/lib.rs b/nearby/presence/rand_ext/src/lib.rs index 9819c6b..d767143 100644 --- a/nearby/presence/rand_ext/src/lib.rs +++ b/nearby/presence/rand_ext/src/lib.rs
@@ -14,8 +14,6 @@ //! Helper functions around `rand`'s offerings for convenient test usage. #![no_std] -#![forbid(unsafe_code)] -#![deny(missing_docs)] extern crate alloc;
diff --git a/nearby/presence/sink/Cargo.toml b/nearby/presence/sink/Cargo.toml index a0322b6..a479205 100644 --- a/nearby/presence/sink/Cargo.toml +++ b/nearby/presence/sink/Cargo.toml
@@ -4,6 +4,9 @@ edition.workspace = true publish.workspace = true +[lints] +workspace = true + [dependencies] tinyvec.workspace = true
diff --git a/nearby/presence/sink/src/lib.rs b/nearby/presence/sink/src/lib.rs index 75c7c1d..fe6e5b6 100644 --- a/nearby/presence/sink/src/lib.rs +++ b/nearby/presence/sink/src/lib.rs
@@ -14,15 +14,8 @@ //! A no_std-friendly data-writing "sink" trait which allows for convenient expression //! of "write me into a limited-size buffer"-type methods on traits. + #![cfg_attr(not(feature = "std"), no_std)] -#![forbid(unsafe_code)] -#![deny( - missing_docs, - clippy::indexing_slicing, - clippy::unwrap_used, - clippy::panic, - clippy::expect_used -)] /// An append-only, limited-size collection. pub trait Sink<T> {
diff --git a/nearby/presence/test_helper/Cargo.toml b/nearby/presence/test_helper/Cargo.toml index 488ea74..c52d6bf 100644 --- a/nearby/presence/test_helper/Cargo.toml +++ b/nearby/presence/test_helper/Cargo.toml
@@ -4,6 +4,9 @@ edition.workspace = true publish.workspace = true +[lints] +workspace = true + [dependencies] hex.workspace = true serde_json.workspace = true
diff --git a/nearby/presence/test_helper/src/lib.rs b/nearby/presence/test_helper/src/lib.rs index 7c0694f..8b42623 100644 --- a/nearby/presence/test_helper/src/lib.rs +++ b/nearby/presence/test_helper/src/lib.rs
@@ -11,11 +11,11 @@ // 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. -#![forbid(unsafe_code)] -#![deny(missing_docs)] //! Helper crate for common functions used in testing +#![allow(clippy::unwrap_used, clippy::expect_used)] + use std::fs; use std::io::Read; @@ -33,7 +33,7 @@ let full_path = get_data_file(file); let mut file = fs::File::open(full_path).expect("Should be able to open data file"); let mut data = String::new(); - file.read_to_string(&mut data).expect("should be able to read data file"); + let _ = file.read_to_string(&mut data).expect("should be able to read data file"); data } @@ -45,12 +45,12 @@ /// extract a string from a jsonvalue pub fn extract_key_str<'a>(value: &'a serde_json::Value, key: &str) -> &'a str { - value[key].as_str().unwrap() + value.get(key).unwrap().as_str().unwrap() } /// Decode a hex-encoded vec at `key` pub fn extract_key_vec(value: &serde_json::Value, key: &str) -> Vec<u8> { - hex::decode(value[key].as_str().unwrap()).unwrap() + hex::decode(value.get(key).unwrap().as_str().unwrap()).unwrap() } /// Decode a hex-encoded array at `key`
diff --git a/nearby/presence/xts_aes/Cargo.toml b/nearby/presence/xts_aes/Cargo.toml index ba7aadb..86745c4 100644 --- a/nearby/presence/xts_aes/Cargo.toml +++ b/nearby/presence/xts_aes/Cargo.toml
@@ -4,6 +4,9 @@ edition.workspace = true publish.workspace = true +[lints] +workspace = true + [features] default = [] std = [] @@ -14,7 +17,7 @@ ldt_tbc.workspace = true [dev-dependencies] -crypto_provider_default = {workspace = true, features = ["rustcrypto"]} +crypto_provider_default = { workspace = true, features = ["rustcrypto"] } rand_ext.workspace = true test_helper.workspace = true wycheproof.workspace = true
diff --git a/nearby/presence/xts_aes/src/lib.rs b/nearby/presence/xts_aes/src/lib.rs index ac2cf15..e8a80a1 100644 --- a/nearby/presence/xts_aes/src/lib.rs +++ b/nearby/presence/xts_aes/src/lib.rs
@@ -12,21 +12,13 @@ // See the License for the specific language governing permissions and // limitations under the License. -#![no_std] -#![forbid(unsafe_code)] -#![deny( - missing_docs, - clippy::unwrap_used, - clippy::panic, - clippy::expect_used, - clippy::indexing_slicing -)] - //! Implementation of the XTS-AES tweakable block cipher. //! //! See NIST docs [here](https://luca-giuzzi.unibs.it/corsi/Support/papers-cryptography/1619-2007-NIST-Submission.pdf) //! and [here](https://nvlpubs.nist.gov/nistpubs/Legacy/SP/nistspecialpublication800-38e.pdf). +#![no_std] + #[cfg(feature = "std")] extern crate std;
diff --git a/nearby/presence/xts_aes/tests/compare_with_xts_mode_test.rs b/nearby/presence/xts_aes/tests/compare_with_xts_mode_test.rs index 94ed066..abe1077 100644 --- a/nearby/presence/xts_aes/tests/compare_with_xts_mode_test.rs +++ b/nearby/presence/xts_aes/tests/compare_with_xts_mode_test.rs
@@ -12,6 +12,8 @@ // See the License for the specific language governing permissions and // limitations under the License. +#![allow(clippy::unwrap_used)] + use aes::{cipher, cipher::KeyInit as _}; use alloc::vec::Vec; use crypto_provider::aes::*;
diff --git a/nearby/presence/xts_aes/tests/wycheproof_test_vectors.rs b/nearby/presence/xts_aes/tests/wycheproof_test_vectors.rs index 724e3bd..1f92f89 100644 --- a/nearby/presence/xts_aes/tests/wycheproof_test_vectors.rs +++ b/nearby/presence/xts_aes/tests/wycheproof_test_vectors.rs
@@ -12,7 +12,7 @@ // See the License for the specific language governing permissions and // limitations under the License. -extern crate core; +#![allow(clippy::unwrap_used, clippy::expect_used, clippy::indexing_slicing)] use crypto_provider::CryptoProvider; use crypto_provider_default::CryptoProviderImpl;
diff --git a/nearby/presence/xts_aes/tests/xts_nist_test_vectors.rs b/nearby/presence/xts_aes/tests/xts_nist_test_vectors.rs index 3d0cb50..6ceb8b9 100644 --- a/nearby/presence/xts_aes/tests/xts_nist_test_vectors.rs +++ b/nearby/presence/xts_aes/tests/xts_nist_test_vectors.rs
@@ -12,7 +12,7 @@ // See the License for the specific language governing permissions and // limitations under the License. -extern crate core; +#![allow(clippy::expect_used, clippy::unwrap_used, clippy::panic)] use anyhow::anyhow; use crypto_provider::CryptoProvider; @@ -225,7 +225,7 @@ // `key = value` in a test case chunk if let Some(captures) = regex::Regex::new("^(.*) = (.*)$").unwrap().captures(&line) { - map.insert( + let _ = map.insert( captures.get(1).unwrap().as_str().to_owned(), captures.get(2).unwrap().as_str().to_owned(), );
diff --git a/nearby/presence/xts_aes/tests/xts_roundtrip_tests.rs b/nearby/presence/xts_aes/tests/xts_roundtrip_tests.rs index 0e28dfa..924b665 100644 --- a/nearby/presence/xts_aes/tests/xts_roundtrip_tests.rs +++ b/nearby/presence/xts_aes/tests/xts_roundtrip_tests.rs
@@ -12,6 +12,8 @@ // See the License for the specific language governing permissions and // limitations under the License. +#![allow(clippy::unwrap_used)] + use alloc::vec::Vec; use crypto_provider::aes::*; use crypto_provider::CryptoProvider;
diff --git a/nearby/src/main.rs b/nearby/src/main.rs index eaf3ed2..3d698e2 100644 --- a/nearby/src/main.rs +++ b/nearby/src/main.rs
@@ -38,6 +38,7 @@ Subcommand::CheckEverything { ref check_options } => { check_everything(&root_dir, check_options)? } + Subcommand::CleanEverything => clean_everything(&root_dir)?, Subcommand::CheckWorkspace(ref options) => check_workspace(&root_dir, options)?, Subcommand::FfiCheckEverything(ref options) => ffi::check_everything(&root_dir, options)?, Subcommand::BoringsslCheckEverything(ref options) => { @@ -95,6 +96,8 @@ Ok(()) } + +/// Runs checks to ensure lints are passing and all targets are building pub fn check_everything(root: &path::Path, check_options: &CheckOptions) -> anyhow::Result<()> { license::check_license_headers(root)?; check_workspace(root, check_options)?; @@ -110,6 +113,15 @@ Ok(()) } +pub fn clean_everything(root: &path::Path) -> anyhow::Result<()> { + run_cmd_shell(root, "cargo clean")?; + run_cmd_shell(&root.join("presence/ldt_np_adv_ffi"), "cargo clean")?; + run_cmd_shell(&root.join("presence/np_c_ffi"), "cargo clean")?; + run_cmd_shell(&root.join("crypto/crypto_provider_boringssl"), "cargo clean")?; + run_cmd_shell(&root.join("connections/ukey2/ukey2_c_ffi"), "cargo clean")?; + Ok(()) +} + #[derive(clap::Parser)] struct Cli { #[clap(subcommand)] @@ -123,6 +135,9 @@ #[command(flatten)] check_options: CheckOptions, }, + /// Cleans the main workspace and all sub projects - useful if upgrading rust compiler version + /// and need dependencies to be compiled with the same version + CleanEverything, /// Checks everything included in the top level workspace CheckWorkspace(CheckOptions), /// Checks everything related to the boringssl version (equivalent of running check-boringssl
diff --git a/nearby/util/handle_map/Cargo.toml b/nearby/util/handle_map/Cargo.toml index fbe3a20..13973d4 100644 --- a/nearby/util/handle_map/Cargo.toml +++ b/nearby/util/handle_map/Cargo.toml
@@ -4,6 +4,9 @@ edition.workspace = true publish.workspace = true +[lints] +workspace = true + [dependencies] lock_adapter.workspace = true
diff --git a/nearby/util/handle_map/benches/benches.rs b/nearby/util/handle_map/benches/benches.rs index f1ee427..f1988ba 100644 --- a/nearby/util/handle_map/benches/benches.rs +++ b/nearby/util/handle_map/benches/benches.rs
@@ -11,6 +11,9 @@ // 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. + +#![allow(missing_docs, unused_results, clippy::unwrap_used)] + use criterion::{black_box, criterion_group, criterion_main, Criterion}; use handle_map::*; use std::sync::Arc; @@ -45,7 +48,7 @@ let handle = handle_map.allocate(|| 0xFF).unwrap(); let handle_map_ref = &handle_map; // Perform repeated reads - c.bench_function("single-threaded reads", |b| { + let _ = c.bench_function("single-threaded reads", |b| { b.iter(|| { let guard = handle_map_ref.get(black_box(handle)).unwrap(); black_box(*guard)
diff --git a/nearby/util/handle_map/src/declare_handle_map.rs b/nearby/util/handle_map/src/declare_handle_map.rs index dbdc77f..c19a012 100644 --- a/nearby/util/handle_map/src/declare_handle_map.rs +++ b/nearby/util/handle_map/src/declare_handle_map.rs
@@ -131,6 +131,13 @@ } impl $crate::HandleLike for $handle_type_name { type Object = $wrapped_type; + fn try_allocate<E: core::fmt::Debug>( + initial_value_provider: impl FnOnce() -> Result<$wrapped_type, E>, + ) -> Result<Self, $crate::HandleMapTryAllocateError<E>> { + GLOBAL_HANDLE_MAP + .try_allocate(initial_value_provider) + .map(|derived_handle| Self { handle_id: derived_handle.get_id() }) + } fn allocate( initial_value_provider: impl FnOnce() -> $wrapped_type, ) -> Result<Self, $crate::HandleMapFullError> {
diff --git a/nearby/util/handle_map/src/guard.rs b/nearby/util/handle_map/src/guard.rs index 4a96bb5..0c8c9d7 100644 --- a/nearby/util/handle_map/src/guard.rs +++ b/nearby/util/handle_map/src/guard.rs
@@ -60,17 +60,13 @@ type Ret = T; fn map<'b>(&self, arg: &'b Self::Arg) -> &'b Self::Ret { - // We know that the entry exists, since we've locked the - // shard and already checked that it exists prior to - // handing out this new, mapped read-lock. - arg.get(&self.handle).unwrap() + #[allow(clippy::expect_used)] + arg.get(&self.handle).expect("We know that the entry exists, since we've locked the shard and already checked that it exists prior to handing out this new, mapped read-lock.") } fn map_mut<'b>(&self, arg: &'b mut Self::Arg) -> &'b mut Self::Ret { - // We know that the entry exists, since we've locked the - // shard and already checked that it exists prior to - // handing out this new, mapped read-lock. - arg.get_mut(&self.handle).unwrap() + #[allow(clippy::expect_used)] + arg.get_mut(&self.handle).expect("We know that the entry exists, since we've locked the shard and already checked that it exists prior to handing out this new, mapped read-lock.") } } @@ -134,10 +130,12 @@ type Ret = T; fn map<'b>(&self, arg: &'b Self::Arg) -> &'b Self::Ret { - arg.get(&self.handle).unwrap() + #[allow(clippy::expect_used)] + arg.get(&self.handle).expect("Caller must verify that provided hande exists") } fn map_mut<'b>(&self, arg: &'b mut Self::Arg) -> &'b mut Self::Ret { - arg.get_mut(&self.handle).unwrap() + #[allow(clippy::expect_used)] + arg.get_mut(&self.handle).expect("Caller must verify that provided hande exists") } }
diff --git a/nearby/util/handle_map/src/lib.rs b/nearby/util/handle_map/src/lib.rs index bb420e3..22009c1 100644 --- a/nearby/util/handle_map/src/lib.rs +++ b/nearby/util/handle_map/src/lib.rs
@@ -15,9 +15,7 @@ //! A thread-safe implementation of a map for managing object handles, //! a safer alternative to raw pointers for FFI interop. -#![forbid(unsafe_code)] -#![deny(missing_docs)] - +use core::fmt::Debug; use std::boxed::Box; use std::sync::atomic::{AtomicU32, AtomicU64, Ordering}; use std::vec::Vec; @@ -84,6 +82,17 @@ #[derive(Debug)] pub struct HandleNotPresentError; +/// Errors which may be raised while attempting to allocate +/// a handle from contents given by a (fallible) value-provider. +#[derive(Debug)] +pub enum HandleMapTryAllocateError<E: Debug> { + /// The call to the value-provider for the allocation failed. + ValueProviderFailed(E), + /// We couldn't reserve a spot for the allocation, because + /// the handle-map was full. + HandleMapFull, +} + /// FFI-transmissible structure expressing the dimensions /// (max # of allocatable slots, number of shards) of a handle-map /// to be used upon initialization. @@ -142,10 +151,35 @@ /// may fail if attempting to allocate over the `dimensions.max_active_handles` /// limit imposed on the handle-map, in which case this method /// will return a `HandleMapFullError`. + /// + /// If you want the passed closure to be able to possibly fail, see + /// [`Self::try_allocate`] instead. pub fn allocate( &self, initial_value_provider: impl FnOnce() -> T, ) -> Result<Handle, HandleMapFullError> { + let wrapped_value_provider = move || Ok(initial_value_provider()); + self.try_allocate::<core::convert::Infallible>(wrapped_value_provider).map_err( + |e| match e { + HandleMapTryAllocateError::ValueProviderFailed(never) => match never {}, + HandleMapTryAllocateError::HandleMapFull => HandleMapFullError, + }, + ) + } + + /// Attempts to allocate a new object within the given handle-map, returning + /// a handle to the location it was stored at. This operation + /// may fail if attempting to allocate over the `dimensions.max_active_handles` + /// limit imposed on the handle-map, in which case this method + /// will return a `HandleMapTryAllocateError::HandleMapFull`, + /// or if the passed initial-value provider fails, in which case this + /// will return the error wrapped in `HandleMapTryAllocateError::ValueProviderFailed`. + /// + /// If your initial-value provider is infallible, see [`Self::allocate`] instead. + pub fn try_allocate<E: Debug>( + &self, + initial_value_provider: impl FnOnce() -> Result<T, E>, + ) -> Result<Handle, HandleMapTryAllocateError<E>> { let mut initial_value_provider = initial_value_provider; loop { // Increment the new-handle-ID counter using relaxed memory ordering, @@ -156,18 +190,26 @@ let shard_index = new_handle.get_shard_index(self.dimensions.num_shards); // Now, check the shard to see if we can actually allocate into it. - let shard_allocate_result = self.handle_map_shards[shard_index].try_allocate( - new_handle, - initial_value_provider, - &self.outstanding_allocations_counter, - self.dimensions.max_active_handles, - ); + #[allow(clippy::expect_used)] + let shard_allocate_result = self + .handle_map_shards + .get(shard_index) + .expect("Shard index is always within range") + .try_allocate( + new_handle, + initial_value_provider, + &self.outstanding_allocations_counter, + self.dimensions.max_active_handles, + ); match shard_allocate_result { Ok(_) => { return Ok(new_handle); } + Err(ShardAllocationError::ValueProviderFailed(e)) => { + return Err(HandleMapTryAllocateError::ValueProviderFailed(e)) + } Err(ShardAllocationError::ExceedsAllocationLimit) => { - return Err(HandleMapFullError); + return Err(HandleMapTryAllocateError::HandleMapFull); } Err(ShardAllocationError::EntryOccupied(thrown_back_provider)) => { // We need to do the whole thing again with a new ID @@ -181,7 +223,11 @@ /// if the given handle is present. Otherwise, returns [`HandleNotPresentError`]. pub fn get(&self, handle: Handle) -> Result<ObjectReadGuardImpl<T>, HandleNotPresentError> { let shard_index = handle.get_shard_index(self.dimensions.num_shards); - self.handle_map_shards[shard_index].get(handle) + #[allow(clippy::expect_used)] + self.handle_map_shards + .get(shard_index) + .expect("shard index is always within range") + .get(handle) } /// Gets a read+write reference to an object within the given handle-map, @@ -191,7 +237,11 @@ handle: Handle, ) -> Result<ObjectReadWriteGuardImpl<T>, HandleNotPresentError> { let shard_index = handle.get_shard_index(self.dimensions.num_shards); - self.handle_map_shards[shard_index].get_mut(handle) + #[allow(clippy::expect_used)] + self.handle_map_shards + .get(shard_index) + .expect("shard_index is always in range") + .get_mut(handle) } /// Removes the object pointed to by the given handle in @@ -199,7 +249,10 @@ /// exists. Otherwise, returns [`HandleNotPresentError`]. pub fn deallocate(&self, handle: Handle) -> Result<T, HandleNotPresentError> { let shard_index = handle.get_shard_index(self.dimensions.num_shards); - self.handle_map_shards[shard_index] + #[allow(clippy::expect_used)] + self.handle_map_shards + .get(shard_index) + .expect("shard index is always in range") .deallocate(handle, &self.outstanding_allocations_counter) } @@ -224,9 +277,16 @@ /// The underlying object type pointed-to by this handle type Object: Send + Sync; - /// Tries to allocate a new handle using the given provider - /// to construct the underlying stored object as a new - /// entry into the global handle table for this type. + /// Tries to allocate a new handle using the given (fallible) + /// provider to construct the underlying stored object as + /// a new entry into the global handle table for this type. + fn try_allocate<E: Debug>( + initial_value_provider: impl FnOnce() -> Result<Self::Object, E>, + ) -> Result<Self, HandleMapTryAllocateError<E>>; + + /// Tries to allocate a new handle using the given (infallible) + /// provider to construct the underlying stored object as + /// a new entry into the global handle table for this type. fn allocate( initial_value_provider: impl FnOnce() -> Self::Object, ) -> Result<Self, HandleMapFullError>;
diff --git a/nearby/util/handle_map/src/shard.rs b/nearby/util/handle_map/src/shard.rs index da3bd57..e5aad2f 100644 --- a/nearby/util/handle_map/src/shard.rs +++ b/nearby/util/handle_map/src/shard.rs
@@ -33,13 +33,15 @@ type ShardReadWriteGuard<'a, T> = RwLockWriteGuard<'a, ShardMapType<T>>; /// Internal error enum for failed allocations into a given shard. -pub(crate) enum ShardAllocationError<T, F: FnOnce() -> T> { +pub(crate) enum ShardAllocationError<T, E, F: FnOnce() -> Result<T, E>> { /// Error for when the entry for the handle is occupied, /// in which case we spit out the object-provider to try again /// with a new handle-id. EntryOccupied(F), /// Error for when we would exceed the maximum number of allocations. ExceedsAllocationLimit, + /// Error for when the initial value-provider call failed. + ValueProviderFailed(E), } /// An individual handle-map shard, which is ultimately @@ -116,7 +118,11 @@ // outstanding upgradeable guard on the shard. See `spin` documentation. // Remove the pointed-to object from the map, and return it, // releasing the lock when the guard goes out of scope. - let removed_object = map_read_write_guard.deref_mut().remove(&handle).unwrap(); + #[allow(clippy::expect_used)] + let removed_object = map_read_write_guard + .deref_mut() + .remove(&handle) + .expect("existence of handle is checked above"); // Decrement the allocations counter. Release ordering because we want // to ensure that clearing the map entry never gets re-ordered to after when // this counter gets decremented. @@ -124,15 +130,15 @@ Ok(removed_object) } - pub fn try_allocate<F>( + pub fn try_allocate<E, F>( &self, handle: Handle, object_provider: F, outstanding_allocations_counter: &AtomicU32, max_active_handles: u32, - ) -> Result<(), ShardAllocationError<T, F>> + ) -> Result<(), ShardAllocationError<T, E, F>> where - F: FnOnce() -> T, + F: FnOnce() -> Result<T, E>, { let mut read_write_guard = self.data.write(); match read_write_guard.entry(handle) { @@ -161,10 +167,17 @@ ); match allocation_count_bump_result { Ok(_) => { - // We're good to actually allocate - let object = object_provider(); - vacant_entry.insert(object); - Ok(()) + // We're good to actually allocate, + // so attempt to call the value-provider. + match object_provider() { + Ok(object) => { + // Successfully obtained the initial value, + // so insert it into the vacant entry. + let _ = vacant_entry.insert(object); + Ok(()) + } + Err(e) => Err(ShardAllocationError::ValueProviderFailed(e)), + } } Err(_) => { // The allocation would cause us to exceed the allowed allocations,
diff --git a/nearby/util/handle_map/src/tests.rs b/nearby/util/handle_map/src/tests.rs index a0774d6..90e773a 100644 --- a/nearby/util/handle_map/src/tests.rs +++ b/nearby/util/handle_map/src/tests.rs
@@ -11,6 +11,9 @@ // 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. + +#![allow(clippy::unwrap_used, clippy::expect_used)] + use crate::*; use core::ops::{Deref, DerefMut}; @@ -111,7 +114,7 @@ let test_fn = Arc::new(move || { let allocation_result = handle_map_function_ref.allocate(|| 0xFF); if let Ok(handle) = allocation_result { - handle_map_function_ref.deallocate(handle).unwrap(); + let _ = handle_map_function_ref.deallocate(handle).unwrap(); } }); test_for_each_thread(test_fn, num_repetitions_per_thread); @@ -123,7 +126,7 @@ assert_eq!((MAX_ACTIVE_HANDLES - 1) as usize, actual_num_active_handles); //Verify that we still have space for one more entry after all that. - handle_map_post_function_ref.allocate(|| 0xEE).unwrap(); + let _ = handle_map_post_function_ref.allocate(|| 0xEE).unwrap(); } /// Tests the progress of allocate/read/write/read/deallocate @@ -219,7 +222,7 @@ let mut handle_map = build_handle_map::<u8>(); for _ in 0..(num_repetitions_per_thread * NUM_ACTIVE_THREADS) { let handle = handle_map.allocate(|| 0xFF).expect("Initial allocations shouldn't fail"); - all_handles.insert(handle); + let _ = all_handles.insert(handle); } // Reset the new-handle-id counter handle_map.set_new_handle_id_counter(0); @@ -254,8 +257,9 @@ fn test_id_wraparound() { let mut handle_map = build_handle_map::<u8>(); handle_map.set_new_handle_id_counter(u64::MAX); - handle_map.allocate(|| 0xAB).expect("Counter wrap-around allocation should not fail"); - handle_map.allocate(|| 0xCD).expect("Post-counter-wrap-around allocation should not fail"); + let _ = handle_map.allocate(|| 0xAB).expect("Counter wrap-around allocation should not fail"); + let _ = + handle_map.allocate(|| 0xCD).expect("Post-counter-wrap-around allocation should not fail"); } #[test]
diff --git a/nearby/util/lock_adapter/Cargo.toml b/nearby/util/lock_adapter/Cargo.toml index eae1fc9..6e3174b 100644 --- a/nearby/util/lock_adapter/Cargo.toml +++ b/nearby/util/lock_adapter/Cargo.toml
@@ -4,6 +4,9 @@ edition.workspace = true publish.workspace = true +[lints] +workspace = true + [dependencies] spin = { workspace = true, optional = true }
diff --git a/nearby/util/lock_adapter/src/lib.rs b/nearby/util/lock_adapter/src/lib.rs index 3d75c0d..6bf6f0f 100644 --- a/nearby/util/lock_adapter/src/lib.rs +++ b/nearby/util/lock_adapter/src/lib.rs
@@ -15,8 +15,6 @@ //! An abstraction layer for Rust synchronization primitives which provides both no_std and std library //! based implementations -#![forbid(unsafe_code)] -#![deny(missing_docs)] #![cfg_attr(not(feature = "std"), no_std)] /// A Spinlock-based implementation of Mutex using the `spin` crate that can be used in `no_std`