aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAndroid Build Coastguard Worker <android-build-coastguard-worker@google.com>2024-02-02 23:56:40 +0000
committerAndroid Build Coastguard Worker <android-build-coastguard-worker@google.com>2024-02-02 23:56:40 +0000
commite51d81bbdaeeed4543c7ef218b9bb34eeb754da5 (patch)
tree130a48b0d282dd0c937e3a60bbc796ba57d12073
parenta0ad49e1207936fb7dd1e6477f9d6f3e7bb9737a (diff)
parent45cc9975196e343faec1c131b490acb797af39d3 (diff)
downloadbeto-rust-simpleperf-release.tar.gz
Snap for 11400057 from 45cc9975196e343faec1c131b490acb797af39d3 to simpleperf-releasesimpleperf-release
Change-Id: I3154c5242809c26f0daa8d40710845c7b72bbfe9
-rw-r--r--Dockerfile3
-rw-r--r--cmd-runner/Cargo.lock30
-rw-r--r--cmd-runner/Cargo.toml11
-rw-r--r--cmd-runner/src/lib.rs (renamed from nearby/src/support.rs)26
-rw-r--r--nearby/.cargo/config-boringssl.toml6
-rw-r--r--nearby/.gitignore1
-rw-r--r--nearby/Android.bp36
-rw-r--r--nearby/Cargo.lock1507
-rw-r--r--nearby/Cargo.toml110
-rw-r--r--nearby/cargo2android.json8
-rw-r--r--nearby/connections/ukey2/lock_adapter/src/std.rs37
-rw-r--r--nearby/connections/ukey2/ukey2/Cargo.toml18
-rw-r--r--nearby/connections/ukey2/ukey2/src/lib.rs9
-rw-r--r--nearby/connections/ukey2/ukey2/src/proto_adapter.rs41
-rw-r--r--nearby/connections/ukey2/ukey2/src/state_machine.rs3
-rw-r--r--nearby/connections/ukey2/ukey2/src/tests.rs74
-rw-r--r--nearby/connections/ukey2/ukey2/src/ukey2_handshake.rs26
-rw-r--r--nearby/connections/ukey2/ukey2/tests/tests.rs44
-rw-r--r--nearby/connections/ukey2/ukey2_c_ffi/Cargo.toml15
-rw-r--r--nearby/connections/ukey2/ukey2_c_ffi/cpp/CMakeLists.txt11
-rw-r--r--nearby/connections/ukey2/ukey2_c_ffi/cpp/ukey2_ffi.h1
-rw-r--r--nearby/connections/ukey2/ukey2_c_ffi/cpp/ukey2_test.cc5
-rw-r--r--nearby/connections/ukey2/ukey2_c_ffi/src/lib.rs8
-rw-r--r--nearby/connections/ukey2/ukey2_connections/Cargo.toml20
-rw-r--r--nearby/connections/ukey2/ukey2_connections/benches/ukey2_benches.rs18
-rw-r--r--nearby/connections/ukey2/ukey2_connections/fuzz/Cargo.lock315
-rw-r--r--nearby/connections/ukey2/ukey2_connections/src/lib.rs4
-rw-r--r--nearby/connections/ukey2/ukey2_connections/src/tests.rs193
-rw-r--r--nearby/connections/ukey2/ukey2_jni/Cargo.toml16
-rw-r--r--nearby/connections/ukey2/ukey2_jni/java/build.gradle.kts5
-rw-r--r--nearby/connections/ukey2/ukey2_jni/java/src/jmh/java/com/google/security/cryptauth/lib/securegcm/Ukey2Benchmark.java83
-rw-r--r--nearby/connections/ukey2/ukey2_jni/java/src/jmh/java/com/google/security/cryptauth/lib/securegcm/ukey2/Ukey2Benchmark.java78
-rw-r--r--nearby/connections/ukey2/ukey2_jni/java/src/main/java/com/google/security/cryptauth/lib/securegcm/D2DConnectionContextV1.java123
-rw-r--r--nearby/connections/ukey2/ukey2_jni/java/src/main/java/com/google/security/cryptauth/lib/securegcm/D2DHandshakeContext.java122
-rw-r--r--nearby/connections/ukey2/ukey2_jni/java/src/main/java/com/google/security/cryptauth/lib/securegcm/ukey2/BadHandleException.java (renamed from nearby/connections/ukey2/ukey2_jni/java/src/main/java/com/google/security/cryptauth/lib/securegcm/BadHandleException.java)7
-rw-r--r--nearby/connections/ukey2/ukey2_jni/java/src/main/java/com/google/security/cryptauth/lib/securegcm/ukey2/CryptoException.java (renamed from nearby/connections/ukey2/ukey2_jni/java/src/main/java/com/google/security/cryptauth/lib/securegcm/CryptoException.java)8
-rw-r--r--nearby/connections/ukey2/ukey2_jni/java/src/main/java/com/google/security/cryptauth/lib/securegcm/ukey2/D2DConnectionContextV1.java139
-rw-r--r--nearby/connections/ukey2/ukey2_jni/java/src/main/java/com/google/security/cryptauth/lib/securegcm/ukey2/D2DHandshakeContext.java129
-rw-r--r--nearby/connections/ukey2/ukey2_jni/java/src/main/java/com/google/security/cryptauth/lib/securegcm/ukey2/HandshakeException.java (renamed from nearby/connections/ukey2/ukey2_jni/java/src/main/java/com/google/security/cryptauth/lib/securegcm/HandshakeException.java)26
-rw-r--r--nearby/connections/ukey2/ukey2_jni/java/src/main/java/com/google/security/cryptauth/lib/securegcm/ukey2/SessionRestoreException.java (renamed from nearby/connections/ukey2/ukey2_jni/java/src/main/java/com/google/security/cryptauth/lib/securegcm/SessionRestoreException.java)8
-rw-r--r--nearby/connections/ukey2/ukey2_jni/java/src/test/java/com/google/security/cryptauth/lib/securegcm/TestUkey2Protocol.kt182
-rw-r--r--nearby/connections/ukey2/ukey2_jni/java/src/test/java/com/google/security/cryptauth/lib/securegcm/ukey2/TestUkey2Protocol.kt167
-rw-r--r--nearby/connections/ukey2/ukey2_jni/src/lib.rs166
-rw-r--r--nearby/connections/ukey2/ukey2_proto/src/ukey2_all_proto/device_to_device_messages.rs2
-rw-r--r--nearby/connections/ukey2/ukey2_proto/src/ukey2_all_proto/securegcm.rs2
-rw-r--r--nearby/connections/ukey2/ukey2_proto/src/ukey2_all_proto/securemessage.rs2
-rw-r--r--nearby/connections/ukey2/ukey2_proto/src/ukey2_all_proto/ukey.rs2
-rw-r--r--nearby/connections/ukey2/ukey2_shell/Cargo.toml6
-rw-r--r--nearby/connections/ukey2/ukey2_shell/src/main.rs10
-rw-r--r--nearby/crypto/crypto_provider/Cargo.toml9
-rw-r--r--nearby/crypto/crypto_provider/benches/constant_time_eq_bench.rs4
-rw-r--r--nearby/crypto/crypto_provider/benches/hkdf_bench.rs17
-rw-r--r--nearby/crypto/crypto_provider/benches/hmac_bench.rs6
-rw-r--r--nearby/crypto/crypto_provider/src/aead.rs86
-rw-r--r--nearby/crypto/crypto_provider/src/aead/mod.rs50
-rw-r--r--nearby/crypto/crypto_provider/src/aes/cbc.rs104
-rw-r--r--nearby/crypto/crypto_provider/src/aes/ctr.rs6
-rw-r--r--nearby/crypto/crypto_provider/src/aes/mod.rs1
-rw-r--r--nearby/crypto/crypto_provider/src/ed25519.rs83
-rw-r--r--nearby/crypto/crypto_provider/src/elliptic_curve.rs14
-rw-r--r--nearby/crypto/crypto_provider/src/lib.rs16
-rw-r--r--nearby/crypto/crypto_provider/src/p256.rs31
-rw-r--r--nearby/crypto/crypto_provider_boringssl/.cargo/config.toml3
-rw-r--r--nearby/crypto/crypto_provider_boringssl/Cargo.lock175
-rw-r--r--nearby/crypto/crypto_provider_boringssl/Cargo.toml5
-rw-r--r--nearby/crypto/crypto_provider_boringssl/src/aead/aes_gcm.rs89
-rw-r--r--nearby/crypto/crypto_provider_boringssl/src/aead/aes_gcm_siv.rs88
-rw-r--r--nearby/crypto/crypto_provider_boringssl/src/aead/mod.rs (renamed from nearby/crypto/bssl-crypto/src/lib.rs)6
-rw-r--r--nearby/crypto/crypto_provider_boringssl/src/aes/cbc.rs81
-rw-r--r--nearby/crypto/crypto_provider_boringssl/src/aes/ctr.rs74
-rw-r--r--nearby/crypto/crypto_provider_boringssl/src/aes/mod.rs (renamed from nearby/crypto/crypto_provider_boringssl/src/aes.rs)6
-rw-r--r--nearby/crypto/crypto_provider_boringssl/src/ed25519.rs7
-rw-r--r--nearby/crypto/crypto_provider_boringssl/src/lib.rs62
-rw-r--r--nearby/crypto/crypto_provider_boringssl/src/p256.rs117
-rw-r--r--nearby/crypto/crypto_provider_boringssl/src/sha2.rs35
-rw-r--r--nearby/crypto/crypto_provider_boringssl/src/x25519.rs102
-rw-r--r--nearby/crypto/crypto_provider_default/Cargo.toml3
-rw-r--r--nearby/crypto/crypto_provider_default/src/lib.rs2
-rw-r--r--nearby/crypto/crypto_provider_openssl/Cargo.toml3
-rw-r--r--nearby/crypto/crypto_provider_openssl/src/aes.rs80
-rw-r--r--nearby/crypto/crypto_provider_openssl/src/ed25519.rs7
-rw-r--r--nearby/crypto/crypto_provider_openssl/src/hkdf_openssl.rs4
-rw-r--r--nearby/crypto/crypto_provider_openssl/src/hmac_boringssl.rs2
-rw-r--r--nearby/crypto/crypto_provider_openssl/src/lib.rs9
-rw-r--r--nearby/crypto/crypto_provider_openssl/src/p256.rs54
-rw-r--r--nearby/crypto/crypto_provider_openssl/src/sha2.rs6
-rw-r--r--nearby/crypto/crypto_provider_openssl/src/x25519.rs10
-rw-r--r--nearby/crypto/crypto_provider_rustcrypto/Cargo.toml27
-rw-r--r--nearby/crypto/crypto_provider_rustcrypto/src/aead/aes_gcm.rs123
-rw-r--r--nearby/crypto/crypto_provider_rustcrypto/src/aead/aes_gcm_siv.rs124
-rw-r--r--nearby/crypto/crypto_provider_rustcrypto/src/aead/mod.rs2
-rw-r--r--nearby/crypto/crypto_provider_rustcrypto/src/aes/cbc.rs53
-rw-r--r--nearby/crypto/crypto_provider_rustcrypto/src/aes_cp/cbc.rs102
-rw-r--r--nearby/crypto/crypto_provider_rustcrypto/src/aes_cp/ctr.rs (renamed from nearby/crypto/crypto_provider_rustcrypto/src/aes/ctr.rs)12
-rw-r--r--nearby/crypto/crypto_provider_rustcrypto/src/aes_cp/mod.rs (renamed from nearby/crypto/crypto_provider_rustcrypto/src/aes/mod.rs)1
-rw-r--r--nearby/crypto/crypto_provider_rustcrypto/src/ed25519.rs8
-rw-r--r--nearby/crypto/crypto_provider_rustcrypto/src/hkdf_cp.rs (renamed from nearby/crypto/crypto_provider_rustcrypto/src/hkdf_rc.rs)13
-rw-r--r--nearby/crypto/crypto_provider_rustcrypto/src/hmac_cp.rs (renamed from nearby/crypto/crypto_provider_rustcrypto/src/hmac_rc.rs)39
-rw-r--r--nearby/crypto/crypto_provider_rustcrypto/src/lib.rs52
-rw-r--r--nearby/crypto/crypto_provider_rustcrypto/src/p256.rs23
-rw-r--r--nearby/crypto/crypto_provider_rustcrypto/src/sha2_cp.rs (renamed from nearby/crypto/crypto_provider_rustcrypto/src/sha2_rc.rs)0
-rw-r--r--nearby/crypto/crypto_provider_rustcrypto/src/x25519.rs13
-rw-r--r--nearby/crypto/crypto_provider_stubs/Cargo.toml2
-rw-r--r--nearby/crypto/crypto_provider_stubs/src/lib.rs123
-rw-r--r--nearby/crypto/crypto_provider_test/Cargo.toml4
-rw-r--r--nearby/crypto/crypto_provider_test/fuzz/Cargo.lock196
-rw-r--r--nearby/crypto/crypto_provider_test/src/aead/aes_gcm.rs316
-rw-r--r--nearby/crypto/crypto_provider_test/src/aead/aes_gcm_siv.rs247
-rw-r--r--nearby/crypto/crypto_provider_test/src/aead/mod.rs3
-rw-r--r--nearby/crypto/crypto_provider_test/src/aes/cbc.rs98
-rw-r--r--nearby/crypto/crypto_provider_test/src/aes/ctr.rs32
-rw-r--r--nearby/crypto/crypto_provider_test/src/ed25519.rs33
-rw-r--r--nearby/crypto/crypto_provider_test/src/lib.rs2
-rw-r--r--nearby/crypto/crypto_provider_test/src/p256.rs93
-rw-r--r--nearby/crypto/crypto_provider_test/src/sha2.rs2
-rw-r--r--nearby/crypto/crypto_provider_test/src/testdata/EdDSA/rfc_test_vectors.txt (renamed from nearby/crypto/crypto_provider_test/src/testdata/ecdsa/rfc_test_vectors.txt)0
-rw-r--r--nearby/crypto/crypto_provider_test/src/x25519.rs18
-rw-r--r--nearby/crypto/rand_core_05_adapter/Cargo.toml5
-rw-r--r--nearby/crypto/rand_core_05_adapter/src/lib.rs9
-rw-r--r--nearby/presence/CMakeLists.txt28
-rw-r--r--nearby/presence/array_ref/Cargo.toml4
-rw-r--r--nearby/presence/array_ref/README.md4
-rw-r--r--nearby/presence/array_ref/src/lib.rs2
-rw-r--r--nearby/presence/array_view/Cargo.toml8
-rw-r--r--nearby/presence/array_view/src/lib.rs8
-rw-r--r--nearby/presence/handle_map/src/lib.rs608
-rw-r--r--nearby/presence/ldt/Cargo.toml7
-rw-r--r--nearby/presence/ldt/benches/ldt_scan.rs2
-rw-r--r--nearby/presence/ldt/examples/gen_ldt_xor_pad_test_vectors.rs4
-rw-r--r--nearby/presence/ldt/examples/ldt_benchmark.rs2
-rw-r--r--nearby/presence/ldt/examples/ldt_prp.rs3
-rw-r--r--nearby/presence/ldt/fuzz/Cargo.lock50
-rw-r--r--nearby/presence/ldt/src/lib.rs44
-rw-r--r--nearby/presence/ldt/tests/ldt_roundtrip.rs2
-rw-r--r--nearby/presence/ldt/tests/ldt_test_vectors.rs6
-rw-r--r--nearby/presence/ldt/tests/tests.rs5
-rw-r--r--nearby/presence/ldt_np_adv/Cargo.toml7
-rw-r--r--nearby/presence/ldt_np_adv/benches/ldt_adv_scan.rs8
-rw-r--r--nearby/presence/ldt_np_adv/fuzz/Cargo.lock50
-rw-r--r--nearby/presence/ldt_np_adv/src/lib.rs33
-rw-r--r--nearby/presence/ldt_np_adv/src/np_adv_test_vectors.rs2
-rw-r--r--nearby/presence/ldt_np_adv_ffi/.cargo/config-boringssl.toml3
-rw-r--r--nearby/presence/ldt_np_adv_ffi/Cargo.lock75
-rw-r--r--nearby/presence/ldt_np_adv_ffi/src/lib.rs9
-rw-r--r--nearby/presence/ldt_np_c_sample/CMakeLists.txt1
-rw-r--r--nearby/presence/ldt_np_c_sample/main.c4
-rw-r--r--nearby/presence/ldt_np_c_sample/tests/CMakeLists.txt2
-rw-r--r--nearby/presence/ldt_np_c_sample/tests/ldt_benchmarks.cc5
-rw-r--r--nearby/presence/ldt_np_c_sample/tests/ldt_ffi_tests.cc6
-rw-r--r--nearby/presence/ldt_np_jni/Cargo.toml5
-rw-r--r--nearby/presence/ldt_np_jni/java/LdtNpJni/AndroidManifest.xml21
-rw-r--r--nearby/presence/ldt_np_jni/java/LdtNpJni/build.gradle.kts27
-rw-r--r--nearby/presence/ldt_np_jni/java/LdtNpJni/src/main/java/com/google/android/gms/nearby/presence/hazmat/LdtNpCipher.java162
-rw-r--r--nearby/presence/ldt_np_jni/java/LdtNpJni/src/main/java/com/google/android/gms/nearby/presence/hazmat/LdtNpJni.java88
-rw-r--r--nearby/presence/ldt_np_jni/java/LdtNpJni/src/main/java/com/google/android/gms/nearby/presence/hazmat/NpLdtCipher.kt210
-rw-r--r--nearby/presence/ldt_np_jni/java/LdtNpJni/src/test/java/com/google/android/gms/nearby/presence/hazmat/LdtNpJniTests.kt142
-rw-r--r--nearby/presence/ldt_np_jni/src/lib.rs362
-rw-r--r--nearby/presence/ldt_tbc/Cargo.toml3
-rw-r--r--nearby/presence/ldt_tbc/src/lib.rs8
-rw-r--r--nearby/presence/np_adv/Cargo.toml29
-rw-r--r--nearby/presence/np_adv/benches/deser_adv.rs360
-rw-r--r--nearby/presence/np_adv/src/array_vec.rs205
-rw-r--r--nearby/presence/np_adv/src/credential/book.rs544
-rw-r--r--nearby/presence/np_adv/src/credential/mod.rs395
-rw-r--r--nearby/presence/np_adv/src/credential/simple.rs148
-rw-r--r--nearby/presence/np_adv/src/credential/source.rs216
-rw-r--r--nearby/presence/np_adv/src/credential/tests.rs208
-rw-r--r--nearby/presence/np_adv/src/credential/v0.rs120
-rw-r--r--nearby/presence/np_adv/src/credential/v1.rs399
-rw-r--r--nearby/presence/np_adv/src/deser_v0_tests.rs122
-rw-r--r--nearby/presence/np_adv/src/deser_v1_tests.rs180
-rw-r--r--nearby/presence/np_adv/src/deserialization_arena.rs119
-rw-r--r--nearby/presence/np_adv/src/extended/data_elements/mod.rs4
-rw-r--r--nearby/presence/np_adv/src/extended/data_elements/tests.rs2
-rw-r--r--nearby/presence/np_adv/src/extended/de_type.rs8
-rw-r--r--nearby/presence/np_adv/src/extended/deserialize/encrypted_section/mic_decrypt_tests.rs179
-rw-r--r--nearby/presence/np_adv/src/extended/deserialize/encrypted_section/mod.rs135
-rw-r--r--nearby/presence/np_adv/src/extended/deserialize/encrypted_section/signature_decrypt_tests.rs183
-rw-r--r--nearby/presence/np_adv/src/extended/deserialize/mod.rs404
-rw-r--r--nearby/presence/np_adv/src/extended/deserialize/parse_tests.rs278
-rw-r--r--nearby/presence/np_adv/src/extended/deserialize/section_tests.rs465
-rw-r--r--nearby/presence/np_adv/src/extended/deserialize/test_stubs.rs82
-rw-r--r--nearby/presence/np_adv/src/extended/mod.rs6
-rw-r--r--nearby/presence/np_adv/src/extended/section_signature_payload.rs9
-rw-r--r--nearby/presence/np_adv/src/extended/serialize/adv_tests.rs2
-rw-r--r--nearby/presence/np_adv/src/extended/serialize/de_header_tests.rs2
-rw-r--r--nearby/presence/np_adv/src/extended/serialize/mod.rs291
-rw-r--r--nearby/presence/np_adv/src/extended/serialize/section_tests.rs168
-rw-r--r--nearby/presence/np_adv/src/extended/serialize/test_vectors.rs28
-rw-r--r--nearby/presence/np_adv/src/filter/mod.rs296
-rw-r--r--nearby/presence/np_adv/src/filter/tests/actions_filter_tests.rs138
-rw-r--r--nearby/presence/np_adv/src/filter/tests/data_elements_filter_tests.rs107
-rw-r--r--nearby/presence/np_adv/src/filter/tests/mod.rs107
-rw-r--r--nearby/presence/np_adv/src/filter/tests/v0_filter_tests.rs221
-rw-r--r--nearby/presence/np_adv/src/header_parse_tests.rs2
-rw-r--r--nearby/presence/np_adv/src/legacy/actions/macros.rs19
-rw-r--r--nearby/presence/np_adv/src/legacy/actions/mod.rs47
-rw-r--r--nearby/presence/np_adv/src/legacy/actions/tests.rs32
-rw-r--r--nearby/presence/np_adv/src/legacy/data_elements.rs36
-rw-r--r--nearby/presence/np_adv/src/legacy/de_type/mod.rs1
-rw-r--r--nearby/presence/np_adv/src/legacy/de_type/tests.rs2
-rw-r--r--nearby/presence/np_adv/src/legacy/deserialize/mod.rs333
-rw-r--r--nearby/presence/np_adv/src/legacy/deserialize/tests.rs260
-rw-r--r--nearby/presence/np_adv/src/legacy/mod.rs23
-rw-r--r--nearby/presence/np_adv/src/legacy/random_data_elements.rs7
-rw-r--r--nearby/presence/np_adv/src/legacy/serialize/mod.rs44
-rw-r--r--nearby/presence/np_adv/src/legacy/serialize/tests.rs31
-rw-r--r--nearby/presence/np_adv/src/lib.rs487
-rw-r--r--nearby/presence/np_adv/tests/examples_v0.rs116
-rw-r--r--nearby/presence/np_adv/tests/examples_v1.rs133
-rw-r--r--nearby/presence/np_adv_dynamic/Cargo.toml15
-rw-r--r--nearby/presence/np_adv_dynamic/src/extended.rs350
-rw-r--r--nearby/presence/np_adv_dynamic/src/legacy.rs349
-rw-r--r--nearby/presence/np_adv_dynamic/src/lib.rs21
-rw-r--r--nearby/presence/np_c_ffi/Cargo.lock458
-rw-r--r--nearby/presence/np_c_ffi/Cargo.toml12
-rw-r--r--nearby/presence/np_c_ffi/deny.toml213
-rw-r--r--nearby/presence/np_c_ffi/include/c/np_c_ffi.h601
-rw-r--r--nearby/presence/np_c_ffi/include/cpp/np_cpp_ffi_functions.h127
-rw-r--r--nearby/presence/np_c_ffi/include/cpp/np_cpp_ffi_types.h320
-rw-r--r--nearby/presence/np_c_ffi/src/credentials.rs173
-rw-r--r--nearby/presence/np_c_ffi/src/deserialize/v0.rs52
-rw-r--r--nearby/presence/np_c_ffi/src/deserialize/v1.rs68
-rw-r--r--nearby/presence/np_c_ffi/src/lib.rs56
-rw-r--r--nearby/presence/np_cpp_ffi/benchmarks/np_ffi_bench.cc15
-rw-r--r--nearby/presence/np_cpp_ffi/fuzz/fuzzer_np_cpp_deserialize.cc10
-rw-r--r--nearby/presence/np_cpp_ffi/fuzz/fuzzer_np_cpp_valid_header.cc10
-rw-r--r--nearby/presence/np_cpp_ffi/include/nearby_protocol.h173
-rw-r--r--nearby/presence/np_cpp_ffi/nearby_protocol.cc380
-rw-r--r--nearby/presence/np_cpp_ffi/sample/CMakeLists.txt2
-rw-r--r--nearby/presence/np_cpp_ffi/sample/main.cc (renamed from nearby/presence/np_cpp_ffi/sample/main.cpp)27
-rw-r--r--nearby/presence/np_cpp_ffi/shared/shared_test_util.cc5
-rw-r--r--nearby/presence/np_cpp_ffi/tests/CMakeLists.txt14
-rw-r--r--nearby/presence/np_cpp_ffi/tests/byte_buffer_tests.cc22
-rw-r--r--nearby/presence/np_cpp_ffi/tests/credential_book_tests.cc53
-rw-r--r--nearby/presence/np_cpp_ffi/tests/credential_slab_tests.cc226
-rw-r--r--nearby/presence/np_cpp_ffi/tests/deserialize_result_tests.cc139
-rw-r--r--nearby/presence/np_cpp_ffi/tests/global_config_tests.cc205
-rw-r--r--nearby/presence/np_cpp_ffi/tests/np_cpp_test.cc (renamed from nearby/crypto/crypto_provider/src/aead/aes_gcm_siv.rs)10
-rw-r--r--nearby/presence/np_cpp_ffi/tests/np_cpp_test.h42
-rw-r--r--nearby/presence/np_cpp_ffi/tests/v0_private_identity_tests.cc211
-rw-r--r--nearby/presence/np_cpp_ffi/tests/v0_public_identity_tests.cc (renamed from nearby/presence/np_cpp_ffi/tests/deserialize_v0_tests.cc)252
-rw-r--r--nearby/presence/np_cpp_ffi/tests/v1_private_identity_tests.cc130
-rw-r--r--nearby/presence/np_cpp_ffi/tests/v1_public_identity_tests.cc (renamed from nearby/presence/np_cpp_ffi/tests/deserialize_v1_tests.cc)37
-rw-r--r--nearby/presence/np_ed25519/Cargo.toml5
-rw-r--r--nearby/presence/np_ed25519/src/lib.rs44
-rw-r--r--nearby/presence/np_ffi_core/Cargo.toml19
-rw-r--r--nearby/presence/np_ffi_core/src/common.rs155
-rw-r--r--nearby/presence/np_ffi_core/src/credentials.rs376
-rw-r--r--nearby/presence/np_ffi_core/src/deserialize/mod.rs158
-rw-r--r--nearby/presence/np_ffi_core/src/deserialize/v0.rs298
-rw-r--r--nearby/presence/np_ffi_core/src/deserialize/v1.rs409
-rw-r--r--nearby/presence/np_ffi_core/src/lib.rs16
-rw-r--r--nearby/presence/np_ffi_core/src/serialize/mod.rs38
-rw-r--r--nearby/presence/np_ffi_core/src/serialize/v1.rs589
-rw-r--r--nearby/presence/np_ffi_core/src/utils.rs1
-rw-r--r--nearby/presence/np_ffi_core/src/v1.rs38
-rw-r--r--nearby/presence/np_hkdf/Cargo.toml3
-rw-r--r--nearby/presence/np_hkdf/benches/np_hkdf.rs2
-rw-r--r--nearby/presence/np_hkdf/src/lib.rs12
-rw-r--r--nearby/presence/np_hkdf/src/v1_salt.rs10
-rw-r--r--nearby/presence/np_hkdf/tests/test_vectors.rs4
-rw-r--r--nearby/presence/rand_ext/Cargo.toml3
-rw-r--r--nearby/presence/rand_ext/src/lib.rs2
-rw-r--r--nearby/presence/sink/Cargo.toml3
-rw-r--r--nearby/presence/sink/src/lib.rs9
-rw-r--r--nearby/presence/test_helper/Cargo.toml3
-rw-r--r--nearby/presence/test_helper/src/lib.rs10
-rw-r--r--nearby/presence/xts_aes/Cargo.toml6
-rw-r--r--nearby/presence/xts_aes/fuzz/Cargo.lock51
-rw-r--r--nearby/presence/xts_aes/src/lib.rs168
-rw-r--r--nearby/presence/xts_aes/tests/compare_with_xts_mode_test.rs2
-rw-r--r--nearby/presence/xts_aes/tests/tests.rs117
-rw-r--r--nearby/presence/xts_aes/tests/wycheproof_test_vectors.rs77
-rw-r--r--nearby/presence/xts_aes/tests/xts_nist_test_vectors.rs4
-rw-r--r--nearby/presence/xts_aes/tests/xts_roundtrip_tests.rs2
-rw-r--r--nearby/rustfmt.toml2
-rw-r--r--nearby/scripts/openssl-patches/0001-Apply-Android-changes.patch2712
-rw-r--r--nearby/scripts/openssl-patches/0001-Apply-android-patches.patch1017
-rw-r--r--nearby/scripts/openssl-patches/0002-fix-boringssl-dsa-build-errors.patch25
-rw-r--r--nearby/src/crypto_ffi.rs66
-rw-r--r--nearby/src/ffi.rs132
-rw-r--r--nearby/src/file_header/license.rs45
-rw-r--r--nearby/src/file_header/mod.rs559
-rw-r--r--nearby/src/fuzzers.rs2
-rw-r--r--nearby/src/jni.rs13
-rw-r--r--nearby/src/license.rs20
-rw-r--r--nearby/src/main.rs104
-rw-r--r--nearby/src/ukey2.rs24
-rw-r--r--nearby/util/handle_map/Cargo.toml (renamed from nearby/presence/handle_map/Cargo.toml)10
-rw-r--r--nearby/util/handle_map/benches/benches.rs (renamed from nearby/presence/handle_map/benches/benches.rs)5
-rw-r--r--nearby/util/handle_map/src/declare_handle_map.rs168
-rw-r--r--nearby/util/handle_map/src/guard.rs141
-rw-r--r--nearby/util/handle_map/src/lib.rs302
-rw-r--r--nearby/util/handle_map/src/shard.rs198
-rw-r--r--nearby/util/handle_map/src/tests.rs (renamed from nearby/presence/handle_map/src/tests.rs)17
-rw-r--r--nearby/util/lock_adapter/Cargo.toml (renamed from nearby/connections/ukey2/lock_adapter/Cargo.toml)7
-rw-r--r--nearby/util/lock_adapter/src/lib.rs (renamed from nearby/connections/ukey2/lock_adapter/src/lib.rs)23
-rw-r--r--nearby/util/lock_adapter/src/spin.rs60
-rw-r--r--nearby/util/lock_adapter/src/std.rs175
-rw-r--r--remoteauth/Cargo.lock423
-rw-r--r--remoteauth/Cargo.toml17
-rw-r--r--remoteauth/ctap_protocol/src/command.rs227
-rw-r--r--remoteauth/ctap_protocol/src/lib.rs72
-rw-r--r--remoteauth/deny.toml219
-rw-r--r--remoteauth/platform/Cargo.toml2
-rw-r--r--remoteauth/platform/src/lib.rs16
-rw-r--r--remoteauth/platform/src/listeners.rs (renamed from nearby/connections/ukey2/lock_adapter/src/spin.rs)22
-rw-r--r--remoteauth/remote_auth_protocol/Cargo.toml (renamed from nearby/crypto/bssl-crypto/Cargo.toml)3
-rw-r--r--remoteauth/remote_auth_protocol/src/lib.rs (renamed from remoteauth/remote_auth_protool/src/lib.rs)2
-rw-r--r--remoteauth/remote_auth_protocol/src/remote_auth_service.rs (renamed from remoteauth/remote_auth_protool/src/remote_auth_service.rs)18
-rw-r--r--remoteauth/remote_auth_protool/Cargo.toml12
-rw-r--r--remoteauth/src/ctap_protocol.rs29
-rw-r--r--remoteauth/src/main.rs103
-rw-r--r--remoteauth/src/platform.rs29
-rw-r--r--remoteauth/src/remote_auth_protocol.rs29
316 files changed, 20152 insertions, 11158 deletions
diff --git a/Dockerfile b/Dockerfile
index 00dffc7..1f0462e 100644
--- a/Dockerfile
+++ b/Dockerfile
@@ -21,7 +21,7 @@ protobuf-compiler pkg-config libdbus-1-dev libssl-dev ninja-build
RUN apt upgrade -y
# install cargo with default settings
-RUN curl https://sh.rustup.rs -sSf | sh -s -- -y --default-toolchain 1.68.1
+RUN curl https://sh.rustup.rs -sSf | sh -s -- -y --default-toolchain 1.72.0
ENV PATH="/root/.cargo/bin:${PATH}"
RUN cargo install --locked cargo-deny --color never 2>&1
RUN cargo install cargo-fuzz --color never 2>&1
@@ -36,6 +36,7 @@ RUN cargo install cargo-prefetch \
RUN cargo install bindgen-cli --version 0.64.0
RUN cargo install wasm-pack --color never 2>&1
RUN rustup toolchain add nightly
+RUN rustup target add wasm32-unknown-unknown
# boringssl build wants go
RUN curl -L https://go.dev/dl/go1.20.2.linux-amd64.tar.gz | tar -C /usr/local -xz
ENV PATH="$PATH:/usr/local/go/bin"
diff --git a/cmd-runner/Cargo.lock b/cmd-runner/Cargo.lock
new file mode 100644
index 0000000..4dadf2e
--- /dev/null
+++ b/cmd-runner/Cargo.lock
@@ -0,0 +1,30 @@
+# This file is automatically @generated by Cargo.
+# It is not intended for manual editing.
+version = 3
+
+[[package]]
+name = "anyhow"
+version = "1.0.75"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "a4668cab20f66d8d020e1fbc0ebe47217433c1b6c8f2040faf858554e394ace6"
+
+[[package]]
+name = "cmd-runner"
+version = "0.1.0"
+dependencies = [
+ "anyhow",
+ "owo-colors",
+ "shell-escape",
+]
+
+[[package]]
+name = "owo-colors"
+version = "3.5.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "c1b04fb49957986fdce4d6ee7a65027d55d4b6d2265e5848bbb507b58ccfdb6f"
+
+[[package]]
+name = "shell-escape"
+version = "0.1.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "45bb67a18fa91266cc7807181f62f9178a6873bfad7dc788c42e6430db40184f"
diff --git a/cmd-runner/Cargo.toml b/cmd-runner/Cargo.toml
new file mode 100644
index 0000000..e79317e
--- /dev/null
+++ b/cmd-runner/Cargo.toml
@@ -0,0 +1,11 @@
+[package]
+name = "cmd-runner"
+version = "0.1.0"
+edition = "2021"
+publish = false
+
+[dependencies]
+anyhow = "1.0.64"
+shell-escape = "0.1.5"
+owo-colors = "3.5.0"
+
diff --git a/nearby/src/support.rs b/cmd-runner/src/lib.rs
index cc81352..fa2f1f4 100644
--- a/nearby/src/support.rs
+++ b/cmd-runner/src/lib.rs
@@ -33,7 +33,12 @@ pub fn run_cmd_shell_with_color<C: TermColors>(
dir: &path::Path,
cmd: impl AsRef<ffi::OsStr>,
) -> anyhow::Result<SuccessOutput> {
- run::<C>(dir, process::Command::new("sh").current_dir(dir).args(["-c".as_ref(), cmd.as_ref()]))
+ run::<C>(
+ dir,
+ process::Command::new("sh")
+ .current_dir(dir)
+ .args(["-c".as_ref(), cmd.as_ref()]),
+ )
}
/// Run a cmd with explicit args directly without a shell.
@@ -53,7 +58,12 @@ where
A: Clone + IntoIterator<Item = S>,
S: AsRef<ffi::OsStr>,
{
- run::<C>(dir, process::Command::new(cmd.as_ref()).current_dir(dir).args(args))
+ run::<C>(
+ dir,
+ process::Command::new(cmd.as_ref())
+ .current_dir(dir)
+ .args(args),
+ )
}
/// Run the specified command.
@@ -73,8 +83,16 @@ fn run<C: TermColors>(
},
);
- let context = format!("{} [{}]", cmd_with_args.to_string_lossy(), dir.to_string_lossy(),);
- println!("[{}] [{}]", cmd_with_args.to_string_lossy().green(), dir.to_string_lossy().blue());
+ let context = format!(
+ "{} [{}]",
+ cmd_with_args.to_string_lossy(),
+ dir.to_string_lossy(),
+ );
+ println!(
+ "[{}] [{}]",
+ cmd_with_args.to_string_lossy().green(),
+ dir.to_string_lossy().blue()
+ );
let mut child = command
.env_clear()
diff --git a/nearby/.cargo/config-boringssl.toml b/nearby/.cargo/config-boringssl.toml
index bab3518..2bde6df 100644
--- a/nearby/.cargo/config-boringssl.toml
+++ b/nearby/.cargo/config-boringssl.toml
@@ -1,13 +1,9 @@
# The packages to override
paths = [
- "../boringssl-build/boringssl/rust/bssl-crypto",
- "../boringssl-build/boringssl/rust/bssl-sys",
+ "../third_party/boringssl/rust/bssl-sys",
"../boringssl-build/rust-openssl/openssl",
"../boringssl-build/rust-openssl/openssl-sys",
]
[env]
WORKSPACE_DIR = { value = "", relative = true }
-
-
-
diff --git a/nearby/.gitignore b/nearby/.gitignore
index b7d2235..49cb45e 100644
--- a/nearby/.gitignore
+++ b/nearby/.gitignore
@@ -3,3 +3,4 @@ target/
/*.mdb
/auth_token.txt
.DS_Store
+presence/cmake-build-debug
diff --git a/nearby/Android.bp b/nearby/Android.bp
index 548f005..1739424 100644
--- a/nearby/Android.bp
+++ b/nearby/Android.bp
@@ -26,6 +26,7 @@ rust_library_rlib {
rustlibs: [
"libhex",
"librand",
+ "libtinyvec",
],
apex_available: [
"//apex_available:platform",
@@ -56,7 +57,7 @@ rust_library_rlib {
crate_name: "lock_adapter",
cargo_env_compat: true,
cargo_pkg_version: "0.1.0",
- srcs: ["connections/ukey2/lock_adapter/src/lib.rs"],
+ srcs: ["util/lock_adapter/src/lib.rs"],
edition: "2021",
features: [
"std",
@@ -68,20 +69,38 @@ rust_library_rlib {
}
rust_library_rlib {
- name: "libcrypto_provider_openssl",
+ name: "libcrypto_provider_default",
host_supported: true,
- crate_name: "crypto_provider_openssl",
+ crate_name: "crypto_provider_default",
cargo_env_compat: true,
cargo_pkg_version: "0.1.0",
- cfgs: ["soong"],
- srcs: ["crypto/crypto_provider_openssl/src/lib.rs"],
+ srcs: ["crypto/crypto_provider_default/src/lib.rs"],
edition: "2021",
features: ["boringssl"],
rustlibs: [
"libcfg_if",
"libcrypto_provider",
+ "libcrypto_provider_boringssl",
+ ],
+ apex_available: [
+ "//apex_available:platform",
+ "//apex_available:anyapex",
+ ],
+}
+
+rust_library_rlib {
+ name: "libcrypto_provider_boringssl",
+ host_supported: true,
+ crate_name: "crypto_provider_boringssl",
+ cargo_env_compat: true,
+ cargo_pkg_version: "0.1.0",
+ srcs: ["crypto/crypto_provider_boringssl/src/lib.rs"],
+ edition: "2021",
+ rustlibs: [
+ "libcfg_if",
+ "libcrypto_provider",
"libcrypto_provider_stubs",
- "libopenssl",
+ "libbssl_crypto",
"librand",
],
apex_available: [
@@ -105,7 +124,7 @@ rust_ffi_shared {
],
rlibs: [
"libcfg_if",
- "libcrypto_provider_openssl",
+ "libcrypto_provider_default",
"liblazy_static",
"liblock_adapter",
"liblog_rust",
@@ -162,7 +181,7 @@ rust_ffi_shared {
// common projects like libjni and libprotobuf.
rlibs: [
"libcfg_if",
- "libcrypto_provider_openssl",
+ "libcrypto_provider_default",
"libjni",
"liblazy_static",
"liblock_adapter",
@@ -213,7 +232,6 @@ rust_library_rlib {
"librand",
"libukey2_proto",
],
- proc_macros: ["libderive_getters"],
apex_available: [
"//apex_available:platform",
"//apex_available:anyapex",
diff --git a/nearby/Cargo.lock b/nearby/Cargo.lock
index 76582bf..3a3934a 100644
--- a/nearby/Cargo.lock
+++ b/nearby/Cargo.lock
@@ -3,15 +3,6 @@
version = 3
[[package]]
-name = "addr2line"
-version = "0.20.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "f4fa78e18c64fce05e902adecd7a5eed15a5e0a3439f7b0e169f0252214865e3"
-dependencies = [
- "gimli",
-]
-
-[[package]]
name = "adler"
version = "1.0.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -40,36 +31,39 @@ dependencies = [
]
[[package]]
-name = "aes-gcm-siv"
-version = "0.11.1"
+name = "aes-gcm"
+version = "0.10.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "ae0784134ba9375416d469ec31e7c5f9fa94405049cf08c5ce5b4698be673e0d"
+checksum = "831010a0f742e1209b3bcea8fab6a8e149051ba6099432c8cb2cc117dec3ead1"
dependencies = [
"aead",
"aes",
"cipher",
"ctr",
- "polyval",
+ "ghash",
"subtle",
- "zeroize",
]
[[package]]
-name = "ahash"
-version = "0.8.3"
+name = "aes-gcm-siv"
+version = "0.11.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "2c99f64d1e06488f620f932677e24bc6e2897582980441ae90a671415bd7ec2f"
+checksum = "ae0784134ba9375416d469ec31e7c5f9fa94405049cf08c5ce5b4698be673e0d"
dependencies = [
- "cfg-if",
- "once_cell",
- "version_check",
+ "aead",
+ "aes",
+ "cipher",
+ "ctr",
+ "polyval",
+ "subtle",
+ "zeroize",
]
[[package]]
name = "aho-corasick"
-version = "1.0.2"
+version = "1.1.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "43f6cb1bf222025340178f382c426f13757b2960e89779dfcb319c32542a5a41"
+checksum = "b2969dcb958b36655471fc61f7e416fa76033bdd4bfed0678d8fee1e2d07a1f0"
dependencies = [
"memchr",
]
@@ -81,12 +75,6 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "250f629c0161ad8107cf89319e990051fae62832fd343083bea452d93e2205fd"
[[package]]
-name = "allocator-api2"
-version = "0.2.16"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "0942ffc6dcaadf03badf6e6a2d0228460359d5e34b57ccdc720b7382dfbd5ec5"
-
-[[package]]
name = "android-tzdata"
version = "0.1.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -109,58 +97,57 @@ checksum = "4b46cbb362ab8752921c97e041f5e366ee6297bd428a31275b9fcf1e380f7299"
[[package]]
name = "anstream"
-version = "0.3.2"
+version = "0.6.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "0ca84f3628370c59db74ee214b3263d58f9aadd9b4fe7e711fd87dc452b7f163"
+checksum = "d664a92ecae85fd0a7392615844904654d1d5f5514837f471ddef4a057aba1b6"
dependencies = [
"anstyle",
"anstyle-parse",
"anstyle-query",
"anstyle-wincon",
"colorchoice",
- "is-terminal",
"utf8parse",
]
[[package]]
name = "anstyle"
-version = "1.0.1"
+version = "1.0.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "3a30da5c5f2d5e72842e00bcb57657162cdabef0931f40e2deb9b4140440cecd"
+checksum = "7079075b41f533b8c61d2a4d073c4676e1f8b249ff94a393b0595db304e0dd87"
[[package]]
name = "anstyle-parse"
-version = "0.2.1"
+version = "0.2.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "938874ff5980b03a87c5524b3ae5b59cf99b1d6bc836848df7bc5ada9643c333"
+checksum = "c75ac65da39e5fe5ab759307499ddad880d724eed2f6ce5b5e8a26f4f387928c"
dependencies = [
"utf8parse",
]
[[package]]
name = "anstyle-query"
-version = "1.0.0"
+version = "1.0.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "5ca11d4be1bab0c8bc8734a9aa7bf4ee8316d462a08c6ac5052f888fef5b494b"
+checksum = "e28923312444cdd728e4738b3f9c9cac739500909bb3d3c94b43551b16517648"
dependencies = [
- "windows-sys 0.48.0",
+ "windows-sys 0.52.0",
]
[[package]]
name = "anstyle-wincon"
-version = "1.0.1"
+version = "3.0.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "180abfa45703aebe0093f79badacc01b8fd4ea2e35118747e5811127f926e188"
+checksum = "1cd54b81ec8d6180e24654d0b371ad22fc3dd083b6ff8ba325b72e00c87660a7"
dependencies = [
"anstyle",
- "windows-sys 0.48.0",
+ "windows-sys 0.52.0",
]
[[package]]
name = "anyhow"
-version = "1.0.72"
+version = "1.0.79"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "3b13c32d80ecc7ab747b80c3784bce54ee8a7a0cc4fbda9bf4cda2cf6fe90854"
+checksum = "080e9890a082662b09c1ad45f567faeeb47f22b5fb23895fbe1e651e718e25ca"
[[package]]
name = "array_ref"
@@ -171,38 +158,12 @@ name = "array_view"
version = "0.1.0"
[[package]]
-name = "atty"
-version = "0.2.14"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "d9b39be18770d11421cdb1b9947a45dd3f37e93092cbf377614828a319d5fee8"
-dependencies = [
- "hermit-abi 0.1.19",
- "libc",
- "winapi",
-]
-
-[[package]]
name = "autocfg"
version = "1.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa"
[[package]]
-name = "backtrace"
-version = "0.3.68"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "4319208da049c43661739c5fade2ba182f09d1dc2299b32298d3a31692b17e12"
-dependencies = [
- "addr2line",
- "cc",
- "cfg-if",
- "libc",
- "miniz_oxide",
- "object",
- "rustc-demangle",
-]
-
-[[package]]
name = "base16ct"
version = "0.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -210,15 +171,9 @@ checksum = "4c7f02d4ea65f2c1853089ffd8d2787bdbc63de2f0d29dedbcf8ccdfa0ccd4cf"
[[package]]
name = "base64"
-version = "0.13.1"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "9e1b586273c5702936fe7b7d6896644d8be71e6314cfe09d3167c95f712589e8"
-
-[[package]]
-name = "base64"
-version = "0.21.2"
+version = "0.21.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "604178f6c5c21f02dc555784810edfb88d34ac2c73b2eae109655649ee73ce3d"
+checksum = "c79fed4cdb43e993fcdadc7e58a09fd0e3e649c4436fa11da71c9f1f3ee7feb9"
[[package]]
name = "base64ct"
@@ -234,9 +189,9 @@ checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a"
[[package]]
name = "bitflags"
-version = "2.3.3"
+version = "2.4.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "630be753d4e58660abd17930c71b647fe46c27ea6b63cc59e1e3851406972e42"
+checksum = "327762f6e5a765692301e5bb513e0d9fef63be86bbc14528052b1cd3e6f03e07"
[[package]]
name = "blake2"
@@ -268,6 +223,13 @@ dependencies = [
[[package]]
name = "bssl-crypto"
version = "0.1.0"
+dependencies = [
+ "bssl-sys 0.1.0",
+]
+
+[[package]]
+name = "bssl-sys"
+version = "0.1.0"
[[package]]
name = "bssl-sys"
@@ -277,9 +239,9 @@ checksum = "312d12393c060384f2e6ed14c7b4be37b3dd90249857485613c1a91b9a1abb5c"
[[package]]
name = "bstr"
-version = "1.6.0"
+version = "1.9.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "6798148dccfbff0fae41c7574d2fa8f1ef3492fba0face179de5d8d447d67b05"
+checksum = "c48f0051a4b4c5e0b6d365cd04af53aeaa209e3cc15ec2cdb69e73cc87fbd0dc"
dependencies = [
"memchr",
"serde",
@@ -290,41 +252,42 @@ name = "build-scripts"
version = "0.1.0"
dependencies = [
"anyhow",
- "base64 0.21.2",
"chrono",
- "clap 4.3.19",
+ "clap",
+ "cmd-runner",
"crossbeam",
"env_logger",
+ "file-header",
"globset",
"log",
"owo-colors",
- "reqwest",
+ "regex",
"semver",
+ "serde_json",
"shell-escape",
"tempfile",
"thiserror",
- "tinytemplate",
"walkdir",
"which",
]
[[package]]
name = "bumpalo"
-version = "3.13.0"
+version = "3.14.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "a3e2c3daef883ecc1b5d58c15adae93470a91d425f3532ba1695849656af3fc1"
+checksum = "7f30e7476521f6f8af1a1c4c0b8cc94f0bee37d91763d0ca2665f299b6cd8aec"
[[package]]
name = "byteorder"
-version = "1.4.3"
+version = "1.5.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "14c189c53d098945499cdfa7ecc63567cf3886b3332b312a5b4585d8d3a6a610"
+checksum = "1fd0f2584146f6f2ef48085050886acf353beff7305ebd1ae69500e27c67f64b"
[[package]]
name = "bytes"
-version = "1.4.0"
+version = "1.5.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "89b2fd2a0dcf38d7971e2194b6b6eebab45ae01067456a7fd93d5547a61b70be"
+checksum = "a2bd12c1caf447e69cd4528f47f94d203fd2582878ecb9e9465484c4148a8223"
[[package]]
name = "cast"
@@ -343,9 +306,12 @@ dependencies = [
[[package]]
name = "cc"
-version = "1.0.79"
+version = "1.0.83"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "50d30906286121d95be3d479533b458f87493b30a4b5f79a607db8f5d11aa91f"
+checksum = "f1174fb0b6ec23863f8b971027804a42614e347eafb0a95bf0b12cdae21fc4d0"
+dependencies = [
+ "libc",
+]
[[package]]
name = "cesu8"
@@ -361,14 +327,14 @@ checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd"
[[package]]
name = "chrono"
-version = "0.4.26"
+version = "0.4.31"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "ec837a71355b28f6556dbd569b37b3f363091c0bd4b2e735674521b4c5fd9bc5"
+checksum = "7f2c685bad3eb3d45a01354cedb7d5faa66194d1d58ba6e267a8de788f79db38"
dependencies = [
"android-tzdata",
"iana-time-zone",
"num-traits",
- "winapi",
+ "windows-targets 0.48.5",
]
[[package]]
@@ -410,65 +376,52 @@ dependencies = [
[[package]]
name = "clap"
-version = "3.2.25"
+version = "4.4.14"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "4ea181bf566f71cb9a5d17a59e1871af638180a18fb0035c92ae62b705207123"
-dependencies = [
- "bitflags 1.3.2",
- "clap_lex 0.2.4",
- "indexmap",
- "textwrap",
-]
-
-[[package]]
-name = "clap"
-version = "4.3.19"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "5fd304a20bff958a57f04c4e96a2e7594cc4490a0e809cbd48bb6437edaa452d"
+checksum = "33e92c5c1a78c62968ec57dbc2440366a2d6e5a23faf829970ff1585dc6b18e2"
dependencies = [
"clap_builder",
"clap_derive",
- "once_cell",
]
[[package]]
name = "clap_builder"
-version = "4.3.19"
+version = "4.4.14"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "01c6a3f08f1fe5662a35cfe393aec09c4df95f60ee93b7556505260f75eee9e1"
+checksum = "f4323769dc8a61e2c39ad7dc26f6f2800524691a44d74fe3d1071a5c24db6370"
dependencies = [
"anstream",
"anstyle",
- "clap_lex 0.5.0",
+ "clap_lex",
"strsim",
]
[[package]]
name = "clap_derive"
-version = "4.3.12"
+version = "4.4.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "54a9bb5758fc5dfe728d1019941681eccaf0cf8a4189b692a0ee2f2ecf90a050"
+checksum = "cf9804afaaf59a91e75b022a30fb7229a7901f60c755489cc61c9b423b836442"
dependencies = [
"heck",
"proc-macro2",
"quote",
- "syn 2.0.28",
+ "syn 2.0.48",
]
[[package]]
name = "clap_lex"
-version = "0.2.4"
+version = "0.6.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "2850f2f5a82cbf437dd5af4d49848fbdfc27c157c3d010345776f952765261c5"
-dependencies = [
- "os_str_bytes",
-]
+checksum = "702fc72eb24e5a1e48ce58027a675bc24edd52096d5397d4aea7c6dd9eca0bd1"
[[package]]
-name = "clap_lex"
-version = "0.5.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "2da6da31387c7e4ef160ffab6d5e7f00c42626fe39aea70a7b0f1773f7dd6c1b"
+name = "cmd-runner"
+version = "0.1.0"
+dependencies = [
+ "anyhow",
+ "owo-colors",
+ "shell-escape",
+]
[[package]]
name = "colorchoice"
@@ -488,21 +441,21 @@ dependencies = [
[[package]]
name = "const-oid"
-version = "0.9.4"
+version = "0.9.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "795bc6e66a8e340f075fcf6227e417a2dc976b92b91f3cdc778bb858778b6747"
+checksum = "c2459377285ad874054d797f3ccebf984978aa39129f6eafde5cdc8315b612f8"
[[package]]
name = "core-foundation-sys"
-version = "0.8.4"
+version = "0.8.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "e496a50fda8aacccc86d7529e2c1e0892dbd0f898a6b5645b5561b89c3210efa"
+checksum = "06ea2b9bc92be3c2baa9334a323ebca2d6f074ff852cd1d7b11064035cd3868f"
[[package]]
name = "cpufeatures"
-version = "0.2.9"
+version = "0.2.12"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "a17b76ff3a4162b0b27f354a0c87015ddad39d35f9c0c36607a3bdd175dde1f1"
+checksum = "53fe5e26ff1b7aef8bca9c6080520cfb8d9333c7568e1829cef191a9723e5504"
dependencies = [
"libc",
]
@@ -518,19 +471,19 @@ dependencies = [
[[package]]
name = "criterion"
-version = "0.4.0"
+version = "0.5.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "e7c76e09c1aae2bc52b3d2f29e13c6572553b30c4aa1b8a49fd70de6412654cb"
+checksum = "f2b12d017a929603d80db1831cd3a24082f8137ce19c69e6447f54f5fc8d692f"
dependencies = [
"anes",
- "atty",
"cast",
"ciborium",
- "clap 3.2.25",
+ "clap",
"criterion-plot",
+ "is-terminal",
"itertools",
- "lazy_static",
"num-traits",
+ "once_cell",
"oorandom",
"plotters",
"rayon",
@@ -554,11 +507,10 @@ dependencies = [
[[package]]
name = "crossbeam"
-version = "0.8.2"
+version = "0.8.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "2801af0d36612ae591caa9568261fddce32ce6e08a7275ea334a06a4ad021a2c"
+checksum = "1137cd7e7fc0fb5d3c5a8678be38ec56e819125d8d7907411fe24ccb943faca8"
dependencies = [
- "cfg-if",
"crossbeam-channel",
"crossbeam-deque",
"crossbeam-epoch",
@@ -568,62 +520,52 @@ dependencies = [
[[package]]
name = "crossbeam-channel"
-version = "0.5.8"
+version = "0.5.11"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "a33c2bf77f2df06183c3aa30d1e96c0695a313d4f9c453cc3762a6db39f99200"
+checksum = "176dc175b78f56c0f321911d9c8eb2b77a78a4860b9c19db83835fea1a46649b"
dependencies = [
- "cfg-if",
"crossbeam-utils",
]
[[package]]
name = "crossbeam-deque"
-version = "0.8.3"
+version = "0.8.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "ce6fd6f855243022dcecf8702fef0c297d4338e226845fe067f6341ad9fa0cef"
+checksum = "613f8cc01fe9cf1a3eb3d7f488fd2fa8388403e97039e2f73692932e291a770d"
dependencies = [
- "cfg-if",
"crossbeam-epoch",
"crossbeam-utils",
]
[[package]]
name = "crossbeam-epoch"
-version = "0.9.15"
+version = "0.9.18"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "ae211234986c545741a7dc064309f67ee1e5ad243d0e48335adc0484d960bcc7"
+checksum = "5b82ac4a3c2ca9c3460964f020e1402edd5753411d7737aa39c3714ad1b5420e"
dependencies = [
- "autocfg",
- "cfg-if",
"crossbeam-utils",
- "memoffset",
- "scopeguard",
]
[[package]]
name = "crossbeam-queue"
-version = "0.3.8"
+version = "0.3.11"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "d1cfb3ea8a53f37c40dea2c7bedcbd88bdfae54f5e2175d6ecaff1c988353add"
+checksum = "df0346b5d5e76ac2fe4e327c5fd1118d6be7c51dfb18f9b7922923f287471e35"
dependencies = [
- "cfg-if",
"crossbeam-utils",
]
[[package]]
name = "crossbeam-utils"
-version = "0.8.16"
+version = "0.8.19"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "5a22b2d63d4d1dc0b7f1b6b2747dd0088008a9be28b6ddf0b1e7d335e3037294"
-dependencies = [
- "cfg-if",
-]
+checksum = "248e3bacc7dc6baa3b21e405ee045c3047101a49145e7e9eca583ab4c2ca5345"
[[package]]
name = "crypto-bigint"
-version = "0.5.2"
+version = "0.5.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "cf4c2f4e1afd912bc40bfd6fed5d9dc1f288e0ba01bfcc835cc5bc3eb13efe15"
+checksum = "0dc92fb57ca44df6db8059111ab3af99a63d5d0f8375d9972e319a379c6bab76"
dependencies = [
"generic-array",
"rand_core 0.6.4",
@@ -652,6 +594,7 @@ dependencies = [
"hex-literal",
"rand",
"rand_ext",
+ "tinyvec",
]
[[package]]
@@ -660,7 +603,6 @@ version = "0.1.0"
dependencies = [
"bssl-crypto",
"crypto_provider",
- "crypto_provider_stubs",
]
[[package]]
@@ -685,7 +627,7 @@ dependencies = [
"hex-literal",
"openssl",
"ouroboros",
- "rstest 0.17.0",
+ "rstest",
]
[[package]]
@@ -694,6 +636,7 @@ version = "0.1.0"
dependencies = [
"aead",
"aes",
+ "aes-gcm",
"aes-gcm-siv",
"cbc",
"cfg-if",
@@ -731,7 +674,7 @@ dependencies = [
"hex-literal",
"rand",
"rand_ext",
- "rstest 0.17.0",
+ "rstest",
"rstest_reuse",
"test_helper",
"wycheproof",
@@ -748,9 +691,9 @@ dependencies = [
[[package]]
name = "curve25519-dalek"
-version = "4.0.0-rc.3"
+version = "4.1.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "436ace70fc06e06f7f689d2624dc4e2f0ea666efb5aa704215f7249ae6e047a7"
+checksum = "e89b8c6a2e4b1f45971ad09761aafb85514a84744b67a95e32c3cc1352d1f65c"
dependencies = [
"cfg-if",
"cpufeatures",
@@ -765,37 +708,26 @@ dependencies = [
[[package]]
name = "curve25519-dalek-derive"
-version = "0.1.0"
+version = "0.1.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "83fdaf97f4804dcebfa5862639bc9ce4121e82140bec2a987ac5140294865b5b"
+checksum = "f46882e17999c6cc590af592290432be3bce0428cb0d5f8b6715e4dc7b383eb3"
dependencies = [
"proc-macro2",
"quote",
- "syn 2.0.28",
+ "syn 2.0.48",
]
[[package]]
name = "der"
-version = "0.7.7"
+version = "0.7.8"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "0c7ed52955ce76b1554f509074bb357d3fb8ac9b51288a65a3fd480d1dfba946"
+checksum = "fffa369a668c8af7dbf8b5e56c9f744fbd399949ed171606040001947de40b1c"
dependencies = [
"const-oid",
"zeroize",
]
[[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"
@@ -814,9 +746,9 @@ dependencies = [
[[package]]
name = "ed25519"
-version = "2.2.1"
+version = "2.2.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "5fb04eee5d9d907f29e80ee6b0e78f7e2c82342c63e3580d8c4f69d9d5aad963"
+checksum = "115531babc129696a58c64a4fef0a8bf9e9698629fb97e9e40767d235cfbcd53"
dependencies = [
"pkcs8",
"signature",
@@ -824,15 +756,16 @@ dependencies = [
[[package]]
name = "ed25519-dalek"
-version = "2.0.0-rc.3"
+version = "2.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "faa8e9049d5d72bfc12acbc05914731b5322f79b5e2f195e9f2d705fca22ab4c"
+checksum = "1f628eaec48bfd21b865dc2950cfa014450c01d2fa2b69a86c2fd5844ec523c0"
dependencies = [
"curve25519-dalek",
"ed25519",
"rand_core 0.6.4",
"serde",
"sha2",
+ "subtle",
"zeroize",
]
@@ -844,9 +777,9 @@ checksum = "a26ae43d7bcc3b814de94796a5e736d4029efb0ee900c12e2d54c993ad1a1e07"
[[package]]
name = "elliptic-curve"
-version = "0.13.5"
+version = "0.13.8"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "968405c8fdc9b3bf4df0a6638858cc0b52462836ab6b1c87377785dd09cf1c0b"
+checksum = "b5e6043086bf7973472e0c7dff2142ea0b680d30e18d9cc40f267efbf222bd47"
dependencies = [
"base16ct",
"crypto-bigint",
@@ -862,19 +795,10 @@ dependencies = [
]
[[package]]
-name = "encoding_rs"
-version = "0.8.32"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "071a31f4ee85403370b58aca746f01041ede6f0da2730960ad001edc2b71b394"
-dependencies = [
- "cfg-if",
-]
-
-[[package]]
name = "env_logger"
-version = "0.10.0"
+version = "0.10.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "85cdab6a89accf66733ad5a1693a4dcced6aeff64602b634530dd73c1f3ee9f0"
+checksum = "95b3f3e67048839cb0d0781f445682a35113da7121f7c949db0e2be96a4fbece"
dependencies = [
"humantime",
"is-terminal",
@@ -885,30 +809,19 @@ dependencies = [
[[package]]
name = "errno"
-version = "0.3.2"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "6b30f669a7961ef1631673d2766cc92f52d64f7ef354d4fe0ddfd30ed52f0f4f"
-dependencies = [
- "errno-dragonfly",
- "libc",
- "windows-sys 0.48.0",
-]
-
-[[package]]
-name = "errno-dragonfly"
-version = "0.1.2"
+version = "0.3.8"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "aa68f1b12764fab894d2755d2518754e71b4fd80ecfb822714a1206c2aab39bf"
+checksum = "a258e46cdc063eb8519c00b9fc845fc47bcfca4130e2f08e88665ceda8474245"
dependencies = [
- "cc",
"libc",
+ "windows-sys 0.52.0",
]
[[package]]
name = "fastrand"
-version = "2.0.0"
+version = "2.0.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "6999dc1837253364c2ebb0704ba97994bd874e8f195d665c50b7548f6ea92764"
+checksum = "25cbce373ec4653f1a01a31e8a5e5ec0c622dc27ff9c4e6606eefef5cbbed4a5"
[[package]]
name = "ff"
@@ -922,25 +835,32 @@ dependencies = [
[[package]]
name = "fiat-crypto"
-version = "0.1.20"
+version = "0.2.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "e825f6987101665dea6ec934c09ec6d721de7bc1bf92248e1d5810c8cd636b77"
+checksum = "27573eac26f4dd11e2b1916c3fe1baa56407c83c71a773a8ba17ec0bca03b6b7"
[[package]]
-name = "flate2"
-version = "1.0.26"
+name = "file-header"
+version = "0.1.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "3b9429470923de8e8cbd4d2dc513535400b4b3fef0319fb5c4e1f520a7bef743"
+checksum = "b5568149106e77ae33bc3a2c3ef3839cbe63ffa4a8dd4a81612a6f9dfdbc2e9f"
dependencies = [
- "crc32fast",
- "miniz_oxide",
+ "crossbeam",
+ "lazy_static",
+ "license",
+ "thiserror",
+ "walkdir",
]
[[package]]
-name = "fnv"
-version = "1.0.7"
+name = "flate2"
+version = "1.0.28"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1"
+checksum = "46303f565772937ffe1d394a4fac6f411c6013172fadde9dcdb1e147a086940e"
+dependencies = [
+ "crc32fast",
+ "miniz_oxide",
+]
[[package]]
name = "foreign-types"
@@ -958,110 +878,6 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "00b0228411908ca8685dba7fc2cdd70ec9990a6e753e89b6ac91a84c40fbaf4b"
[[package]]
-name = "form_urlencoded"
-version = "1.2.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "a62bc1cf6f830c2ec14a513a9fb124d0a213a629668a4186f329db21fe045652"
-dependencies = [
- "percent-encoding",
-]
-
-[[package]]
-name = "futures"
-version = "0.3.28"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "23342abe12aba583913b2e62f22225ff9c950774065e4bfb61a19cd9770fec40"
-dependencies = [
- "futures-channel",
- "futures-core",
- "futures-executor",
- "futures-io",
- "futures-sink",
- "futures-task",
- "futures-util",
-]
-
-[[package]]
-name = "futures-channel"
-version = "0.3.28"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "955518d47e09b25bbebc7a18df10b81f0c766eaf4c4f1cccef2fca5f2a4fb5f2"
-dependencies = [
- "futures-core",
- "futures-sink",
-]
-
-[[package]]
-name = "futures-core"
-version = "0.3.28"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "4bca583b7e26f571124fe5b7561d49cb2868d79116cfa0eefce955557c6fee8c"
-
-[[package]]
-name = "futures-executor"
-version = "0.3.28"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "ccecee823288125bd88b4d7f565c9e58e41858e47ab72e8ea2d64e93624386e0"
-dependencies = [
- "futures-core",
- "futures-task",
- "futures-util",
-]
-
-[[package]]
-name = "futures-io"
-version = "0.3.28"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "4fff74096e71ed47f8e023204cfd0aa1289cd54ae5430a9523be060cdb849964"
-
-[[package]]
-name = "futures-macro"
-version = "0.3.28"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "89ca545a94061b6365f2c7355b4b32bd20df3ff95f02da9329b34ccc3bd6ee72"
-dependencies = [
- "proc-macro2",
- "quote",
- "syn 2.0.28",
-]
-
-[[package]]
-name = "futures-sink"
-version = "0.3.28"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "f43be4fe21a13b9781a69afa4985b0f6ee0e1afab2c6f454a8cf30e2b2237b6e"
-
-[[package]]
-name = "futures-task"
-version = "0.3.28"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "76d3d132be6c0e6aa1534069c705a74a5997a356c0dc2f86a47765e5617c5b65"
-
-[[package]]
-name = "futures-timer"
-version = "3.0.2"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "e64b03909df88034c26dc1547e8970b91f98bdb65165d6a4e9110d94263dbb2c"
-
-[[package]]
-name = "futures-util"
-version = "0.3.28"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "26b01e40b772d54cf6c6d721c1d1abd0647a0106a12ecaa1c186273392a69533"
-dependencies = [
- "futures-channel",
- "futures-core",
- "futures-io",
- "futures-macro",
- "futures-sink",
- "futures-task",
- "memchr",
- "pin-project-lite",
- "pin-utils",
- "slab",
-]
-
-[[package]]
name = "generic-array"
version = "0.14.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -1074,9 +890,9 @@ dependencies = [
[[package]]
name = "getrandom"
-version = "0.2.10"
+version = "0.2.12"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "be4136b2a15dd319360be1c07d9933517ccf0be8f16bf62a3bee4f0d618df427"
+checksum = "190092ea657667030ac6a35e305e62fc4dd69fd98ac98631e5d3a2b1575a12b5"
dependencies = [
"cfg-if",
"libc",
@@ -1084,22 +900,32 @@ dependencies = [
]
[[package]]
-name = "gimli"
-version = "0.27.3"
+name = "ghash"
+version = "0.5.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "b6c80984affa11d98d1b88b66ac8853f143217b399d3c74116778ff8fdb4ed2e"
+checksum = "d930750de5717d2dd0b8c0d42c076c0e884c81a73e6cab859bbd2339c71e3e40"
+dependencies = [
+ "opaque-debug",
+ "polyval",
+]
+
+[[package]]
+name = "glob"
+version = "0.3.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d2fabcfbdc87f4758337ca535fb41a6d701b65693ce38287d856d1674551ec9b"
[[package]]
name = "globset"
-version = "0.4.12"
+version = "0.4.14"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "aca8bbd8e0707c1887a8bbb7e6b40e228f251ff5d62c8220a4a7a53c73aff006"
+checksum = "57da3b9b5b85bd66f31093f8c408b90a74431672542466497dcbdfdc02034be1"
dependencies = [
"aho-corasick",
"bstr",
- "fnv",
"log",
- "regex",
+ "regex-automata",
+ "regex-syntax",
]
[[package]]
@@ -1114,25 +940,6 @@ dependencies = [
]
[[package]]
-name = "h2"
-version = "0.3.20"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "97ec8491ebaf99c8eaa73058b045fe58073cd6be7f596ac993ced0b0a0c01049"
-dependencies = [
- "bytes",
- "fnv",
- "futures-core",
- "futures-sink",
- "futures-util",
- "http",
- "indexmap",
- "slab",
- "tokio",
- "tokio-util",
- "tracing",
-]
-
-[[package]]
name = "half"
version = "1.8.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -1143,11 +950,8 @@ name = "handle_map"
version = "0.1.0"
dependencies = [
"criterion",
- "crypto_provider",
- "hashbrown 0.14.0",
- "lock_api",
- "portable-atomic",
- "spin 0.9.8",
+ "lazy_static",
+ "lock_adapter",
]
[[package]]
@@ -1157,22 +961,12 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8a9ee70c43aaf417c914396645a0fa852624801b24ebb7ae78fe8272889ac888"
[[package]]
-name = "hashbrown"
-version = "0.14.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "2c6201b9ff9fd90a5a3bac2e56a830d0caa509576f0e503818ee82c181b3437a"
-dependencies = [
- "ahash",
- "allocator-api2",
-]
-
-[[package]]
name = "hdrhistogram"
-version = "7.5.2"
+version = "7.5.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "7f19b9f54f7c7f55e31401bb647626ce0cf0f67b0004982ce815b3ee72a02aa8"
+checksum = "765c9198f173dd59ce26ff9f95ef0aafd0a0fe01fb9d72841bc5066a4c06511d"
dependencies = [
- "base64 0.13.1",
+ "base64",
"byteorder",
"crossbeam-channel",
"flate2",
@@ -1188,18 +982,9 @@ checksum = "95505c38b4572b2d910cecb0281560f54b440a19336cbbcb27bf6ce6adc6f5a8"
[[package]]
name = "hermit-abi"
-version = "0.1.19"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "62b467343b94ba476dcb2500d242dadbb39557df889310ac77c5d99100aaac33"
-dependencies = [
- "libc",
-]
-
-[[package]]
-name = "hermit-abi"
-version = "0.3.2"
+version = "0.3.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "443144c8cdadd93ebf52ddb4056d257f5b52c04d3c804e657d19eb73fc33668b"
+checksum = "d77f7ec81a6d05a3abb01ab6eb7590f6083d08449fe5a1c8b1e620283546ccb7"
[[package]]
name = "hex"
@@ -1215,9 +1000,9 @@ checksum = "6fe2267d4ed49bc07b63801559be28c718ea06c4738b7a03c94df7386d2cde46"
[[package]]
name = "hkdf"
-version = "0.12.3"
+version = "0.12.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "791a029f6b9fc27657f6f188ec6e5e43f6911f6f878e0dc5501396e09809d437"
+checksum = "7b5f8eb2ad728638ea2c7d47a21db23b7b58a72ed6a38256b8a1849f15fbbdf7"
dependencies = [
"hmac",
]
@@ -1232,95 +1017,32 @@ dependencies = [
]
[[package]]
-name = "http"
-version = "0.2.9"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "bd6effc99afb63425aff9b05836f029929e345a6148a14b7ecd5ab67af944482"
-dependencies = [
- "bytes",
- "fnv",
- "itoa",
-]
-
-[[package]]
-name = "http-body"
-version = "0.4.5"
+name = "home"
+version = "0.5.9"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "d5f38f16d184e36f2408a55281cd658ecbd3ca05cce6d6510a176eca393e26d1"
+checksum = "e3d1354bf6b7235cb4a0576c2619fd4ed18183f689b12b006a0ee7329eeff9a5"
dependencies = [
- "bytes",
- "http",
- "pin-project-lite",
+ "windows-sys 0.52.0",
]
[[package]]
-name = "httparse"
-version = "1.8.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "d897f394bad6a705d5f4104762e116a75639e470d80901eed05a860a95cb1904"
-
-[[package]]
-name = "httpdate"
-version = "1.0.2"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "c4a1e36c821dbe04574f602848a19f742f4fb3c98d40449f11bcad18d6b17421"
-
-[[package]]
name = "humantime"
version = "2.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9a3a5bfb195931eeb336b2a7b4d761daec841b97f947d34394601737a7bba5e4"
[[package]]
-name = "hyper"
-version = "0.14.27"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "ffb1cfd654a8219eaef89881fdb3bb3b1cdc5fa75ded05d6933b2b382e395468"
-dependencies = [
- "bytes",
- "futures-channel",
- "futures-core",
- "futures-util",
- "h2",
- "http",
- "http-body",
- "httparse",
- "httpdate",
- "itoa",
- "pin-project-lite",
- "socket2",
- "tokio",
- "tower-service",
- "tracing",
- "want",
-]
-
-[[package]]
-name = "hyper-rustls"
-version = "0.24.1"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "8d78e1e73ec14cf7375674f74d7dde185c8206fd9dea6fb6295e8a98098aaa97"
-dependencies = [
- "futures-util",
- "http",
- "hyper",
- "rustls",
- "tokio",
- "tokio-rustls",
-]
-
-[[package]]
name = "iana-time-zone"
-version = "0.1.57"
+version = "0.1.59"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "2fad5b825842d2b38bd206f3e81d6957625fd7f0a361e345c30e01a0ae2dd613"
+checksum = "b6a67363e2aa4443928ce15e57ebae94fd8949958fd1223c4cfc0cd473ad7539"
dependencies = [
"android_system_properties",
"core-foundation-sys",
"iana-time-zone-haiku",
"js-sys",
"wasm-bindgen",
- "windows",
+ "windows-core",
]
[[package]]
@@ -1333,32 +1055,16 @@ dependencies = [
]
[[package]]
-name = "idna"
-version = "0.4.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "7d20d6b07bfbc108882d88ed8e37d39636dcc260e15e30c45e6ba089610b917c"
-dependencies = [
- "unicode-bidi",
- "unicode-normalization",
-]
-
-[[package]]
name = "indexmap"
version = "1.9.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bd070e393353796e801d209ad339e89596eb4c8d430d18ede6a1cced8fafbd99"
dependencies = [
"autocfg",
- "hashbrown 0.12.3",
+ "hashbrown",
]
[[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"
@@ -1369,20 +1075,14 @@ dependencies = [
]
[[package]]
-name = "ipnet"
-version = "2.8.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "28b29a3cd74f0f4598934efe3aeba42bae0eb4680554128851ebbecb02af14e6"
-
-[[package]]
name = "is-terminal"
-version = "0.4.9"
+version = "0.4.10"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "cb0889898416213fab133e1d33a0e5858a48177452750691bde3666d0fdbaf8b"
+checksum = "0bad00257d07be169d870ab665980b06cdb366d792ad690bf2e76876dc503455"
dependencies = [
- "hermit-abi 0.3.2",
+ "hermit-abi",
"rustix",
- "windows-sys 0.48.0",
+ "windows-sys 0.52.0",
]
[[package]]
@@ -1396,9 +1096,9 @@ dependencies = [
[[package]]
name = "itoa"
-version = "1.0.9"
+version = "1.0.10"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "af150ab688ff2122fcef229be89cb50dd66af9e01a4ff320cc137eecc9bacc38"
+checksum = "b1a46d1a171d865aa5f83f92695765caa047a9b4cbae2cbf37dbd613a793fd4c"
[[package]]
name = "jni"
@@ -1424,9 +1124,9 @@ checksum = "8eaf4bc02d17cbdd7ff4c7438cafcdf7fb9a4613313ad11b4f8fefe7d3fa0130"
[[package]]
name = "js-sys"
-version = "0.3.64"
+version = "0.3.66"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "c5f195fe497f702db0f318b07fdd68edb16955aed830df8363d837542f8f935a"
+checksum = "cee9c64da59eae3b50095c18d3e74f8b73c0b86d2792824ff01bbce68ba229ca"
dependencies = [
"wasm-bindgen",
]
@@ -1446,9 +1146,9 @@ version = "0.1.0"
dependencies = [
"aes",
"anyhow",
- "base64 0.21.2",
+ "base64",
"blake2",
- "clap 4.3.19",
+ "clap",
"criterion",
"crypto_provider",
"crypto_provider_default",
@@ -1473,7 +1173,7 @@ version = "0.1.0"
dependencies = [
"anyhow",
"array_view",
- "base64 0.21.2",
+ "base64",
"criterion",
"crypto_provider",
"crypto_provider_default",
@@ -1513,15 +1213,26 @@ dependencies = [
[[package]]
name = "libc"
-version = "0.2.147"
+version = "0.2.152"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "b4668fb0ea861c1df094127ac5f1da3409a82116a4ba74fca2e58ef927159bb3"
+checksum = "13e3bf6590cbc649f4d1a3eefc9d5d6eb746f5200ffb04e5e142700b8faa56e7"
+
+[[package]]
+name = "license"
+version = "3.2.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "778718185117620a06e95d2b1e57d50166b1d6bfad93c8abfc1b3344c863ad8c"
+dependencies = [
+ "reword",
+ "serde",
+ "serde_json",
+]
[[package]]
name = "linux-raw-sys"
-version = "0.4.3"
+version = "0.4.12"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "09fc20d2ca12cb9f044c93e3bd6d32d523e6e2ec3db4f7b2939cd99026ecd3f0"
+checksum = "c4cd1a83af159aa67994778be9070f0ae1bd732942279cabb14f86f986a21456"
[[package]]
name = "lock_adapter"
@@ -1532,9 +1243,9 @@ dependencies = [
[[package]]
name = "lock_api"
-version = "0.4.10"
+version = "0.4.11"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "c1cc9717a20b1bb222f333e6a92fd32f7d8a18ddc5a3191a11af45dcbf4dcd16"
+checksum = "3c168f8615b12bc01f9c17e2eb0cc07dcae1940121185446edc3744920e8ef45"
dependencies = [
"autocfg",
"scopeguard",
@@ -1542,30 +1253,15 @@ dependencies = [
[[package]]
name = "log"
-version = "0.4.19"
+version = "0.4.20"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "b06a4cde4c0f271a446782e3eff8de789548ce57dbc8eca9292c27f4a42004b4"
+checksum = "b5e6163cb8c49088c2c36f57875e58ccd8c87c7427f7fbd50ea6710b2f3f2e8f"
[[package]]
name = "memchr"
-version = "2.5.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "2dffe52ecf27772e601905b7522cb4ef790d2cc203488bbd0e2fe85fcb74566d"
-
-[[package]]
-name = "memoffset"
-version = "0.9.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "5a634b1c61a95585bd15607c6ab0c4e5b226e695ff2800ba0cdccddf208c406c"
-dependencies = [
- "autocfg",
-]
-
-[[package]]
-name = "mime"
-version = "0.3.17"
+version = "2.7.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "6877bb514081ee2a7ff5ef9de3281f14a4dd4bceac4c09388074a6b5df8a139a"
+checksum = "523dc4f511e55ab87b694dc30d0f820d60906ef06413f93d4d7a1385599cc149"
[[package]]
name = "minimal-lexical"
@@ -1583,17 +1279,6 @@ dependencies = [
]
[[package]]
-name = "mio"
-version = "0.8.8"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "927a765cd3fc26206e66b296465fa9d3e5ab003e651c1b3c060e7956d96b19d2"
-dependencies = [
- "libc",
- "wasi",
- "windows-sys 0.48.0",
-]
-
-[[package]]
name = "nom"
version = "7.1.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -1613,7 +1298,6 @@ dependencies = [
"crypto_provider",
"crypto_provider_default",
"hex",
- "init_with",
"lazy_static",
"ldt",
"ldt_np_adv",
@@ -1622,6 +1306,7 @@ dependencies = [
"np_hkdf",
"rand",
"rand_ext",
+ "serde",
"serde_json",
"sink",
"strum",
@@ -1632,6 +1317,17 @@ dependencies = [
]
[[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 = [
@@ -1649,8 +1345,12 @@ dependencies = [
"crypto_provider",
"crypto_provider_default",
"handle_map",
+ "lazy_static",
+ "ldt_np_adv",
+ "lock_adapter",
"np_adv",
- "spin 0.9.8",
+ "np_adv_dynamic",
+ "np_hkdf",
]
[[package]]
@@ -1672,9 +1372,9 @@ dependencies = [
[[package]]
name = "num-bigint"
-version = "0.4.3"
+version = "0.4.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "f93ab6289c7b344a8a9f60f88d80aa20032336fe78da341afc91c8a2341fc75f"
+checksum = "608e7659b5c3d7cba262d894801b9ec9d00de989e8a82bd4bef91d08da45cdc0"
dependencies = [
"autocfg",
"num-integer",
@@ -1693,37 +1393,18 @@ dependencies = [
[[package]]
name = "num-traits"
-version = "0.2.16"
+version = "0.2.17"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "f30b0abd723be7e2ffca1272140fac1a2f084c77ec3e123c192b66af1ee9e6c2"
+checksum = "39e3200413f237f41ab11ad6d161bc7239c84dcb631773ccd7de3dfe4b5c267c"
dependencies = [
"autocfg",
]
[[package]]
-name = "num_cpus"
-version = "1.16.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "4161fcb6d602d4d2081af7c3a45852d875a03dd337a6bfdd6e06407b61342a43"
-dependencies = [
- "hermit-abi 0.3.2",
- "libc",
-]
-
-[[package]]
-name = "object"
-version = "0.31.1"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "8bda667d9f2b5051b8833f59f3bf748b28ef54f850f4fcb389a252aa383866d1"
-dependencies = [
- "memchr",
-]
-
-[[package]]
name = "once_cell"
-version = "1.18.0"
+version = "1.19.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "dd8b5dd2ae5ed71462c540258bedcb51965123ad7e7ccf4b9a8cafaa4a63576d"
+checksum = "3fdb12b2476b595f9358c5161aa467c2438859caa136dec86c26fdd2efe17b92"
[[package]]
name = "oorandom"
@@ -1739,11 +1420,11 @@ checksum = "624a8340c38c1b80fd549087862da4ba43e08858af025b236e509b6649fc13d5"
[[package]]
name = "openssl"
-version = "0.10.55"
+version = "0.10.62"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "345df152bc43501c5eb9e4654ff05f794effb78d4efe3d53abc158baddc0703d"
+checksum = "8cde4d2d9200ad5909f8dac647e29482e07c3a35de8a13fce7c9c7747ad9f671"
dependencies = [
- "bitflags 1.3.2",
+ "bitflags 2.4.1",
"cfg-if",
"foreign-types",
"libc",
@@ -1760,16 +1441,16 @@ checksum = "a948666b637a0f465e8564c73e89d4dde00d72d4d473cc972f390fc3dcee7d9c"
dependencies = [
"proc-macro2",
"quote",
- "syn 2.0.28",
+ "syn 2.0.48",
]
[[package]]
name = "openssl-sys"
-version = "0.9.90"
+version = "0.9.98"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "374533b0e45f3a7ced10fcaeccca020e66656bc03dac384f852e4e5a7a8104a6"
+checksum = "c1665caf8ab2dc9aef43d1c0023bd904633a6a05cb30b0ad59bec2ae986e57a7"
dependencies = [
- "bssl-sys",
+ "bssl-sys 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)",
"cc",
"libc",
"pkg-config",
@@ -1777,12 +1458,6 @@ dependencies = [
]
[[package]]
-name = "os_str_bytes"
-version = "6.5.1"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "4d5d9eb14b174ee9aa2ef96dc2b94637a2d4b6e7cb873c7e171f0c20c6cf3eac"
-
-[[package]]
name = "ouroboros"
version = "0.17.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -1803,7 +1478,7 @@ dependencies = [
"proc-macro-error",
"proc-macro2",
"quote",
- "syn 2.0.28",
+ "syn 2.0.48",
]
[[package]]
@@ -1823,24 +1498,6 @@ dependencies = [
]
[[package]]
-name = "percent-encoding"
-version = "2.3.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "9b2a4787296e9989611394c33f193f676704af1686e70b8f8033ab5ba9a35a94"
-
-[[package]]
-name = "pin-project-lite"
-version = "0.2.10"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "4c40d25201921e5ff0c862a505c6557ea88568a4e3ace775ab55e93f2f4f9d57"
-
-[[package]]
-name = "pin-utils"
-version = "0.1.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184"
-
-[[package]]
name = "pkcs8"
version = "0.10.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -1852,15 +1509,15 @@ dependencies = [
[[package]]
name = "pkg-config"
-version = "0.3.27"
+version = "0.3.28"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "26072860ba924cbfa98ea39c8c19b4dd6a4a25423dbdf219c1eca91aa0cf6964"
+checksum = "69d3587f8a9e599cc7ec2c00e331f71c4e69a5f9a4b8a6efd5b07466b9736f9a"
[[package]]
name = "platforms"
-version = "3.0.2"
+version = "3.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "e3d7ddaed09e0eb771a79ab0fd64609ba0afb0a8366421957936ad14cbd13630"
+checksum = "626dec3cac7cc0e1577a2ec3fc496277ec2baa084bebad95bb6fdbfae235f84c"
[[package]]
name = "plotters"
@@ -1903,12 +1560,6 @@ dependencies = [
]
[[package]]
-name = "portable-atomic"
-version = "1.4.2"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "f32154ba0af3a075eefa1eda8bb414ee928f62303a54ea85b8d6638ff1a6ee9e"
-
-[[package]]
name = "ppv-lite86"
version = "0.2.17"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -1916,9 +1567,9 @@ checksum = "5b40af805b3121feab8a3c29f04d8ad262fa8e0561883e7653e024ae4479e6de"
[[package]]
name = "primeorder"
-version = "0.13.2"
+version = "0.13.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "3c2fcef82c0ec6eefcc179b978446c399b3cdf73c392c35604e399eee6df1ee3"
+checksum = "353e1ca18966c16d9deb1c69278edbc5f194139612772bd9537af60ac231e1e6"
dependencies = [
"elliptic-curve",
]
@@ -1949,9 +1600,9 @@ dependencies = [
[[package]]
name = "proc-macro2"
-version = "1.0.66"
+version = "1.0.76"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "18fb31db3f9bddb2ea821cde30a9f70117e3f119938b5ee630b7403aa6e2ead9"
+checksum = "95fc56cda0b5c3325f5fbbd7ff9fda9e02bb00bb3dac51252d2f1bfa1cb8cc8c"
dependencies = [
"unicode-ident",
]
@@ -2009,9 +1660,9 @@ dependencies = [
[[package]]
name = "quote"
-version = "1.0.32"
+version = "1.0.35"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "50f3b39ccfb720540debaa0164757101c08ecb8d326b15358ce76a62c7e85965"
+checksum = "291ec9ab5efd934aaf503a6466c5d5251535d108ee747472c3977cc5acc868ef"
dependencies = [
"proc-macro2",
]
@@ -2081,9 +1732,9 @@ dependencies = [
[[package]]
name = "rayon"
-version = "1.7.0"
+version = "1.8.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "1d2df5196e37bcc87abebc0053e20787d73847bb33134a69841207dd0a47f03b"
+checksum = "9c27db03db7734835b3f53954b534c91069375ce6ccaa2e065441e07d9b6cdb1"
dependencies = [
"either",
"rayon-core",
@@ -2091,30 +1742,28 @@ dependencies = [
[[package]]
name = "rayon-core"
-version = "1.11.0"
+version = "1.12.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "4b8f95bd6966f5c87776639160a66bd8ab9895d9d4ab01ddba9fc60661aebe8d"
+checksum = "5ce3fb6ad83f861aac485e76e1985cd109d9a3713802152be56c3b1f0e0658ed"
dependencies = [
- "crossbeam-channel",
"crossbeam-deque",
"crossbeam-utils",
- "num_cpus",
]
[[package]]
name = "redox_syscall"
-version = "0.3.5"
+version = "0.4.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "567664f262709473930a4bf9e51bf2ebf3348f2e748ccc50dea20646858f8f29"
+checksum = "4722d768eff46b75989dd134e5c353f0d6296e5aaa3132e776cbdb56be7731aa"
dependencies = [
"bitflags 1.3.2",
]
[[package]]
name = "regex"
-version = "1.9.1"
+version = "1.10.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "b2eae68fc220f7cf2532e4494aded17545fce192d59cd996e0fe7887f4ceb575"
+checksum = "380b951a9c5e80ddfd6136919eef32310721aa4aacd4889a8d39124b026ab343"
dependencies = [
"aho-corasick",
"memchr",
@@ -2124,9 +1773,9 @@ dependencies = [
[[package]]
name = "regex-automata"
-version = "0.3.4"
+version = "0.4.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "b7b6d6190b7594385f61bd3911cd1be99dfddcfc365a4160cc2ab5bff4aed294"
+checksum = "5f804c7828047e88b2d32e2d7fe5a105da8ee3264f01902f796c8e067dc2483f"
dependencies = [
"aho-corasick",
"memchr",
@@ -2135,133 +1784,65 @@ dependencies = [
[[package]]
name = "regex-syntax"
-version = "0.7.4"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "e5ea92a5b6195c6ef2a0295ea818b312502c6fc94dde986c5553242e18fd4ce2"
-
-[[package]]
-name = "reqwest"
-version = "0.11.18"
+version = "0.8.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "cde824a14b7c14f85caff81225f411faacc04a2013f41670f41443742b1c1c55"
-dependencies = [
- "base64 0.21.2",
- "bytes",
- "encoding_rs",
- "futures-core",
- "futures-util",
- "h2",
- "http",
- "http-body",
- "hyper",
- "hyper-rustls",
- "ipnet",
- "js-sys",
- "log",
- "mime",
- "once_cell",
- "percent-encoding",
- "pin-project-lite",
- "rustls",
- "rustls-pemfile",
- "serde",
- "serde_json",
- "serde_urlencoded",
- "tokio",
- "tokio-rustls",
- "tower-service",
- "url",
- "wasm-bindgen",
- "wasm-bindgen-futures",
- "web-sys",
- "webpki-roots",
- "winreg",
-]
+checksum = "c08c74e62047bb2de4ff487b251e4a92e24f48745648451635cec7d591162d9f"
[[package]]
-name = "ring"
-version = "0.16.20"
+name = "relative-path"
+version = "1.9.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "3053cf52e236a3ed746dfc745aa9cacf1b791d846bdaf412f60a8d7d6e17c8fc"
-dependencies = [
- "cc",
- "libc",
- "once_cell",
- "spin 0.5.2",
- "untrusted",
- "web-sys",
- "winapi",
-]
+checksum = "e898588f33fdd5b9420719948f9f2a32c922a246964576f71ba7f24f80610fbc"
[[package]]
-name = "rstest"
-version = "0.16.0"
+name = "reword"
+version = "7.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "b07f2d176c472198ec1e6551dc7da28f1c089652f66a7b722676c2238ebc0edf"
+checksum = "fe272098dce9ed76b479995953f748d1851261390b08f8a0ff619c885a1f0765"
dependencies = [
- "futures",
- "futures-timer",
- "rstest_macros 0.16.0",
- "rustc_version",
+ "unicode-segmentation",
]
[[package]]
name = "rstest"
-version = "0.17.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "de1bb486a691878cd320c2f0d319ba91eeaa2e894066d8b5f8f117c000e9d962"
-dependencies = [
- "rstest_macros 0.17.0",
- "rustc_version",
-]
-
-[[package]]
-name = "rstest_macros"
-version = "0.16.0"
+version = "0.18.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "7229b505ae0706e64f37ffc54a9c163e11022a6636d58fe1f3f52018257ff9f7"
+checksum = "97eeab2f3c0a199bc4be135c36c924b6590b88c377d416494288c14f2db30199"
dependencies = [
- "cfg-if",
- "proc-macro2",
- "quote",
+ "rstest_macros",
"rustc_version",
- "syn 1.0.109",
- "unicode-ident",
]
[[package]]
name = "rstest_macros"
-version = "0.17.0"
+version = "0.18.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "290ca1a1c8ca7edb7c3283bd44dc35dd54fdec6253a3912e201ba1072018fca8"
+checksum = "d428f8247852f894ee1be110b375111b586d4fa431f6c46e64ba5a0dcccbe605"
dependencies = [
"cfg-if",
+ "glob",
"proc-macro2",
"quote",
+ "regex",
+ "relative-path",
"rustc_version",
- "syn 1.0.109",
+ "syn 2.0.48",
"unicode-ident",
]
[[package]]
name = "rstest_reuse"
-version = "0.5.0"
+version = "0.6.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "45f80dcc84beab3a327bbe161f77db25f336a1452428176787c8c79ac79d7073"
+checksum = "88530b681abe67924d42cca181d070e3ac20e0740569441a9e35a7cedd2b34a4"
dependencies = [
"quote",
"rand",
"rustc_version",
- "syn 1.0.109",
+ "syn 2.0.48",
]
[[package]]
-name = "rustc-demangle"
-version = "0.1.23"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "d626bb9dae77e28219937af045c257c28bfd3f69333c512553507f5f9798cb76"
-
-[[package]]
name = "rustc_version"
version = "0.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -2272,46 +1853,15 @@ dependencies = [
[[package]]
name = "rustix"
-version = "0.38.4"
+version = "0.38.28"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "0a962918ea88d644592894bc6dc55acc6c0956488adcebbfb6e273506b7fd6e5"
+checksum = "72e572a5e8ca657d7366229cdde4bd14c4eb5499a9573d4d366fe1b599daa316"
dependencies = [
- "bitflags 2.3.3",
+ "bitflags 2.4.1",
"errno",
"libc",
"linux-raw-sys",
- "windows-sys 0.48.0",
-]
-
-[[package]]
-name = "rustls"
-version = "0.21.5"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "79ea77c539259495ce8ca47f53e66ae0330a8819f67e23ac96ca02f50e7b7d36"
-dependencies = [
- "log",
- "ring",
- "rustls-webpki",
- "sct",
-]
-
-[[package]]
-name = "rustls-pemfile"
-version = "1.0.3"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "2d3987094b1d07b653b7dfdc3f70ce9a1da9c51ac18c1b06b662e4f9a0e9f4b2"
-dependencies = [
- "base64 0.21.2",
-]
-
-[[package]]
-name = "rustls-webpki"
-version = "0.101.2"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "513722fd73ad80a71f72b61009ea1b584bcfa1483ca93949c8f290298837fa59"
-dependencies = [
- "ring",
- "untrusted",
+ "windows-sys 0.52.0",
]
[[package]]
@@ -2322,9 +1872,9 @@ checksum = "7ffc183a10b4478d04cbbbfc96d0873219d962dd5accaff2ffbd4ceb7df837f4"
[[package]]
name = "ryu"
-version = "1.0.15"
+version = "1.0.16"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "1ad4cc8da4ef723ed60bced201181d83791ad433213d8c24efffda1eec85d741"
+checksum = "f98d2aa92eebf49b69786be48e4477826b256916e84a57ff2a4f21923b48eb4c"
[[package]]
name = "same-file"
@@ -2342,16 +1892,6 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49"
[[package]]
-name = "sct"
-version = "0.7.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "d53dcdb7c9f8158937a7981b48accfd39a43af418591a5d008c7b22b5e1b7ca4"
-dependencies = [
- "ring",
- "untrusted",
-]
-
-[[package]]
name = "sec1"
version = "0.7.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -2366,35 +1906,35 @@ dependencies = [
[[package]]
name = "semver"
-version = "1.0.18"
+version = "1.0.21"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "b0293b4b29daaf487284529cc2f5675b8e57c61f70167ba415a463651fd6a918"
+checksum = "b97ed7a9823b74f99c7742f5336af7be5ecd3eeafcb1507d1fa93347b1d589b0"
[[package]]
name = "serde"
-version = "1.0.179"
+version = "1.0.195"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "0a5bf42b8d227d4abf38a1ddb08602e229108a517cd4e5bb28f9c7eaafdce5c0"
+checksum = "63261df402c67811e9ac6def069e4786148c4563f4b50fd4bf30aa370d626b02"
dependencies = [
"serde_derive",
]
[[package]]
name = "serde_derive"
-version = "1.0.179"
+version = "1.0.195"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "741e124f5485c7e60c03b043f79f320bff3527f4bbf12cf3831750dc46a0ec2c"
+checksum = "46fe8f8603d81ba86327b23a2e9cdf49e1255fb94a4c5f297f6ee0547178ea2c"
dependencies = [
"proc-macro2",
"quote",
- "syn 2.0.28",
+ "syn 2.0.48",
]
[[package]]
name = "serde_json"
-version = "1.0.104"
+version = "1.0.111"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "076066c5f1078eac5b722a31827a8832fe108bed65dfa75e233c89f8206e976c"
+checksum = "176e46fa42316f18edd598015a5166857fc835ec732f5215eac6b7bdbf0a84f4"
dependencies = [
"itoa",
"ryu",
@@ -2402,22 +1942,10 @@ dependencies = [
]
[[package]]
-name = "serde_urlencoded"
-version = "0.7.1"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "d3491c14715ca2294c4d6a88f15e84739788c1d030eed8c110436aafdaa2f3fd"
-dependencies = [
- "form_urlencoded",
- "itoa",
- "ryu",
- "serde",
-]
-
-[[package]]
name = "sha2"
-version = "0.10.7"
+version = "0.10.8"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "479fb9d862239e610720565ca91403019f2f00410f1864c5aa7479b950a76ed8"
+checksum = "793db75ad2bcafc3ffa7c68b215fee268f537982cd901d132f89c6343f3a3dc8"
dependencies = [
"cfg-if",
"cpufeatures",
@@ -2432,9 +1960,12 @@ checksum = "45bb67a18fa91266cc7807181f62f9178a6873bfad7dc788c42e6430db40184f"
[[package]]
name = "signature"
-version = "2.1.0"
+version = "2.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "5e1788eed21689f9cf370582dfc467ef36ed9c707f073528ddafa8d83e3b8500"
+checksum = "77549399552de45a898a580c1b41d445bf730df867cc44e6c0233bbc4b8329de"
+dependencies = [
+ "rand_core 0.6.4",
+]
[[package]]
name = "sink"
@@ -2444,25 +1975,6 @@ dependencies = [
]
[[package]]
-name = "slab"
-version = "0.4.8"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "6528351c9bc8ab22353f9d776db39a20288e8d6c37ef8cfe3317cf875eecfc2d"
-dependencies = [
- "autocfg",
-]
-
-[[package]]
-name = "socket2"
-version = "0.4.9"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "64a4a911eed85daf18834cfaa86a79b7d266ff93ff5ba14005426219480ed662"
-dependencies = [
- "libc",
- "winapi",
-]
-
-[[package]]
name = "spin"
version = "0.5.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -2479,9 +1991,9 @@ dependencies = [
[[package]]
name = "spki"
-version = "0.7.2"
+version = "0.7.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "9d1e996ef02c474957d681f1b05213dfb0abab947b446a62d37770b23500184a"
+checksum = "d91ed6c858b01f942cd56b37a94b3e0a1798290327d1236e4d9cf4eaca44d29d"
dependencies = [
"base64ct",
"der",
@@ -2501,21 +2013,21 @@ checksum = "73473c0e59e6d5812c5dfe2a064a6444949f089e20eec9a2e5506596494e4623"
[[package]]
name = "strum"
-version = "0.24.1"
+version = "0.25.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "063e6045c0e62079840579a7e47a355ae92f60eb74daaf156fb1e84ba164e63f"
+checksum = "290d54ea6f91c969195bdbcd7442c8c2a2ba87da8bf60a7ee86a235d4bc1e125"
[[package]]
name = "strum_macros"
-version = "0.24.3"
+version = "0.25.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "1e385be0d24f186b4ce2f9982191e7101bb737312ad61c1f2f984f34bcf85d59"
+checksum = "23dc1fa9ac9c169a78ba62f0b841814b7abae11bdd047b9c58f893439e309ea0"
dependencies = [
"heck",
"proc-macro2",
"quote",
"rustversion",
- "syn 1.0.109",
+ "syn 2.0.48",
]
[[package]]
@@ -2531,15 +2043,14 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "72b64191b275b66ffe2469e8af2c1cfe3bafa67b529ead792a6d0160888b4237"
dependencies = [
"proc-macro2",
- "quote",
"unicode-ident",
]
[[package]]
name = "syn"
-version = "2.0.28"
+version = "2.0.48"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "04361975b3f5e348b2189d8dc55bc942f278b2d482a6a0365de5bdd62d351567"
+checksum = "0f3531638e407dfc0814761abb7c00a5b54992b849452a0646b7f65c9f770f3f"
dependencies = [
"proc-macro2",
"quote",
@@ -2548,22 +2059,22 @@ dependencies = [
[[package]]
name = "tempfile"
-version = "3.7.0"
+version = "3.9.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "5486094ee78b2e5038a6382ed7645bc084dc2ec433426ca4c3cb61e2007b8998"
+checksum = "01ce4141aa927a6d1bd34a041795abd0db1cccba5d5f24b009f694bdf3a1f3fa"
dependencies = [
"cfg-if",
"fastrand",
"redox_syscall",
"rustix",
- "windows-sys 0.48.0",
+ "windows-sys 0.52.0",
]
[[package]]
name = "termcolor"
-version = "1.2.0"
+version = "1.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "be55cf8942feac5c765c2c993422806843c9a9a45d4d5c407ad6dd2ea95eb9b6"
+checksum = "ff1bc3d3f05aff0403e8ac0d92ced918ec05b666a43f83297ccef5bea8a3d449"
dependencies = [
"winapi-util",
]
@@ -2577,29 +2088,23 @@ dependencies = [
]
[[package]]
-name = "textwrap"
-version = "0.16.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "222a222a5bfe1bba4a77b45ec488a741b3cb8872e5e499451fd7d0129c9c7c3d"
-
-[[package]]
name = "thiserror"
-version = "1.0.44"
+version = "1.0.56"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "611040a08a0439f8248d1990b111c95baa9c704c805fa1f62104b39655fd7f90"
+checksum = "d54378c645627613241d077a3a79db965db602882668f9136ac42af9ecb730ad"
dependencies = [
"thiserror-impl",
]
[[package]]
name = "thiserror-impl"
-version = "1.0.44"
+version = "1.0.56"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "090198534930841fab3a5d1bb637cde49e339654e606195f8d9c76eeb081dc96"
+checksum = "fa0faa943b50f3db30a20aa7e265dbc66076993efed8463e8de414e5d06d3471"
dependencies = [
"proc-macro2",
"quote",
- "syn 2.0.28",
+ "syn 2.0.48",
]
[[package]]
@@ -2617,102 +2122,19 @@ name = "tinyvec"
version = "1.6.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "87cc5ceb3875bb20c2890005a4e226a4651264a5c75edb2421b52861a0a0cb50"
-dependencies = [
- "tinyvec_macros",
-]
-
-[[package]]
-name = "tinyvec_macros"
-version = "0.1.1"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "1f3ccbac311fea05f86f61904b462b55fb3df8837a366dfc601a0161d0532f20"
-
-[[package]]
-name = "tokio"
-version = "1.29.1"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "532826ff75199d5833b9d2c5fe410f29235e25704ee5f0ef599fb51c21f4a4da"
-dependencies = [
- "autocfg",
- "backtrace",
- "bytes",
- "libc",
- "mio",
- "num_cpus",
- "pin-project-lite",
- "socket2",
- "windows-sys 0.48.0",
-]
-
-[[package]]
-name = "tokio-rustls"
-version = "0.24.1"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "c28327cf380ac148141087fbfb9de9d7bd4e84ab5d2c28fbc911d753de8a7081"
-dependencies = [
- "rustls",
- "tokio",
-]
-
-[[package]]
-name = "tokio-util"
-version = "0.7.8"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "806fe8c2c87eccc8b3267cbae29ed3ab2d0bd37fca70ab622e46aaa9375ddb7d"
-dependencies = [
- "bytes",
- "futures-core",
- "futures-sink",
- "pin-project-lite",
- "tokio",
- "tracing",
-]
-
-[[package]]
-name = "tower-service"
-version = "0.3.2"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "b6bc1c9ce2b5135ac7f93c72918fc37feb872bdc6a5533a8b85eb4b86bfdae52"
-
-[[package]]
-name = "tracing"
-version = "0.1.37"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "8ce8c33a8d48bd45d624a6e523445fd21ec13d3653cd51f681abf67418f54eb8"
-dependencies = [
- "cfg-if",
- "pin-project-lite",
- "tracing-core",
-]
-
-[[package]]
-name = "tracing-core"
-version = "0.1.31"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "0955b8137a1df6f1a2e9a37d8a6656291ff0297c1a97c24e0d8425fe2312f79a"
-dependencies = [
- "once_cell",
-]
-
-[[package]]
-name = "try-lock"
-version = "0.2.4"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "3528ecfd12c466c6f163363caf2d02a71161dd5e1cc6ae7b34207ea2d42d81ed"
[[package]]
name = "typenum"
-version = "1.16.0"
+version = "1.17.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "497961ef93d974e23eb6f433eb5fe1b7930b659f06d12dec6fc44a8f554c0bba"
+checksum = "42ff0bf0c66b8238c6f3b578df37d0b7848e55df8577b3f74f92a69acceeb825"
[[package]]
name = "ukey2_c_ffi"
version = "0.1.0"
dependencies = [
"cfg-if",
- "crypto_provider_openssl",
- "crypto_provider_rustcrypto",
+ "crypto_provider_default",
"lazy_static",
"lock_adapter",
"log",
@@ -2729,11 +2151,10 @@ dependencies = [
"bytes",
"criterion",
"crypto_provider",
- "crypto_provider_openssl",
+ "crypto_provider_default",
"crypto_provider_rustcrypto",
"nom",
"rand",
- "rstest 0.16.0",
"ukey2_proto",
"ukey2_rs",
]
@@ -2743,8 +2164,7 @@ name = "ukey2_jni"
version = "0.1.0"
dependencies = [
"cfg-if",
- "crypto_provider_openssl",
- "crypto_provider_rustcrypto",
+ "crypto_provider_default",
"jni",
"lazy_static",
"lock_adapter",
@@ -2769,13 +2189,10 @@ name = "ukey2_rs"
version = "0.1.0"
dependencies = [
"crypto_provider",
- "crypto_provider_openssl",
- "crypto_provider_rustcrypto",
- "derive-getters",
+ "crypto_provider_default",
"log",
"num-bigint",
"rand",
- "rstest 0.16.0",
"sha2",
"ukey2_proto",
]
@@ -2784,32 +2201,23 @@ dependencies = [
name = "ukey2_shell"
version = "0.1.0"
dependencies = [
- "clap 4.3.19",
+ "clap",
"crypto_provider_rustcrypto",
"ukey2_connections",
"ukey2_rs",
]
[[package]]
-name = "unicode-bidi"
-version = "0.3.13"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "92888ba5573ff080736b3648696b70cafad7d250551175acbaa4e0385b3e1460"
-
-[[package]]
name = "unicode-ident"
-version = "1.0.11"
+version = "1.0.12"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "301abaae475aa91687eb82514b328ab47a211a533026cb25fc3e519b86adfc3c"
+checksum = "3354b9ac3fae1ff6755cb6db53683adb661634f67557942dea4facebec0fee4b"
[[package]]
-name = "unicode-normalization"
-version = "0.1.22"
+name = "unicode-segmentation"
+version = "1.10.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "5c5713f0fc4b5db668a2ac63cdb7bb4469d8c9fed047b1d0292cc7b0ce2ba921"
-dependencies = [
- "tinyvec",
-]
+checksum = "1dd624098567895118886609431a7c3b8f516e41d30e0643f03d94592a147e36"
[[package]]
name = "universal-hash"
@@ -2822,23 +2230,6 @@ dependencies = [
]
[[package]]
-name = "untrusted"
-version = "0.7.1"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "a156c684c91ea7d62626509bce3cb4e1d9ed5c4d978f7b4352658f96a4c26b4a"
-
-[[package]]
-name = "url"
-version = "2.4.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "50bff7831e19200a85b17131d085c25d7811bc4e186efdaf54bbd132994a88cb"
-dependencies = [
- "form_urlencoded",
- "idna",
- "percent-encoding",
-]
-
-[[package]]
name = "utf8parse"
version = "0.2.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -2858,24 +2249,15 @@ checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f"
[[package]]
name = "walkdir"
-version = "2.3.3"
+version = "2.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "36df944cda56c7d8d8b7496af378e6b16de9284591917d307c9b4d313c44e698"
+checksum = "d71d857dc86794ca4c280d616f7da00d2dbfd8cd788846559a6813e6aa4b54ee"
dependencies = [
"same-file",
"winapi-util",
]
[[package]]
-name = "want"
-version = "0.3.1"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "bfa7760aed19e106de2c7c0b581b509f2f25d3dacaf737cb82ac61bc6d760b0e"
-dependencies = [
- "try-lock",
-]
-
-[[package]]
name = "wasi"
version = "0.11.0+wasi-snapshot-preview1"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -2883,9 +2265,9 @@ checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423"
[[package]]
name = "wasm-bindgen"
-version = "0.2.87"
+version = "0.2.89"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "7706a72ab36d8cb1f80ffbf0e071533974a60d0a308d01a5d0375bf60499a342"
+checksum = "0ed0d4f68a3015cc185aff4db9506a015f4b96f95303897bfa23f846db54064e"
dependencies = [
"cfg-if",
"wasm-bindgen-macro",
@@ -2893,36 +2275,24 @@ dependencies = [
[[package]]
name = "wasm-bindgen-backend"
-version = "0.2.87"
+version = "0.2.89"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "5ef2b6d3c510e9625e5fe6f509ab07d66a760f0885d858736483c32ed7809abd"
+checksum = "1b56f625e64f3a1084ded111c4d5f477df9f8c92df113852fa5a374dbda78826"
dependencies = [
"bumpalo",
"log",
"once_cell",
"proc-macro2",
"quote",
- "syn 2.0.28",
+ "syn 2.0.48",
"wasm-bindgen-shared",
]
[[package]]
-name = "wasm-bindgen-futures"
-version = "0.4.37"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "c02dbc21516f9f1f04f187958890d7e6026df8d16540b7ad9492bc34a67cea03"
-dependencies = [
- "cfg-if",
- "js-sys",
- "wasm-bindgen",
- "web-sys",
-]
-
-[[package]]
name = "wasm-bindgen-macro"
-version = "0.2.87"
+version = "0.2.89"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "dee495e55982a3bd48105a7b947fd2a9b4a8ae3010041b9e0faab3f9cd028f1d"
+checksum = "0162dbf37223cd2afce98f3d0785506dcb8d266223983e4b5b525859e6e182b2"
dependencies = [
"quote",
"wasm-bindgen-macro-support",
@@ -2930,61 +2300,43 @@ dependencies = [
[[package]]
name = "wasm-bindgen-macro-support"
-version = "0.2.87"
+version = "0.2.89"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "54681b18a46765f095758388f2d0cf16eb8d4169b639ab575a8f5693af210c7b"
+checksum = "f0eb82fcb7930ae6219a7ecfd55b217f5f0893484b7a13022ebb2b2bf20b5283"
dependencies = [
"proc-macro2",
"quote",
- "syn 2.0.28",
+ "syn 2.0.48",
"wasm-bindgen-backend",
"wasm-bindgen-shared",
]
[[package]]
name = "wasm-bindgen-shared"
-version = "0.2.87"
+version = "0.2.89"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "ca6ad05a4870b2bf5fe995117d3728437bd27d7cd5f06f13c17443ef369775a1"
+checksum = "7ab9b36309365056cd639da3134bf87fa8f3d86008abf99e612384a6eecd459f"
[[package]]
name = "web-sys"
-version = "0.3.64"
+version = "0.3.66"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "9b85cbef8c220a6abc02aefd892dfc0fc23afb1c6a426316ec33253a3877249b"
+checksum = "50c24a44ec86bb68fbecd1b3efed7e85ea5621b39b35ef2766b66cd984f8010f"
dependencies = [
"js-sys",
"wasm-bindgen",
]
[[package]]
-name = "webpki"
-version = "0.22.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "f095d78192e208183081cc07bc5515ef55216397af48b873e5edcd72637fa1bd"
-dependencies = [
- "ring",
- "untrusted",
-]
-
-[[package]]
-name = "webpki-roots"
-version = "0.22.6"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "b6c71e40d7d2c34a5106301fb632274ca37242cd0c9d3e64dbece371a40a2d87"
-dependencies = [
- "webpki",
-]
-
-[[package]]
name = "which"
-version = "4.4.0"
+version = "4.4.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "2441c784c52b289a054b7201fc93253e288f094e2f4be9058343127c4226a269"
+checksum = "87ba24419a2078cd2b0f2ede2691b6c66d8e47836da3b6db8265ebad47afbfc7"
dependencies = [
"either",
- "libc",
+ "home",
"once_cell",
+ "rustix",
]
[[package]]
@@ -3005,9 +2357,9 @@ checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6"
[[package]]
name = "winapi-util"
-version = "0.1.5"
+version = "0.1.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "70ec6ce85bb158151cae5e5c87f95a8e97d2c0c4b001223f33a334e3ce5de178"
+checksum = "f29e6f9198ba0d26b4c9f07dbe6f9ed633e1f3d5b8b414090084349e46a52596"
dependencies = [
"winapi",
]
@@ -3019,12 +2371,12 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f"
[[package]]
-name = "windows"
-version = "0.48.0"
+name = "windows-core"
+version = "0.52.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "e686886bc078bc1b0b600cac0147aadb815089b6e4da64016cbd754b6342700f"
+checksum = "33ab640c8d7e35bf8ba19b884ba838ceb4fba93a4e8c65a9059d08afcfc683d9"
dependencies = [
- "windows-targets 0.48.1",
+ "windows-targets 0.52.0",
]
[[package]]
@@ -3038,11 +2390,11 @@ dependencies = [
[[package]]
name = "windows-sys"
-version = "0.48.0"
+version = "0.52.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "677d2418bec65e3338edb076e806bc1ec15693c5d0104683f2efe857f61056a9"
+checksum = "282be5f36a8ce781fad8c8ae18fa3f9beff57ec1b52cb3de0789201425d9a33d"
dependencies = [
- "windows-targets 0.48.1",
+ "windows-targets 0.52.0",
]
[[package]]
@@ -3062,17 +2414,32 @@ dependencies = [
[[package]]
name = "windows-targets"
-version = "0.48.1"
+version = "0.48.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "05d4b17490f70499f20b9e791dcf6a299785ce8af4d709018206dc5b4953e95f"
+checksum = "9a2fa6e2155d7247be68c096456083145c183cbbbc2764150dda45a87197940c"
dependencies = [
- "windows_aarch64_gnullvm 0.48.0",
- "windows_aarch64_msvc 0.48.0",
- "windows_i686_gnu 0.48.0",
- "windows_i686_msvc 0.48.0",
- "windows_x86_64_gnu 0.48.0",
- "windows_x86_64_gnullvm 0.48.0",
- "windows_x86_64_msvc 0.48.0",
+ "windows_aarch64_gnullvm 0.48.5",
+ "windows_aarch64_msvc 0.48.5",
+ "windows_i686_gnu 0.48.5",
+ "windows_i686_msvc 0.48.5",
+ "windows_x86_64_gnu 0.48.5",
+ "windows_x86_64_gnullvm 0.48.5",
+ "windows_x86_64_msvc 0.48.5",
+]
+
+[[package]]
+name = "windows-targets"
+version = "0.52.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "8a18201040b24831fbb9e4eb208f8892e1f50a37feb53cc7ff887feb8f50e7cd"
+dependencies = [
+ "windows_aarch64_gnullvm 0.52.0",
+ "windows_aarch64_msvc 0.52.0",
+ "windows_i686_gnu 0.52.0",
+ "windows_i686_msvc 0.52.0",
+ "windows_x86_64_gnu 0.52.0",
+ "windows_x86_64_gnullvm 0.52.0",
+ "windows_x86_64_msvc 0.52.0",
]
[[package]]
@@ -3083,9 +2450,15 @@ checksum = "597a5118570b68bc08d8d59125332c54f1ba9d9adeedeef5b99b02ba2b0698f8"
[[package]]
name = "windows_aarch64_gnullvm"
-version = "0.48.0"
+version = "0.48.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "2b38e32f0abccf9987a4e3079dfb67dcd799fb61361e53e2882c3cbaf0d905d8"
+
+[[package]]
+name = "windows_aarch64_gnullvm"
+version = "0.52.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "91ae572e1b79dba883e0d315474df7305d12f569b400fcf90581b06062f7e1bc"
+checksum = "cb7764e35d4db8a7921e09562a0304bf2f93e0a51bfccee0bd0bb0b666b015ea"
[[package]]
name = "windows_aarch64_msvc"
@@ -3095,9 +2468,15 @@ checksum = "e08e8864a60f06ef0d0ff4ba04124db8b0fb3be5776a5cd47641e942e58c4d43"
[[package]]
name = "windows_aarch64_msvc"
-version = "0.48.0"
+version = "0.48.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "dc35310971f3b2dbbf3f0690a219f40e2d9afcf64f9ab7cc1be722937c26b4bc"
+
+[[package]]
+name = "windows_aarch64_msvc"
+version = "0.52.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "b2ef27e0d7bdfcfc7b868b317c1d32c641a6fe4629c171b8928c7b08d98d7cf3"
+checksum = "bbaa0368d4f1d2aaefc55b6fcfee13f41544ddf36801e793edbbfd7d7df075ef"
[[package]]
name = "windows_i686_gnu"
@@ -3107,9 +2486,15 @@ checksum = "c61d927d8da41da96a81f029489353e68739737d3beca43145c8afec9a31a84f"
[[package]]
name = "windows_i686_gnu"
-version = "0.48.0"
+version = "0.48.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "a75915e7def60c94dcef72200b9a8e58e5091744960da64ec734a6c6e9b3743e"
+
+[[package]]
+name = "windows_i686_gnu"
+version = "0.52.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "622a1962a7db830d6fd0a69683c80a18fda201879f0f447f065a3b7467daa241"
+checksum = "a28637cb1fa3560a16915793afb20081aba2c92ee8af57b4d5f28e4b3e7df313"
[[package]]
name = "windows_i686_msvc"
@@ -3119,9 +2504,15 @@ checksum = "44d840b6ec649f480a41c8d80f9c65108b92d89345dd94027bfe06ac444d1060"
[[package]]
name = "windows_i686_msvc"
-version = "0.48.0"
+version = "0.48.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "8f55c233f70c4b27f66c523580f78f1004e8b5a8b659e05a4eb49d4166cca406"
+
+[[package]]
+name = "windows_i686_msvc"
+version = "0.52.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "4542c6e364ce21bf45d69fdd2a8e455fa38d316158cfd43b3ac1c5b1b19f8e00"
+checksum = "ffe5e8e31046ce6230cc7215707b816e339ff4d4d67c65dffa206fd0f7aa7b9a"
[[package]]
name = "windows_x86_64_gnu"
@@ -3131,9 +2522,15 @@ checksum = "8de912b8b8feb55c064867cf047dda097f92d51efad5b491dfb98f6bbb70cb36"
[[package]]
name = "windows_x86_64_gnu"
-version = "0.48.0"
+version = "0.48.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "ca2b8a661f7628cbd23440e50b05d705db3686f894fc9580820623656af974b1"
+checksum = "53d40abd2583d23e4718fddf1ebec84dbff8381c07cae67ff7768bbf19c6718e"
+
+[[package]]
+name = "windows_x86_64_gnu"
+version = "0.52.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "3d6fa32db2bc4a2f5abeacf2b69f7992cd09dca97498da74a151a3132c26befd"
[[package]]
name = "windows_x86_64_gnullvm"
@@ -3143,9 +2540,15 @@ checksum = "26d41b46a36d453748aedef1486d5c7a85db22e56aff34643984ea85514e94a3"
[[package]]
name = "windows_x86_64_gnullvm"
-version = "0.48.0"
+version = "0.48.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "0b7b52767868a23d5bab768e390dc5f5c55825b6d30b86c844ff2dc7414044cc"
+
+[[package]]
+name = "windows_x86_64_gnullvm"
+version = "0.52.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "7896dbc1f41e08872e9d5e8f8baa8fdd2677f29468c4e156210174edc7f7b953"
+checksum = "1a657e1e9d3f514745a572a6846d3c7aa7dbe1658c056ed9c3344c4109a6949e"
[[package]]
name = "windows_x86_64_msvc"
@@ -3155,26 +2558,23 @@ checksum = "9aec5da331524158c6d1a4ac0ab1541149c0b9505fde06423b02f5ef0106b9f0"
[[package]]
name = "windows_x86_64_msvc"
-version = "0.48.0"
+version = "0.48.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "1a515f5799fe4961cb532f983ce2b23082366b898e52ffbce459c86f67c8378a"
+checksum = "ed94fce61571a4006852b7389a063ab983c02eb1bb37b47f8272ce92d06d9538"
[[package]]
-name = "winreg"
-version = "0.10.1"
+name = "windows_x86_64_msvc"
+version = "0.52.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "80d0f4e272c85def139476380b12f9ac60926689dd2e01d4923222f40580869d"
-dependencies = [
- "winapi",
-]
+checksum = "dff9641d1cd4be8d1a070daf9e3773c5f67e78b4d9d42263020c057706765c04"
[[package]]
name = "wycheproof"
-version = "0.4.0"
+version = "0.5.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "183c789620c674b79dac33cd3aadb6c8006b66cba6a680402235aaebc743e3df"
+checksum = "e639f57253b80c6584b378011aec0fed61c4c21d7a4b97c4d9d7eaf35ca77d12"
dependencies = [
- "base64 0.13.1",
+ "base64",
"hex",
"serde",
"serde_json",
@@ -3182,9 +2582,9 @@ dependencies = [
[[package]]
name = "x25519-dalek"
-version = "2.0.0-rc.3"
+version = "2.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "ec7fae07da688e17059d5886712c933bb0520f15eff2e09cfa18e30968f4e63a"
+checksum = "fb66477291e7e8d2b0ff1bcb900bf29489a9692816d79874bea351e7a8b6de96"
dependencies = [
"curve25519-dalek",
"rand_core 0.6.4",
@@ -3207,7 +2607,7 @@ dependencies = [
"aes",
"anyhow",
"array_ref",
- "base64 0.21.2",
+ "base64",
"crypto_provider",
"crypto_provider_default",
"hex",
@@ -3217,11 +2617,12 @@ dependencies = [
"rand_pcg",
"regex",
"test_helper",
+ "wycheproof",
"xts-mode",
]
[[package]]
name = "zeroize"
-version = "1.6.0"
+version = "1.7.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "2a0956f1ba7c7909bfb66c2e9e4124ab6f6482560f6628b5aaeba39207c9aad9"
+checksum = "525b4ec142c6b68a2d10f01f7bbf6755599ca3f81ea53b8431b7dd348f5fdb2d"
diff --git a/nearby/Cargo.toml b/nearby/Cargo.toml
index 8ce92ea..236b74f 100644
--- a/nearby/Cargo.toml
+++ b/nearby/Cargo.toml
@@ -1,13 +1,11 @@
[workspace]
members = [
- "connections/ukey2/lock_adapter",
"connections/ukey2/ukey2",
"connections/ukey2/ukey2_connections",
"connections/ukey2/ukey2_c_ffi",
"connections/ukey2/ukey2_jni",
"connections/ukey2/ukey2_proto",
"connections/ukey2/ukey2_shell",
- "crypto/bssl-crypto",
"crypto/crypto_provider",
"crypto/crypto_provider_openssl",
"crypto/crypto_provider_rustcrypto",
@@ -16,12 +14,12 @@ members = [
"crypto/crypto_provider_default",
"crypto/rand_core_05_adapter",
"presence/array_view",
- "presence/handle_map",
"presence/ldt",
"presence/ldt_np_adv",
"presence/ldt_np_jni",
"presence/ldt_tbc",
"presence/np_adv",
+ "presence/np_adv_dynamic",
"presence/np_ed25519",
"presence/np_ffi_core",
"presence/np_hkdf",
@@ -29,6 +27,8 @@ members = [
"presence/sink",
"presence/test_helper",
"presence/xts_aes",
+ "util/lock_adapter",
+ "util/handle_map",
]
# TODO: remove ldt_np_adv_ffi once support for no_std + alloc no longer requires nightly
@@ -40,18 +40,35 @@ exclude = [
"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" }
array_view = { path = "presence/array_view" }
-crypto_provider = { path = "crypto/crypto_provider" }
+crypto_provider = { path = "crypto/crypto_provider", default-features = false }
crypto_provider_default = { path = "crypto/crypto_provider_default", default-features = false }
crypto_provider_openssl = { path = "crypto/crypto_provider_openssl" }
+crypto_provider_boringssl = { path = "crypto/crypto_provider_boringssl" }
crypto_provider_rustcrypto = { path = "crypto/crypto_provider_rustcrypto" }
crypto_provider_stubs = { path = "crypto/crypto_provider_stubs" }
crypto_provider_test = { path = "crypto/crypto_provider_test" }
-lock_adapter = { path = "connections/ukey2/lock_adapter" }
-handle_map = { path = "presence/handle_map" }
+lock_adapter = { path = "util/lock_adapter" }
+handle_map = { path = "util/handle_map" }
rand_core_05_adapter = { path = "crypto/rand_core_05_adapter" }
rand_ext = { path = "presence/rand_ext" }
test_helper = { path = "presence/test_helper" }
@@ -62,65 +79,67 @@ ldt = { path = "presence/ldt" }
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" }
# from crates.io
rand = { version = "0.8.5", default-features = false }
-rand_core = "0.6.4"
+rand_core = { version = "0.6.4", features = ["getrandom"] }
rand_pcg = "0.3.1"
-sha2 = { version = "0.10.6", default-features = false }
-aes = "0.8.2"
-cbc = { version = "0.1.2", features = ["alloc", "block-padding"] }
-ctr = "0.9.1"
-hashbrown = "0.14.0"
+sha2 = { version = "0.10.8", default-features = false }
+aes = "0.8.3"
+cbc = { version = "0.1.2", features = ["block-padding"] }
+ctr = "0.9.2"
hkdf = "0.12.3"
hmac = "0.12.1"
-ed25519-dalek = { version = "2.0.0", default-features = false }
-ed25519 = "2.2.0"
-aes-gcm = "0.10.1"
+ed25519-dalek = { version = "2.1.0", default-features = false }
+ed25519 = "2.2.3"
+aes-gcm = "0.10.3"
hex = "0.4.3"
-serde_json = { version = "1.0.96", features = [
+serde = { version = "1.0.193" }
+serde_json = { version = "1.0.108", features = [
"alloc",
], default-features = false }
-base64 = "0.21.0"
+base64 = "0.21.5"
x25519-dalek = { version = "2.0.0", default-features = false }
subtle = { version = "2.5.0", default-features = false }
rand_chacha = { version = "0.3.1", default-features = false }
p256 = { version = "0.13.2", default-features = false }
-sec1 = "0.7.2"
-portable-atomic = "1.3.2"
-protobuf = "3.2.0"
-protobuf-codegen = "3.2.0"
-jni = "0.21.1"
-lock_api = "0.4.9"
+sec1 = "0.7.3"
+protobuf = "=3.2.0"
+protobuf-codegen = "=3.2.0"
+reqwest = { version = "0.11.22", default-features = false, features = ["blocking", "rustls-tls"] }
+jni = "0.20.0"
+lock_api = "0.4.11"
spin = { version = "0.9.8", features = ["once", "lock_api", "rwlock"] }
-anyhow = "1.0.64"
-log = "0.4.17"
-env_logger = "0.10.0"
-criterion = { version = "0.4.0", features = ["html_reports"] }
-clap = { version = "4.0.25", features = ["derive"] }
+anyhow = "1.0.75"
+log = "0.4.20"
+env_logger = "0.10.1"
+criterion = { version = "0.5.1", features = ["html_reports"] }
+clap = { version = "4.4.11", features = ["derive"] }
lazy_static = { version = "1.4.0", features = ["spin_no_std"] }
hex-literal = "0.4.1"
-openssl = "0.10.48"
+openssl = "0.10.61"
cfg-if = "1.0.0"
-blake2 = "0.10.4"
-hdrhistogram = "7.5.0"
-regex = "1.7.0"
-tokio = { version = "1.20.3", features = ["full"] }
+blake2 = "0.10.6"
+hdrhistogram = "7.5.4"
+regex = "1.10.2"
+tokio = { version = "1.35.0", features = ["full"] }
xts-mode = "0.5.1"
-rstest = { version = "0.17.0", default-features = false }
-rstest_reuse = "0.5.0"
-wycheproof = "0.4.0"
-chrono = { version = "0.4.24", default-features = false, features = ["clock"] }
-tempfile = "3.5.0"
-thiserror = "1.0.40"
+rstest = { version = "0.18.2", default-features = false }
+rstest_reuse = "0.6.0"
+wycheproof = "0.5.1"
+chrono = { version = "0.4.31", default-features = false, features = ["clock"] }
+tempfile = "3.8.1"
+thiserror = "1.0.51"
tinyvec = { version = "1.6.0", features = ["rustc_1_55"] }
-mlua = "0.8.8"
-strum = { version = "0.24.1", default-features=false }
-strum_macros = { version = "0.24.2", default-features=false }
+mlua = "0.9.2"
+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"
@@ -155,21 +174,22 @@ rust-version = "1.71.0"
[dependencies]
clap.workspace = true
+cmd-runner = { path = "../cmd-runner" }
anyhow.workspace = true
shell-escape = "0.1.5"
owo-colors.workspace = true
-reqwest = { version = "0.11.17", default-features = false, features = ["blocking", "rustls-tls"] }
semver = "1.0.17"
-base64.workspace = true
walkdir = "2.3.3"
globset = "0.4.10"
crossbeam = "0.8.2"
-tinytemplate = "1.2.1"
chrono.workspace = true
thiserror.workspace = true
log.workspace = true
env_logger.workspace = true
which = "4.4.0"
+file-header = "0.1.2"
+serde_json.workspace = true
+regex = "1.10.2"
[dev-dependencies]
tempfile.workspace = true
diff --git a/nearby/cargo2android.json b/nearby/cargo2android.json
deleted file mode 100644
index 84e42b8..0000000
--- a/nearby/cargo2android.json
+++ /dev/null
@@ -1,8 +0,0 @@
-{
- "patch": "patches/Android.bp.patch",
- "device": true,
- "run": true,
- "features": "openssl,boringssl",
- "onefile": true,
- "patch": "patches/Android.bp.patch"
-}
diff --git a/nearby/connections/ukey2/lock_adapter/src/std.rs b/nearby/connections/ukey2/lock_adapter/src/std.rs
deleted file mode 100644
index ffd3ecc..0000000
--- a/nearby/connections/ukey2/lock_adapter/src/std.rs
+++ /dev/null
@@ -1,37 +0,0 @@
-// Copyright 2023 Google LLC
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-// http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-use crate::NoPoisonMutex;
-
-pub struct Mutex<T>(std::sync::Mutex<T>);
-
-impl<T> NoPoisonMutex<T> for Mutex<T> {
- type MutexGuard<'a> = std::sync::MutexGuard<'a, T> where T: 'a;
-
- fn lock(&self) -> Self::MutexGuard<'_> {
- self.0.lock().unwrap_or_else(|poison| poison.into_inner())
- }
-
- fn try_lock(&self) -> Option<Self::MutexGuard<'_>> {
- match self.0.try_lock() {
- Ok(guard) => Some(guard),
- Err(std::sync::TryLockError::Poisoned(guard)) => Some(guard.into_inner()),
- Err(std::sync::TryLockError::WouldBlock) => None,
- }
- }
-
- fn new(value: T) -> Self {
- Self(std::sync::Mutex::new(value))
- }
-}
diff --git a/nearby/connections/ukey2/ukey2/Cargo.toml b/nearby/connections/ukey2/ukey2/Cargo.toml
index 7c10b41..2ac6f3a 100644
--- a/nearby/connections/ukey2/ukey2/Cargo.toml
+++ b/nearby/connections/ukey2/ukey2/Cargo.toml
@@ -4,18 +4,24 @@ version.workspace = true
edition.workspace = true
publish.workspace = true
+[lints]
+workspace = true
+
+[features]
+default = []
+test_rustcrypto = ["crypto_provider_default/rustcrypto"]
+test_openssl = ["crypto_provider_default/openssl"]
+test_boringssl = ["crypto_provider_default/boringssl"]
+
[dependencies]
crypto_provider.workspace = true
rand.workspace = true
ukey2_proto.workspace = true
log.workspace = true
-derive-getters = "0.2.0"
-num-bigint = "0.4.3"
+num-bigint = "0.4.4"
[dev-dependencies]
-rand = { workspace = true, features = ["std_rng"] }
-crypto_provider_rustcrypto = {workspace = true, features = ["std"] }
-crypto_provider_openssl.workspace = true
-rstest = "0.16.0"
+rand = { workspace = true, features = ["std_rng", "getrandom"] }
+crypto_provider_default.workspace = true
sha2.workspace = true
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 @@ pub(crate) enum MessageType {
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 @@ impl HandshakeCipher {
}
}
-#[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 @@ impl<C: CryptoProvider> IntoAdapter<GenericPublicKey<C>> for securemessage::Gene
/// 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 @@ impl SendAlert {
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 b58cee6..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,
@@ -21,42 +23,41 @@ use crypto_provider::elliptic_curve::{EcdhProvider, EphemeralSecret, PublicKey};
use crypto_provider::p256::P256;
use crypto_provider::x25519::X25519;
use crypto_provider::{CryptoProvider, CryptoRng};
-use crypto_provider_openssl::Openssl;
-use crypto_provider_rustcrypto::RustCrypto;
+use crypto_provider_default::CryptoProviderImpl;
use rand::rngs::StdRng;
use rand::{Rng, SeedableRng};
-use rstest::rstest;
use sha2::Digest;
use std::collections::hash_set;
use ukey2_proto::protobuf::Message;
use ukey2_proto::ukey2_all_proto::ukey;
-#[rstest]
-fn advance_from_init_to_finish_client_test<C: CryptoProvider>(
- #[values(RustCrypto::new(), Openssl)] _crypto_provider: C,
-) {
+type X25519EphemeralSecret =
+ <<CryptoProviderImpl as CryptoProvider>::X25519 as EcdhProvider<X25519>>::EphemeralSecret;
+type X25519PublicKey =
+ <<CryptoProviderImpl as CryptoProvider>::X25519 as EcdhProvider<X25519>>::PublicKey;
+type P256EphemeralSecret =
+ <<CryptoProviderImpl as CryptoProvider>::P256 as EcdhProvider<P256>>::EphemeralSecret;
+
+#[test]
+fn advance_from_init_to_finish_client_test() {
let mut rng = StdRng::from_entropy();
- let client1 = Ukey2ClientStage1::<C>::from(
+ let client1 = Ukey2ClientStage1::<CryptoProviderImpl>::from(
&mut rng,
"next protocol".to_string(),
HandshakeImplementation::Spec,
);
let secret =
- <C::X25519 as EcdhProvider<X25519>>::EphemeralSecret::generate_random(
- &mut <<C::X25519 as EcdhProvider<X25519>>::EphemeralSecret as EphemeralSecret<
- X25519,
- >>::Rng::new(),
- );
- let public_key =
- <C::X25519 as EcdhProvider<X25519>>::PublicKey::from_bytes(&secret.public_key_bytes())
- .unwrap();
+ X25519EphemeralSecret::generate_random(&mut <X25519EphemeralSecret as EphemeralSecret<
+ X25519,
+ >>::Rng::new());
+ let public_key = X25519PublicKey::from_bytes(secret.public_key_bytes().as_ref()).unwrap();
let random: [u8; 32] = rng.gen();
let message_data: ukey::Ukey2ServerInit = ukey::Ukey2ServerInit {
version: Some(1),
random: Some(random.to_vec()),
handshake_cipher: Some(ukey::Ukey2HandshakeCipher::CURVE25519_SHA512.into()),
- public_key: Some(public_key.to_bytes()),
+ public_key: Some(public_key.to_bytes().as_ref().to_vec()),
..Default::default()
};
@@ -66,25 +67,24 @@ fn advance_from_init_to_finish_client_test<C: CryptoProvider>(
// TODO assertions on client state
}
-#[rstest]
-fn advance_from_init_to_complete_server_x25519_test<C: CryptoProvider>(
- #[values(RustCrypto::new(), Openssl)] _crypto_provider: C,
-) {
+#[test]
+fn advance_from_init_to_complete_server_x25519_test() {
let mut rng = StdRng::from_entropy();
let mut next_protocols = hash_set::HashSet::new();
let _ = next_protocols.insert("AES_256_CBC-HMAC_SHA256".to_string());
- let server1 = Ukey2ServerStage1::<C>::from(next_protocols, HandshakeImplementation::Spec);
+ let server1 = Ukey2ServerStage1::<CryptoProviderImpl>::from(
+ next_protocols,
+ HandshakeImplementation::Spec,
+ );
// We construct a ClientInit message for the server to get it into the state to handle
// ClientFinish messages.
let secret =
- <C::X25519 as EcdhProvider<X25519>>::EphemeralSecret::generate_random(
- &mut <<C::X25519 as EcdhProvider<X25519>>::EphemeralSecret as EphemeralSecret<
- X25519,
- >>::Rng::new(),
- );
+ X25519EphemeralSecret::generate_random(&mut <X25519EphemeralSecret as EphemeralSecret<
+ X25519,
+ >>::Rng::new());
let client_finished_msg = {
let mut msg = ukey::Ukey2ClientFinished::default();
- msg.set_public_key(secret.public_key_bytes());
+ msg.set_public_key(secret.public_key_bytes().as_ref().to_vec());
msg.to_wrapped_msg()
};
let client_finished_bytes = client_finished_msg.write_to_bytes().unwrap();
@@ -120,23 +120,23 @@ fn advance_from_init_to_complete_server_x25519_test<C: CryptoProvider>(
// TODO assertions on server state
}
-#[rstest]
-fn advance_from_init_to_complete_server_p256_test<C: CryptoProvider>(
- #[values(RustCrypto::new(), Openssl)] _crypto_provider: C,
-) {
+#[test]
+fn advance_from_init_to_complete_server_p256_test() {
let mut rng = StdRng::from_entropy();
let mut next_protocols = hash_set::HashSet::new();
let _ = next_protocols.insert("AES_256_CBC-HMAC_SHA256".to_string());
- let server1 = Ukey2ServerStage1::<C>::from(next_protocols, HandshakeImplementation::Spec);
+ let server1 = Ukey2ServerStage1::<CryptoProviderImpl>::from(
+ next_protocols,
+ HandshakeImplementation::Spec,
+ );
// We construct a ClientInit message for the server to get it into the state to handle
// ClientFinish messages.
- let secret = <C::P256 as EcdhProvider<P256>>::EphemeralSecret::generate_random(
- &mut <<C::P256 as EcdhProvider<P256>>::EphemeralSecret as EphemeralSecret<P256>>::Rng::new(
- ),
+ let secret = P256EphemeralSecret::generate_random(
+ &mut <P256EphemeralSecret as EphemeralSecret<P256>>::Rng::new(),
);
let client_finished_msg = {
let mut msg = ukey::Ukey2ClientFinished::default();
- msg.set_public_key(secret.public_key_bytes());
+ msg.set_public_key(secret.public_key_bytes().as_ref().to_vec());
msg.to_wrapped_msg()
};
let client_finished_bytes = client_finished_msg.write_to_bytes().unwrap();
diff --git a/nearby/connections/ukey2/ukey2/src/ukey2_handshake.rs b/nearby/connections/ukey2/ukey2/src/ukey2_handshake.rs
index a22c0ed..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 @@ impl WireCompatibilityLayer for HandshakeImplementation {
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,
@@ -133,7 +136,7 @@ impl WireCompatibilityLayer for HandshakeImplementation {
match public_key {
GenericPublicKey::Ec256(key) => {
debug_assert_eq!(cipher, HandshakeCipher::P256Sha512);
- Some(key.to_bytes())
+ Some(key.to_bytes().to_vec())
}
}
}
@@ -167,7 +170,7 @@ impl<C: CryptoProvider> Ukey2ServerStage1<C> {
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 @@ impl<C: CryptoProvider> Ukey2ServerStage1<C> {
// 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(
@@ -267,9 +270,12 @@ impl<C: CryptoProvider> Ukey2ServerStage2<C> {
server_init.set_random(random.to_vec());
server_init.set_handshake_cipher(commitment.cipher().as_proto());
server_init.set_public_key(match &key_pair {
- ServerKeyPair::Curve25519(es) => es.public_key_bytes(),
+ ServerKeyPair::Curve25519(es) => es.public_key_bytes().as_ref().to_vec(),
ServerKeyPair::P256(es) => handshake_impl
- .encode_public_key::<C>(es.public_key_bytes(), HandshakeCipher::P256Sha512)
+ .encode_public_key::<C>(
+ es.public_key_bytes().as_ref().to_vec(),
+ HandshakeCipher::P256Sha512,
+ )
.unwrap(),
});
@@ -392,7 +398,7 @@ impl<C: CryptoProvider> Ukey2ClientStage1<C> {
);
let curve25519_client_finished_bytes = {
let client_finished = ukey::Ukey2ClientFinished {
- public_key: Some(curve25519_secret.public_key_bytes()),
+ public_key: Some(curve25519_secret.public_key_bytes().as_ref().to_vec()),
..Default::default()
};
client_finished.to_wrapped_msg().write_to_bytes().unwrap()
@@ -411,7 +417,7 @@ impl<C: CryptoProvider> Ukey2ClientStage1<C> {
public_key: Some(
handshake_impl
.encode_public_key::<C>(
- p256_secret.public_key_bytes(),
+ p256_secret.public_key_bytes().as_ref().to_vec(),
HandshakeCipher::P256Sha512,
)
.expect("Output of p256_secret.public_key_bytes should always be valid input for encode_public_key"),
@@ -471,7 +477,7 @@ impl<C: CryptoProvider> Ukey2ClientStage1<C> {
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);
}
@@ -480,7 +486,7 @@ impl<C: CryptoProvider> Ukey2ClientStage1<C> {
.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 31ba416..85b972c 100644
--- a/nearby/connections/ukey2/ukey2/tests/tests.rs
+++ b/nearby/connections/ukey2/ukey2/tests/tests.rs
@@ -12,7 +12,9 @@
// See the License for the specific language governing permissions and
// limitations under the License.
-use crypto_provider_rustcrypto::RustCrypto;
+#![allow(clippy::unwrap_used)]
+
+use crypto_provider_default::CryptoProviderImpl;
use rand::{rngs::StdRng, SeedableRng};
use std::collections::hash_set;
use ukey2_rs::*;
@@ -22,10 +24,12 @@ fn full_integration_state_machine() {
let mut next_protocols = hash_set::HashSet::new();
let next_protocol = "AES_256_CBC-HMAC_SHA256".to_string();
let _ = next_protocols.insert(next_protocol.clone());
- let server1 =
- Ukey2ServerStage1::<RustCrypto>::from(next_protocols, HandshakeImplementation::Spec);
+ let server1 = Ukey2ServerStage1::<CryptoProviderImpl>::from(
+ next_protocols,
+ HandshakeImplementation::Spec,
+ );
let mut rng = StdRng::from_entropy();
- let client1 = Ukey2ClientStage1::<RustCrypto>::from(
+ let client1 = Ukey2ClientStage1::<CryptoProviderImpl>::from(
&mut rng,
next_protocol,
HandshakeImplementation::Spec,
@@ -37,12 +41,18 @@ fn full_integration_state_machine() {
let server3 = server2.advance_state(&mut rng, client2.client_finished_msg()).unwrap();
assert_eq!(
- server3.completed_handshake().auth_string::<RustCrypto>().derive_array::<32>(),
- client2.completed_handshake().auth_string::<RustCrypto>().derive_array::<32>()
+ server3.completed_handshake().auth_string::<CryptoProviderImpl>().derive_array::<32>(),
+ client2.completed_handshake().auth_string::<CryptoProviderImpl>().derive_array::<32>()
);
assert_eq!(
- server3.completed_handshake().next_protocol_secret::<RustCrypto>().derive_array::<32>(),
- client2.completed_handshake().next_protocol_secret::<RustCrypto>().derive_array::<32>()
+ server3
+ .completed_handshake()
+ .next_protocol_secret::<CryptoProviderImpl>()
+ .derive_array::<32>(),
+ client2
+ .completed_handshake()
+ .next_protocol_secret::<CryptoProviderImpl>()
+ .derive_array::<32>()
);
}
@@ -51,12 +61,12 @@ fn full_integration_state_machine_public_key_in_protobuf() {
let mut next_protocols = hash_set::HashSet::new();
let next_protocol = "AES_256_CBC-HMAC_SHA256".to_string();
let _ = next_protocols.insert(next_protocol.clone());
- let server1 = Ukey2ServerStage1::<RustCrypto>::from(
+ let server1 = Ukey2ServerStage1::<CryptoProviderImpl>::from(
next_protocols,
HandshakeImplementation::PublicKeyInProtobuf,
);
let mut rng = StdRng::from_entropy();
- let client1 = Ukey2ClientStage1::<RustCrypto>::from(
+ let client1 = Ukey2ClientStage1::<CryptoProviderImpl>::from(
&mut rng,
next_protocol,
HandshakeImplementation::PublicKeyInProtobuf,
@@ -68,11 +78,17 @@ fn full_integration_state_machine_public_key_in_protobuf() {
let server3 = server2.advance_state(&mut rng, client2.client_finished_msg()).unwrap();
assert_eq!(
- server3.completed_handshake().auth_string::<RustCrypto>().derive_array::<32>(),
- client2.completed_handshake().auth_string::<RustCrypto>().derive_array::<32>()
+ server3.completed_handshake().auth_string::<CryptoProviderImpl>().derive_array::<32>(),
+ client2.completed_handshake().auth_string::<CryptoProviderImpl>().derive_array::<32>()
);
assert_eq!(
- server3.completed_handshake().next_protocol_secret::<RustCrypto>().derive_array::<32>(),
- client2.completed_handshake().next_protocol_secret::<RustCrypto>().derive_array::<32>()
+ server3
+ .completed_handshake()
+ .next_protocol_secret::<CryptoProviderImpl>()
+ .derive_array::<32>(),
+ client2
+ .completed_handshake()
+ .next_protocol_secret::<CryptoProviderImpl>()
+ .derive_array::<32>()
);
}
diff --git a/nearby/connections/ukey2/ukey2_c_ffi/Cargo.toml b/nearby/connections/ukey2/ukey2_c_ffi/Cargo.toml
index 3ca55f4..182ecf2 100644
--- a/nearby/connections/ukey2/ukey2_c_ffi/Cargo.toml
+++ b/nearby/connections/ukey2/ukey2_c_ffi/Cargo.toml
@@ -8,9 +8,8 @@ publish.workspace = true
ukey2_connections = { path = "../ukey2_connections" }
ukey2_rs = { path = "../ukey2" }
cfg-if.workspace = true
-crypto_provider_openssl = { workspace = true, optional = true }
-crypto_provider_rustcrypto = { workspace = true, optional = true, features = ["alloc"] }
-lock_adapter.workspace = true
+crypto_provider_default.workspace = true
+lock_adapter = {workspace = true, features = ["spin"]}
lazy_static.workspace = true
log.workspace = true
@@ -18,11 +17,11 @@ rand.workspace = true
rand_chacha.workspace = true
[features]
-default = ["rustcrypto"]
-std = ["crypto_provider_rustcrypto/std", "lock_adapter/std"]
-openssl = ["dep:crypto_provider_openssl", "std"]
-rustcrypto = ["crypto_provider_rustcrypto"]
-crypto_provider_rustcrypto = ["dep:crypto_provider_rustcrypto"]
+default = ["rustcrypto", "std"]
+std = ["lock_adapter/std"]
+openssl = ["crypto_provider_default/openssl", "std"]
+boringssl = ["crypto_provider_default/boringssl", "std"]
+rustcrypto = ["crypto_provider_default/rustcrypto"]
[lib]
diff --git a/nearby/connections/ukey2/ukey2_c_ffi/cpp/CMakeLists.txt b/nearby/connections/ukey2/ukey2_c_ffi/cpp/CMakeLists.txt
index c1247eb..a8b7b9d 100644
--- a/nearby/connections/ukey2/ukey2_c_ffi/cpp/CMakeLists.txt
+++ b/nearby/connections/ukey2/ukey2_c_ffi/cpp/CMakeLists.txt
@@ -14,12 +14,16 @@
cmake_minimum_required(VERSION 3.14)
+project(Ukey2)
+
enable_testing()
include_directories(
${CMAKE_SOURCE_DIR}/ukey2_c_ffi/cpp/)
+include(GoogleTest)
include(ExternalProject)
+
set_directory_properties(PROPERTIES EP_PREFIX ${CMAKE_BINARY_DIR}/target/tmp)
ExternalProject_Add(
ukey2_c_ffi
@@ -33,11 +37,9 @@ ExternalProject_Add(
set(CMAKE_CXX_STANDARD 20)
if(UNIX)
- add_compile_options(-Wall -Werror -Wextra -Wimplicit-fallthrough -Wextra-semi
+ add_compile_options(-Wall -Wextra -Wimplicit-fallthrough -Wextra-semi
-Wno-missing-field-initializers -Wno-unused-parameter -Wno-psabi
- -Wno-unneeded-internal-declaration
- -Wno-ignored-pragma-optimize
- -Wno-bitfield-constant-conversion -Wno-deprecated-this-capture -Wshadow
+ -Wshadow
-Wsign-compare)
elseif(MSVC)
add_compile_options(-W4 -MD)
@@ -81,5 +83,4 @@ elseif(MSVC)
)
endif()
-include(GoogleTest)
gtest_discover_tests(ffi_test)
diff --git a/nearby/connections/ukey2/ukey2_c_ffi/cpp/ukey2_ffi.h b/nearby/connections/ukey2/ukey2_c_ffi/cpp/ukey2_ffi.h
index b269da8..654e293 100644
--- a/nearby/connections/ukey2/ukey2_c_ffi/cpp/ukey2_ffi.h
+++ b/nearby/connections/ukey2/ukey2_c_ffi/cpp/ukey2_ffi.h
@@ -13,6 +13,7 @@
// limitations under the License.
#include "ukey2_bindings.h"
+
#include <string>
struct D2DRestoreConnectionContextV1Result;
diff --git a/nearby/connections/ukey2/ukey2_c_ffi/cpp/ukey2_test.cc b/nearby/connections/ukey2/ukey2_c_ffi/cpp/ukey2_test.cc
index 1e913b5..962ada8 100644
--- a/nearby/connections/ukey2/ukey2_c_ffi/cpp/ukey2_test.cc
+++ b/nearby/connections/ukey2/ukey2_c_ffi/cpp/ukey2_test.cc
@@ -12,10 +12,11 @@
// See the License for the specific language governing permissions and
// limitations under the License.
+#include "ukey2_ffi.h"
+
#include <string>
-#include <gtest/gtest.h>
-#include "ukey2_ffi.h"
+#include "gtest/gtest.h"
namespace {
diff --git a/nearby/connections/ukey2/ukey2_c_ffi/src/lib.rs b/nearby/connections/ukey2/ukey2_c_ffi/src/lib.rs
index a79fe60..e5cc548 100644
--- a/nearby/connections/ukey2/ukey2_c_ffi/src/lib.rs
+++ b/nearby/connections/ukey2/ukey2_c_ffi/src/lib.rs
@@ -15,6 +15,7 @@
use std::collections::HashMap;
use std::ptr::null_mut;
+use crypto_provider_default::CryptoProviderImpl as CryptoProvider;
use lazy_static::lazy_static;
use lock_adapter::NoPoisonMutex;
use rand::Rng;
@@ -31,13 +32,6 @@ use ukey2_connections::{
InitiatorD2DHandshakeContext, ServerD2DHandshakeContext,
};
-cfg_if::cfg_if! {
- if #[cfg(feature = "rustcrypto")] {
- use crypto_provider_rustcrypto::RustCrypto as CryptoProvider;
- } else {
- use crypto_provider_openssl::Openssl as CryptoProvider;
- }
-}
#[repr(C)]
pub struct RustFFIByteArray {
ptr: *mut u8,
diff --git a/nearby/connections/ukey2/ukey2_connections/Cargo.toml b/nearby/connections/ukey2/ukey2_connections/Cargo.toml
index 41fe10d..b1c34a1 100644
--- a/nearby/connections/ukey2/ukey2_connections/Cargo.toml
+++ b/nearby/connections/ukey2/ukey2_connections/Cargo.toml
@@ -4,20 +4,30 @@ version.workspace = true
edition.workspace = true
publish.workspace = true
+[lints]
+workspace = true
+
+[features]
+default = []
+test_boringssl = ["crypto_provider_default/boringssl"]
+test_rustcrypto = ["crypto_provider_default/rustcrypto"]
+test_openssl = ["crypto_provider_default/openssl"]
+
[dependencies]
ukey2_rs = { path = "../ukey2" }
crypto_provider.workspace = true
rand = { workspace = true, features = ["std", "std_rng"] }
ukey2_proto.workspace = true
-nom = { version = "7.1.1", features = ["alloc"] }
-bytes = "1.2.1"
-criterion = "0.4.0"
+nom = { version = "7.1.3", features = ["alloc"] }
+bytes = "1.5.0"
+criterion.workspace = true
[dev-dependencies]
-crypto_provider_openssl.workspace = true
+crypto_provider_default.workspace = true
+# This would only be used when the feature "test_rustcrypto" is set, but optional dev-dependencies
+# are not allowed ¯\_(ツ)_/¯
crypto_provider_rustcrypto = { workspace = true, features = ["alloc", "std"] }
-rstest = "0.16.0"
[[bench]]
name = "ukey2_benches"
diff --git a/nearby/connections/ukey2/ukey2_connections/benches/ukey2_benches.rs b/nearby/connections/ukey2/ukey2_connections/benches/ukey2_benches.rs
index b609761..f816165 100644
--- a/nearby/connections/ukey2/ukey2_connections/benches/ukey2_benches.rs
+++ b/nearby/connections/ukey2/ukey2_connections/benches/ukey2_benches.rs
@@ -12,11 +12,13 @@
// 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};
use crypto_provider::CryptoProvider;
-use crypto_provider_rustcrypto::RustCrypto;
+use crypto_provider_default::CryptoProviderImpl;
use ukey2_connections::{
D2DConnectionContextV1, D2DHandshakeContext, InitiatorD2DHandshakeContext,
ServerD2DHandshakeContext,
@@ -63,16 +65,18 @@ fn criterion_benchmark(c: &mut Criterion) {
let mut group = c.benchmark_group("throughput");
let mut plaintext = Vec::new();
let (mut initiator_ctx, mut server_ctx) =
- run_handshake_with_rng::<RustCrypto, _>(rand::rngs::StdRng::from_entropy());
+ 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::<RustCrypto, &[u8]>(&plaintext, None);
- black_box(server_ctx.decode_message_from_peer::<RustCrypto, &[u8]>(&msg, None))
+ let msg = initiator_ctx
+ .encode_message_to_peer::<CryptoProviderImpl, &[u8]>(&plaintext, None);
+ black_box(
+ server_ctx.decode_message_from_peer::<CryptoProviderImpl, &[u8]>(&msg, None),
+ )
})
});
}
diff --git a/nearby/connections/ukey2/ukey2_connections/fuzz/Cargo.lock b/nearby/connections/ukey2/ukey2_connections/fuzz/Cargo.lock
index c4aad27..167e0de 100644
--- a/nearby/connections/ukey2/ukey2_connections/fuzz/Cargo.lock
+++ b/nearby/connections/ukey2/ukey2_connections/fuzz/Cargo.lock
@@ -25,6 +25,20 @@ dependencies = [
]
[[package]]
+name = "aes-gcm"
+version = "0.10.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "831010a0f742e1209b3bcea8fab6a8e149051ba6099432c8cb2cc117dec3ead1"
+dependencies = [
+ "aead",
+ "aes",
+ "cipher",
+ "ctr",
+ "ghash",
+ "subtle",
+]
+
+[[package]]
name = "aes-gcm-siv"
version = "0.11.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -55,6 +69,12 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4b46cbb362ab8752921c97e041f5e366ee6297bd428a31275b9fcf1e380f7299"
[[package]]
+name = "anstyle"
+version = "1.0.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "7079075b41f533b8c61d2a4d073c4676e1f8b249ff94a393b0595db304e0dd87"
+
+[[package]]
name = "anyhow"
version = "1.0.72"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -70,17 +90,6 @@ dependencies = [
]
[[package]]
-name = "atty"
-version = "0.2.14"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "d9b39be18770d11421cdb1b9947a45dd3f37e93092cbf377614828a319d5fee8"
-dependencies = [
- "hermit-abi 0.1.19",
- "libc",
- "winapi",
-]
-
-[[package]]
name = "autocfg"
version = "1.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -99,6 +108,12 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a"
[[package]]
+name = "bitflags"
+version = "2.4.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "327762f6e5a765692301e5bb513e0d9fef63be86bbc14528052b1cd3e6f03e07"
+
+[[package]]
name = "block-buffer"
version = "0.10.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -124,9 +139,9 @@ checksum = "a3e2c3daef883ecc1b5d58c15adae93470a91d425f3532ba1695849656af3fc1"
[[package]]
name = "bytes"
-version = "1.4.0"
+version = "1.5.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "89b2fd2a0dcf38d7971e2194b6b6eebab45ae01067456a7fd93d5547a61b70be"
+checksum = "a2bd12c1caf447e69cd4528f47f94d203fd2582878ecb9e9465484c4148a8223"
[[package]]
name = "cast"
@@ -197,26 +212,30 @@ dependencies = [
[[package]]
name = "clap"
-version = "3.2.25"
+version = "4.4.14"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "4ea181bf566f71cb9a5d17a59e1871af638180a18fb0035c92ae62b705207123"
+checksum = "33e92c5c1a78c62968ec57dbc2440366a2d6e5a23faf829970ff1585dc6b18e2"
dependencies = [
- "bitflags",
- "clap_lex",
- "indexmap",
- "textwrap",
+ "clap_builder",
]
[[package]]
-name = "clap_lex"
-version = "0.2.4"
+name = "clap_builder"
+version = "4.4.14"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "2850f2f5a82cbf437dd5af4d49848fbdfc27c157c3d010345776f952765261c5"
+checksum = "f4323769dc8a61e2c39ad7dc26f6f2800524691a44d74fe3d1071a5c24db6370"
dependencies = [
- "os_str_bytes",
+ "anstyle",
+ "clap_lex",
]
[[package]]
+name = "clap_lex"
+version = "0.6.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "702fc72eb24e5a1e48ce58027a675bc24edd52096d5397d4aea7c6dd9eca0bd1"
+
+[[package]]
name = "const-oid"
version = "0.9.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -233,19 +252,19 @@ dependencies = [
[[package]]
name = "criterion"
-version = "0.4.0"
+version = "0.5.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "e7c76e09c1aae2bc52b3d2f29e13c6572553b30c4aa1b8a49fd70de6412654cb"
+checksum = "f2b12d017a929603d80db1831cd3a24082f8137ce19c69e6447f54f5fc8d692f"
dependencies = [
"anes",
- "atty",
"cast",
"ciborium",
"clap",
"criterion-plot",
+ "is-terminal",
"itertools",
- "lazy_static",
"num-traits",
+ "once_cell",
"oorandom",
"plotters",
"rayon",
@@ -337,7 +356,7 @@ dependencies = [
name = "crypto_provider"
version = "0.1.0"
dependencies = [
- "bytes",
+ "tinyvec",
]
[[package]]
@@ -346,6 +365,7 @@ version = "0.1.0"
dependencies = [
"aead",
"aes",
+ "aes-gcm",
"aes-gcm-siv",
"cbc",
"cfg-if",
@@ -375,9 +395,9 @@ dependencies = [
[[package]]
name = "curve25519-dalek"
-version = "4.0.0-rc.3"
+version = "4.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "436ace70fc06e06f7f689d2624dc4e2f0ea666efb5aa704215f7249ae6e047a7"
+checksum = "f711ade317dd348950a9910f81c5947e3d8907ebd2b83f76203ff1807e6a2bc2"
dependencies = [
"cfg-if",
"cpufeatures",
@@ -397,7 +417,7 @@ checksum = "83fdaf97f4804dcebfa5862639bc9ce4121e82140bec2a987ac5140294865b5b"
dependencies = [
"proc-macro2",
"quote",
- "syn 2.0.26",
+ "syn",
]
[[package]]
@@ -411,17 +431,6 @@ dependencies = [
]
[[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 = "derive_arbitrary"
version = "1.3.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -429,7 +438,7 @@ checksum = "53e0efad4403bfc52dc201159c4b842a246a14b98c64b55dfd0f2d89729dfeb8"
dependencies = [
"proc-macro2",
"quote",
- "syn 2.0.26",
+ "syn",
]
[[package]]
@@ -454,14 +463,15 @@ dependencies = [
[[package]]
name = "ed25519-dalek"
-version = "2.0.0-rc.3"
+version = "2.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "faa8e9049d5d72bfc12acbc05914731b5322f79b5e2f195e9f2d705fca22ab4c"
+checksum = "1f628eaec48bfd21b865dc2950cfa014450c01d2fa2b69a86c2fd5844ec523c0"
dependencies = [
"curve25519-dalek",
"ed25519",
"rand_core",
"sha2",
+ "subtle",
]
[[package]]
@@ -497,7 +507,7 @@ checksum = "4bcfec3a70f97c962c307b2d2c56e358cf1d00b558d74262b5f929ee8cc7e73a"
dependencies = [
"errno-dragonfly",
"libc",
- "windows-sys",
+ "windows-sys 0.48.0",
]
[[package]]
@@ -558,6 +568,16 @@ dependencies = [
]
[[package]]
+name = "ghash"
+version = "0.5.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d930750de5717d2dd0b8c0d42c076c0e884c81a73e6cab859bbd2339c71e3e40"
+dependencies = [
+ "opaque-debug",
+ "polyval",
+]
+
+[[package]]
name = "group"
version = "0.13.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -582,15 +602,6 @@ checksum = "8a9ee70c43aaf417c914396645a0fa852624801b24ebb7ae78fe8272889ac888"
[[package]]
name = "hermit-abi"
-version = "0.1.19"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "62b467343b94ba476dcb2500d242dadbb39557df889310ac77c5d99100aaac33"
-dependencies = [
- "libc",
-]
-
-[[package]]
-name = "hermit-abi"
version = "0.3.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "443144c8cdadd93ebf52ddb4056d257f5b52c04d3c804e657d19eb73fc33668b"
@@ -648,9 +659,20 @@ version = "1.0.11"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "eae7b9aee968036d54dce06cebaefd919e4472e753296daccd6d344e3e2df0c2"
dependencies = [
- "hermit-abi 0.3.2",
+ "hermit-abi",
"libc",
- "windows-sys",
+ "windows-sys 0.48.0",
+]
+
+[[package]]
+name = "is-terminal"
+version = "0.4.10"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "0bad00257d07be169d870ab665980b06cdb366d792ad690bf2e76876dc503455"
+dependencies = [
+ "hermit-abi",
+ "rustix 0.38.13",
+ "windows-sys 0.52.0",
]
[[package]]
@@ -687,12 +709,6 @@ dependencies = [
]
[[package]]
-name = "lazy_static"
-version = "1.4.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646"
-
-[[package]]
name = "libc"
version = "0.2.147"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -716,10 +732,16 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ef53942eb7bf7ff43a617b3e2c1c4a5ecf5944a7c1bc12d7ee39bbb15e5c1519"
[[package]]
+name = "linux-raw-sys"
+version = "0.4.12"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "c4cd1a83af159aa67994778be9070f0ae1bd732942279cabb14f86f986a21456"
+
+[[package]]
name = "log"
-version = "0.4.19"
+version = "0.4.20"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "b06a4cde4c0f271a446782e3eff8de789548ce57dbc8eca9292c27f4a42004b4"
+checksum = "b5e6163cb8c49088c2c36f57875e58ccd8c87c7427f7fbd50ea6710b2f3f2e8f"
[[package]]
name = "memchr"
@@ -754,9 +776,9 @@ dependencies = [
[[package]]
name = "num-bigint"
-version = "0.4.3"
+version = "0.4.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "f93ab6289c7b344a8a9f60f88d80aa20032336fe78da341afc91c8a2341fc75f"
+checksum = "608e7659b5c3d7cba262d894801b9ec9d00de989e8a82bd4bef91d08da45cdc0"
dependencies = [
"autocfg",
"num-integer",
@@ -775,9 +797,9 @@ dependencies = [
[[package]]
name = "num-traits"
-version = "0.2.15"
+version = "0.2.17"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "578ede34cf02f8924ab9447f50c28075b4d3e5b269972345e7e0372b38c6cdcd"
+checksum = "39e3200413f237f41ab11ad6d161bc7239c84dcb631773ccd7de3dfe4b5c267c"
dependencies = [
"autocfg",
]
@@ -788,7 +810,7 @@ version = "1.16.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4161fcb6d602d4d2081af7c3a45852d875a03dd337a6bfdd6e06407b61342a43"
dependencies = [
- "hermit-abi 0.3.2",
+ "hermit-abi",
"libc",
]
@@ -811,12 +833,6 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "624a8340c38c1b80fd549087862da4ba43e08858af025b236e509b6649fc13d5"
[[package]]
-name = "os_str_bytes"
-version = "6.5.1"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "4d5d9eb14b174ee9aa2ef96dc2b94637a2d4b6e7cb873c7e171f0c20c6cf3eac"
-
-[[package]]
name = "p256"
version = "0.13.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -1014,7 +1030,7 @@ version = "0.3.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "567664f262709473930a4bf9e51bf2ebf3348f2e748ccc50dea20646858f8f29"
dependencies = [
- "bitflags",
+ "bitflags 1.3.2",
]
[[package]]
@@ -1061,12 +1077,25 @@ version = "0.37.23"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4d69718bf81c6127a49dc64e44a742e8bb9213c0ff8869a22c308f84c1d4ab06"
dependencies = [
- "bitflags",
+ "bitflags 1.3.2",
"errno",
"io-lifetimes",
"libc",
- "linux-raw-sys",
- "windows-sys",
+ "linux-raw-sys 0.3.8",
+ "windows-sys 0.48.0",
+]
+
+[[package]]
+name = "rustix"
+version = "0.38.13"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d7db8590df6dfcd144d22afd1b83b36c21a18d7cbc1dc4bb5295a8712e9eb662"
+dependencies = [
+ "bitflags 2.4.1",
+ "errno",
+ "libc",
+ "linux-raw-sys 0.4.12",
+ "windows-sys 0.48.0",
]
[[package]]
@@ -1126,7 +1155,7 @@ checksum = "389894603bd18c46fa56231694f8d827779c0951a667087194cf9de94ed24682"
dependencies = [
"proc-macro2",
"quote",
- "syn 2.0.26",
+ "syn",
]
[[package]]
@@ -1142,9 +1171,9 @@ dependencies = [
[[package]]
name = "sha2"
-version = "0.10.7"
+version = "0.10.8"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "479fb9d862239e610720565ca91403019f2f00410f1864c5aa7479b950a76ed8"
+checksum = "793db75ad2bcafc3ffa7c68b215fee268f537982cd901d132f89c6343f3a3dc8"
dependencies = [
"cfg-if",
"cpufeatures",
@@ -1165,17 +1194,6 @@ checksum = "81cdd64d312baedb58e21336b31bc043b77e01cc99033ce76ef539f78e965ebc"
[[package]]
name = "syn"
-version = "1.0.109"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "72b64191b275b66ffe2469e8af2c1cfe3bafa67b529ead792a6d0160888b4237"
-dependencies = [
- "proc-macro2",
- "quote",
- "unicode-ident",
-]
-
-[[package]]
-name = "syn"
version = "2.0.26"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "45c3457aacde3c65315de5031ec191ce46604304d2446e803d71ade03308d970"
@@ -1195,17 +1213,11 @@ dependencies = [
"cfg-if",
"fastrand",
"redox_syscall",
- "rustix",
- "windows-sys",
+ "rustix 0.37.23",
+ "windows-sys 0.48.0",
]
[[package]]
-name = "textwrap"
-version = "0.16.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "222a222a5bfe1bba4a77b45ec488a741b3cb8872e5e499451fd7d0129c9c7c3d"
-
-[[package]]
name = "thiserror"
version = "1.0.43"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -1222,7 +1234,7 @@ checksum = "463fe12d7993d3b327787537ce8dd4dfa058de32fc2b195ef3cde03dc4771e8f"
dependencies = [
"proc-macro2",
"quote",
- "syn 2.0.26",
+ "syn",
]
[[package]]
@@ -1236,6 +1248,12 @@ dependencies = [
]
[[package]]
+name = "tinyvec"
+version = "1.6.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "87cc5ceb3875bb20c2890005a4e226a4651264a5c75edb2421b52861a0a0cb50"
+
+[[package]]
name = "typenum"
version = "1.16.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -1279,7 +1297,6 @@ name = "ukey2_rs"
version = "0.1.0"
dependencies = [
"crypto_provider",
- "derive-getters",
"log",
"num-bigint",
"rand",
@@ -1345,7 +1362,7 @@ dependencies = [
"once_cell",
"proc-macro2",
"quote",
- "syn 2.0.26",
+ "syn",
"wasm-bindgen-shared",
]
@@ -1367,7 +1384,7 @@ checksum = "54681b18a46765f095758388f2d0cf16eb8d4169b639ab575a8f5693af210c7b"
dependencies = [
"proc-macro2",
"quote",
- "syn 2.0.26",
+ "syn",
"wasm-bindgen-backend",
"wasm-bindgen-shared",
]
@@ -1436,7 +1453,16 @@ version = "0.48.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "677d2418bec65e3338edb076e806bc1ec15693c5d0104683f2efe857f61056a9"
dependencies = [
- "windows-targets",
+ "windows-targets 0.48.1",
+]
+
+[[package]]
+name = "windows-sys"
+version = "0.52.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "282be5f36a8ce781fad8c8ae18fa3f9beff57ec1b52cb3de0789201425d9a33d"
+dependencies = [
+ "windows-targets 0.52.0",
]
[[package]]
@@ -1445,13 +1471,28 @@ version = "0.48.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "05d4b17490f70499f20b9e791dcf6a299785ce8af4d709018206dc5b4953e95f"
dependencies = [
- "windows_aarch64_gnullvm",
- "windows_aarch64_msvc",
- "windows_i686_gnu",
- "windows_i686_msvc",
- "windows_x86_64_gnu",
- "windows_x86_64_gnullvm",
- "windows_x86_64_msvc",
+ "windows_aarch64_gnullvm 0.48.0",
+ "windows_aarch64_msvc 0.48.0",
+ "windows_i686_gnu 0.48.0",
+ "windows_i686_msvc 0.48.0",
+ "windows_x86_64_gnu 0.48.0",
+ "windows_x86_64_gnullvm 0.48.0",
+ "windows_x86_64_msvc 0.48.0",
+]
+
+[[package]]
+name = "windows-targets"
+version = "0.52.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "8a18201040b24831fbb9e4eb208f8892e1f50a37feb53cc7ff887feb8f50e7cd"
+dependencies = [
+ "windows_aarch64_gnullvm 0.52.0",
+ "windows_aarch64_msvc 0.52.0",
+ "windows_i686_gnu 0.52.0",
+ "windows_i686_msvc 0.52.0",
+ "windows_x86_64_gnu 0.52.0",
+ "windows_x86_64_gnullvm 0.52.0",
+ "windows_x86_64_msvc 0.52.0",
]
[[package]]
@@ -1461,46 +1502,88 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "91ae572e1b79dba883e0d315474df7305d12f569b400fcf90581b06062f7e1bc"
[[package]]
+name = "windows_aarch64_gnullvm"
+version = "0.52.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "cb7764e35d4db8a7921e09562a0304bf2f93e0a51bfccee0bd0bb0b666b015ea"
+
+[[package]]
name = "windows_aarch64_msvc"
version = "0.48.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b2ef27e0d7bdfcfc7b868b317c1d32c641a6fe4629c171b8928c7b08d98d7cf3"
[[package]]
+name = "windows_aarch64_msvc"
+version = "0.52.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "bbaa0368d4f1d2aaefc55b6fcfee13f41544ddf36801e793edbbfd7d7df075ef"
+
+[[package]]
name = "windows_i686_gnu"
version = "0.48.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "622a1962a7db830d6fd0a69683c80a18fda201879f0f447f065a3b7467daa241"
[[package]]
+name = "windows_i686_gnu"
+version = "0.52.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "a28637cb1fa3560a16915793afb20081aba2c92ee8af57b4d5f28e4b3e7df313"
+
+[[package]]
name = "windows_i686_msvc"
version = "0.48.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4542c6e364ce21bf45d69fdd2a8e455fa38d316158cfd43b3ac1c5b1b19f8e00"
[[package]]
+name = "windows_i686_msvc"
+version = "0.52.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ffe5e8e31046ce6230cc7215707b816e339ff4d4d67c65dffa206fd0f7aa7b9a"
+
+[[package]]
name = "windows_x86_64_gnu"
version = "0.48.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ca2b8a661f7628cbd23440e50b05d705db3686f894fc9580820623656af974b1"
[[package]]
+name = "windows_x86_64_gnu"
+version = "0.52.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "3d6fa32db2bc4a2f5abeacf2b69f7992cd09dca97498da74a151a3132c26befd"
+
+[[package]]
name = "windows_x86_64_gnullvm"
version = "0.48.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7896dbc1f41e08872e9d5e8f8baa8fdd2677f29468c4e156210174edc7f7b953"
[[package]]
+name = "windows_x86_64_gnullvm"
+version = "0.52.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "1a657e1e9d3f514745a572a6846d3c7aa7dbe1658c056ed9c3344c4109a6949e"
+
+[[package]]
name = "windows_x86_64_msvc"
version = "0.48.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1a515f5799fe4961cb532f983ce2b23082366b898e52ffbce459c86f67c8378a"
[[package]]
+name = "windows_x86_64_msvc"
+version = "0.52.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "dff9641d1cd4be8d1a070daf9e3773c5f67e78b4d9d42263020c057706765c04"
+
+[[package]]
name = "x25519-dalek"
-version = "2.0.0-rc.3"
+version = "2.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "ec7fae07da688e17059d5886712c933bb0520f15eff2e09cfa18e30968f4e63a"
+checksum = "fb66477291e7e8d2b0ff1bcb900bf29489a9692816d79874bea351e7a8b6de96"
dependencies = [
"curve25519-dalek",
"rand_core",
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 35a1e70..d6acecc 100644
--- a/nearby/connections/ukey2/ukey2_connections/src/tests.rs
+++ b/nearby/connections/ukey2/ukey2_connections/src/tests.rs
@@ -12,14 +12,12 @@
// See the License for the specific language governing permissions and
// limitations under the License.
-use crypto_provider_rustcrypto::RustCryptoImpl;
-use rand::SeedableRng;
-use rand::{rngs::StdRng, CryptoRng, RngCore};
-use rstest::rstest;
+#![allow(clippy::indexing_slicing)]
use crypto_provider::CryptoProvider;
-use crypto_provider_openssl::Openssl;
-use crypto_provider_rustcrypto::RustCrypto;
+use crypto_provider_default::CryptoProviderImpl;
+use rand::SeedableRng;
+use rand::{rngs::StdRng, CryptoRng, RngCore};
use ukey2_rs::HandshakeImplementation;
use crate::{
@@ -28,27 +26,25 @@ use crate::{
InitiatorD2DHandshakeContext, ServerD2DHandshakeContext,
};
-#[rstest]
-fn crypto_test_encrypt_decrypt<C: CryptoProvider>(
- #[values(RustCrypto::new(), Openssl)] _crypto_provider: C,
-) {
+type AesCbcPkcs7Padded = <CryptoProviderImpl as CryptoProvider>::AesCbcPkcs7Padded;
+
+#[test]
+fn crypto_test_encrypt_decrypt() {
let message = b"Hello World!";
let key = b"42424242424242424242424242424242";
let (ciphertext, iv) =
- encrypt::<_, C::AesCbcPkcs7Padded>(key, message, &mut rand::rngs::StdRng::from_entropy());
- let decrypt_result = decrypt::<C::AesCbcPkcs7Padded>(key, ciphertext.as_slice(), &iv);
+ encrypt::<_, AesCbcPkcs7Padded>(key, message, &mut rand::rngs::StdRng::from_entropy());
+ let decrypt_result = decrypt::<AesCbcPkcs7Padded>(key, ciphertext.as_slice(), &iv);
let ptext = decrypt_result.expect("Decrypt should be successful");
assert_eq!(ptext, message.to_vec());
}
-#[rstest]
-fn crypto_test_encrypt_seeded<C: CryptoProvider>(
- #[values(RustCrypto::new(), Openssl)] _crypto_provider: C,
-) {
+#[test]
+fn crypto_test_encrypt_seeded() {
let message = b"Hello World!";
let key = b"42424242424242424242424242424242";
let mut rng = MockRng;
- let (ciphertext, iv) = encrypt::<_, C::AesCbcPkcs7Padded>(key, message, &mut rng);
+ let (ciphertext, iv) = encrypt::<_, AesCbcPkcs7Padded>(key, message, &mut rng);
// Expected values extracted from the results of the current implementation.
// This test makes sure that we don't accidentally change the encryption logic that
// causes incompatibility between versions.
@@ -59,43 +55,36 @@ fn crypto_test_encrypt_seeded<C: CryptoProvider>(
);
}
-#[rstest]
-fn crypto_test_decrypt_seeded<C: CryptoProvider>(
- #[values(RustCrypto::new(), Openssl)] _crypto_provider: C,
-) {
+#[test]
+fn crypto_test_decrypt_seeded() {
let iv = [1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1];
let ciphertext = [20, 59, 195, 101, 11, 208, 245, 128, 247, 196, 81, 80, 158, 77, 174, 61];
let key = b"42424242424242424242424242424242";
- let plaintext = decrypt::<C::AesCbcPkcs7Padded>(key, &ciphertext, &iv).unwrap();
+ let plaintext = decrypt::<AesCbcPkcs7Padded>(key, &ciphertext, &iv).unwrap();
assert_eq!(plaintext, b"Hello World!");
}
-#[rstest]
-fn decrypt_test_wrong_key<C: CryptoProvider>(
- #[values(RustCrypto::new(), Openssl)] _crypto_provider: C,
-) {
+#[test]
+fn decrypt_test_wrong_key() {
let message = b"Hello World!";
let good_key = b"42424242424242424242424242424242";
- let (ciphertext, iv) = encrypt::<_, C::AesCbcPkcs7Padded>(
- good_key,
- message,
- &mut rand::rngs::StdRng::from_entropy(),
- );
+ let (ciphertext, iv) =
+ encrypt::<_, AesCbcPkcs7Padded>(good_key, message, &mut rand::rngs::StdRng::from_entropy());
let bad_key = b"43434343434343434343434343434343";
- let decrypt_result = decrypt::<C::AesCbcPkcs7Padded>(bad_key, ciphertext.as_slice(), &iv);
+ let decrypt_result = decrypt::<AesCbcPkcs7Padded>(bad_key, ciphertext.as_slice(), &iv);
match decrypt_result {
// The padding is valid, but the decrypted value should be bad since the keys don't match
Ok(decrypted_bad) => assert_ne!(decrypted_bad, message),
// The padding is bad, so it returns an error and is unable to decrypt
Err(crypto_provider::aes::cbc::DecryptionError::BadPadding) => (),
}
- let decrypt_result = decrypt::<C::AesCbcPkcs7Padded>(good_key, ciphertext.as_slice(), &iv);
+ let decrypt_result = decrypt::<AesCbcPkcs7Padded>(good_key, ciphertext.as_slice(), &iv);
let ptext = decrypt_result.unwrap();
assert_eq!(ptext, message.to_vec());
}
-fn run_handshake<C: CryptoProvider>() -> (D2DConnectionContextV1, D2DConnectionContextV1) {
- run_handshake_with_rng::<C, _>(rand::rngs::StdRng::from_entropy())
+fn run_handshake() -> (D2DConnectionContextV1, D2DConnectionContextV1) {
+ run_handshake_with_rng::<CryptoProviderImpl, _>(rand::rngs::StdRng::from_entropy())
}
fn run_handshake_with_rng<C, R>(
@@ -133,15 +122,17 @@ where
(initiator_ctx.to_connection_context().unwrap(), server_ctx.to_connection_context().unwrap())
}
-#[rstest]
-fn send_receive_message_seeded<C: CryptoProvider>(
- // TODO: Find a way to inject RNG / generated ephemeral secrets in openSSL and test them here
- #[values(RustCryptoImpl::< MockRng >::new())] _crypto_provider: C,
-) {
+// TODO: Find a way to inject RNG / generated ephemeral secrets in openSSL and test them here
+#[cfg(feature = "test_rustcrypto")]
+#[test]
+fn send_receive_message_seeded() {
+ use crypto_provider_rustcrypto::RustCryptoImpl;
let rng = MockRng;
let message = b"Hello World!";
- let (mut init_conn_ctx, mut server_conn_ctx) = run_handshake_with_rng::<C, _>(rng);
- let encoded = init_conn_ctx.encode_message_to_peer::<C, &[u8]>(message, None);
+ let (mut init_conn_ctx, mut server_conn_ctx) =
+ run_handshake_with_rng::<RustCryptoImpl<MockRng>, _>(rng);
+ let encoded =
+ init_conn_ctx.encode_message_to_peer::<RustCryptoImpl<MockRng>, &[u8]>(message, None);
// Expected values extracted from the results of the current implementation.
// This test makes sure that we don't accidentally change the encryption logic that
// causes incompatibility between versions.
@@ -155,102 +146,104 @@ fn send_receive_message_seeded<C: CryptoProvider>(
45, 239, 234, 248, 148, 9, 150, 204, 117, 32, 216, 5, 126, 224, 39
]
);
- let decoded = server_conn_ctx.decode_message_from_peer::<C, &[u8]>(&encoded, None).unwrap();
+ let decoded = server_conn_ctx
+ .decode_message_from_peer::<CryptoProviderImpl, &[u8]>(&encoded, None)
+ .unwrap();
assert_eq!(message, &decoded[..]);
}
-#[rstest]
-fn send_receive_message<C: CryptoProvider>(
- #[values(RustCrypto::new(), Openssl)] _crypto_provider: C,
-) {
+#[test]
+fn send_receive_message() {
let message = b"Hello World!";
- let (mut init_conn_ctx, mut server_conn_ctx) = run_handshake::<C>();
- let encoded = init_conn_ctx.encode_message_to_peer::<C, &[u8]>(message, None);
- let decoded = server_conn_ctx.decode_message_from_peer::<C, &[u8]>(encoded.as_slice(), None);
+ let (mut init_conn_ctx, mut server_conn_ctx) = run_handshake();
+ let encoded = init_conn_ctx.encode_message_to_peer::<CryptoProviderImpl, &[u8]>(message, None);
+ let decoded = server_conn_ctx
+ .decode_message_from_peer::<CryptoProviderImpl, &[u8]>(encoded.as_slice(), None);
assert_eq!(message.to_vec(), decoded.expect("Decode should be successful"));
}
-#[rstest]
-fn send_receive_message_associated_data<C: CryptoProvider>(
- #[values(RustCrypto::new(), Openssl)] _crypto_provider: C,
-) {
+#[test]
+fn send_receive_message_associated_data() {
let message = b"Hello World!";
- let (mut init_conn_ctx, mut server_conn_ctx) = run_handshake::<C>();
- let encoded = init_conn_ctx.encode_message_to_peer::<C, _>(message, Some(b"associated data"));
- let decoded = server_conn_ctx
- .decode_message_from_peer::<C, _>(encoded.as_slice(), Some(b"associated data"));
+ let (mut init_conn_ctx, mut server_conn_ctx) = run_handshake();
+ let encoded = init_conn_ctx
+ .encode_message_to_peer::<CryptoProviderImpl, _>(message, Some(b"associated data"));
+ let decoded = server_conn_ctx.decode_message_from_peer::<CryptoProviderImpl, _>(
+ encoded.as_slice(),
+ Some(b"associated data"),
+ );
assert_eq!(message.to_vec(), decoded.expect("Decode should be successful"));
// Make sure decode fails with missing associated data.
- let decoded = server_conn_ctx.decode_message_from_peer::<C, &[u8]>(encoded.as_slice(), None);
+ let decoded = server_conn_ctx
+ .decode_message_from_peer::<CryptoProviderImpl, &[u8]>(encoded.as_slice(), None);
assert!(decoded.is_err());
// Make sure decode fails with different associated data.
- let decoded = server_conn_ctx
- .decode_message_from_peer::<C, _>(encoded.as_slice(), Some(b"assoc1ated data"));
+ let decoded = server_conn_ctx.decode_message_from_peer::<CryptoProviderImpl, _>(
+ encoded.as_slice(),
+ Some(b"assoc1ated data"),
+ );
assert!(decoded.is_err());
}
-#[rstest]
-fn test_save_restore_session<C: CryptoProvider>(
- #[values(RustCrypto::new(), Openssl)] _crypto_provider: C,
-) {
- let (init_conn_ctx, server_conn_ctx) = run_handshake::<C>();
+#[test]
+fn test_save_restore_session() {
+ let (init_conn_ctx, server_conn_ctx) = run_handshake();
let init_session = init_conn_ctx.save_session();
let server_session = server_conn_ctx.save_session();
let mut init_restored_ctx =
- D2DConnectionContextV1::from_saved_session::<C>(init_session.as_slice())
+ D2DConnectionContextV1::from_saved_session::<CryptoProviderImpl>(init_session.as_slice())
.expect("failed to restore client session");
let mut server_restored_ctx =
- D2DConnectionContextV1::from_saved_session::<C>(server_session.as_slice())
+ D2DConnectionContextV1::from_saved_session::<CryptoProviderImpl>(server_session.as_slice())
.expect("failed to restore server session");
let message = b"Hello World!";
- let encoded = init_restored_ctx.encode_message_to_peer::<C, &[u8]>(message, None);
- let decoded =
- server_restored_ctx.decode_message_from_peer::<C, &[u8]>(encoded.as_slice(), None);
+ let encoded =
+ init_restored_ctx.encode_message_to_peer::<CryptoProviderImpl, &[u8]>(message, None);
+ let decoded = server_restored_ctx
+ .decode_message_from_peer::<CryptoProviderImpl, &[u8]>(encoded.as_slice(), None);
assert_eq!(message.to_vec(), decoded.expect("Decode should be successful"));
}
-#[rstest]
-fn test_save_restore_bad_session<C: CryptoProvider>(
- #[values(RustCrypto::new(), Openssl)] _crypto_provider: C,
-) {
- let (init_conn_ctx, server_conn_ctx) = run_handshake::<C>();
+#[test]
+fn test_save_restore_bad_session() {
+ let (init_conn_ctx, server_conn_ctx) = run_handshake();
let init_session = init_conn_ctx.save_session();
let server_session = server_conn_ctx.save_session();
- let _ = D2DConnectionContextV1::from_saved_session::<C>(init_session.as_slice())
- .expect("failed to restore client session");
+ let _ =
+ D2DConnectionContextV1::from_saved_session::<CryptoProviderImpl>(init_session.as_slice())
+ .expect("failed to restore client session");
let server_restored_ctx =
- D2DConnectionContextV1::from_saved_session::<C>(&server_session[0..60]);
+ D2DConnectionContextV1::from_saved_session::<CryptoProviderImpl>(&server_session[0..60]);
assert_eq!(server_restored_ctx.unwrap_err(), DeserializeError::BadDataLength);
}
-#[rstest]
-fn test_save_restore_bad_protocol_version<C: CryptoProvider>(
- #[values(RustCrypto::new(), Openssl)] _crypto_provider: C,
-) {
- let (init_conn_ctx, server_conn_ctx) = run_handshake::<C>();
+#[test]
+fn test_save_restore_bad_protocol_version() {
+ let (init_conn_ctx, server_conn_ctx) = run_handshake();
let init_session = init_conn_ctx.save_session();
let mut server_session = server_conn_ctx.save_session();
- let _ = D2DConnectionContextV1::from_saved_session::<C>(init_session.as_slice())
- .expect("failed to restore client session");
+ let _ =
+ D2DConnectionContextV1::from_saved_session::<CryptoProviderImpl>(init_session.as_slice())
+ .expect("failed to restore client session");
server_session[0] = 0; // Change the protocol version to an invalid one (0)
- let server_restored_ctx = D2DConnectionContextV1::from_saved_session::<C>(&server_session);
+ let server_restored_ctx =
+ D2DConnectionContextV1::from_saved_session::<CryptoProviderImpl>(&server_session);
assert_eq!(server_restored_ctx.unwrap_err(), DeserializeError::BadProtocolVersion);
}
-#[rstest]
-fn test_unique_session<C: CryptoProvider>(
- #[values(RustCrypto::new(), Openssl)] _crypto_provider: C,
-) {
- let (mut init_conn_ctx, mut server_conn_ctx) = run_handshake::<C>();
- let init_session = init_conn_ctx.get_session_unique::<C>();
- let server_session = server_conn_ctx.get_session_unique::<C>();
+#[test]
+fn test_unique_session() {
+ let (mut init_conn_ctx, mut server_conn_ctx) = run_handshake();
+ let init_session = init_conn_ctx.get_session_unique::<CryptoProviderImpl>();
+ let server_session = server_conn_ctx.get_session_unique::<CryptoProviderImpl>();
let message = b"Hello World!";
- let encoded = init_conn_ctx.encode_message_to_peer::<C, &[u8]>(message, None);
- let decoded = server_conn_ctx.decode_message_from_peer::<C, &[u8]>(encoded.as_slice(), None);
+ let encoded = init_conn_ctx.encode_message_to_peer::<CryptoProviderImpl, &[u8]>(message, None);
+ let decoded = server_conn_ctx
+ .decode_message_from_peer::<CryptoProviderImpl, &[u8]>(encoded.as_slice(), None);
assert_eq!(message.to_vec(), decoded.expect("Decode should be successful"));
- let init_session_after = init_conn_ctx.get_session_unique::<C>();
- let server_session_after = server_conn_ctx.get_session_unique::<C>();
- let bad_server_ctx = D2DConnectionContextV1::new::<C>(
+ let init_session_after = init_conn_ctx.get_session_unique::<CryptoProviderImpl>();
+ let server_session_after = server_conn_ctx.get_session_unique::<CryptoProviderImpl>();
+ let bad_server_ctx = D2DConnectionContextV1::new::<CryptoProviderImpl>(
server_conn_ctx.get_sequence_number_for_decoding(),
server_conn_ctx.get_sequence_number_for_encoding(),
Aes256Key::default(),
@@ -260,7 +253,7 @@ fn test_unique_session<C: CryptoProvider>(
assert_eq!(init_session, init_session_after);
assert_eq!(server_session, server_session_after);
assert_eq!(init_session, server_session);
- assert_ne!(server_session, bad_server_ctx.get_session_unique::<C>());
+ assert_ne!(server_session, bad_server_ctx.get_session_unique::<CryptoProviderImpl>());
}
#[test]
diff --git a/nearby/connections/ukey2/ukey2_jni/Cargo.toml b/nearby/connections/ukey2/ukey2_jni/Cargo.toml
index 7a399d3..08566dd 100644
--- a/nearby/connections/ukey2/ukey2_jni/Cargo.toml
+++ b/nearby/connections/ukey2/ukey2_jni/Cargo.toml
@@ -4,16 +4,18 @@ version.workspace = true
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]
ukey2_connections = { path = "../ukey2_connections" }
ukey2_rs = { path = "../ukey2" }
-lock_adapter.workspace = true
+lock_adapter = {workspace = true, features = ["spin"]}
cfg-if.workspace = true
-crypto_provider_openssl = { workspace = true, optional = true }
-crypto_provider_rustcrypto = { workspace = true, optional = true, features = [ "alloc" ] }
+crypto_provider_default = { workspace = true }
lazy_static.workspace = true
rand.workspace = true
rand_chacha.workspace = true
@@ -22,10 +24,10 @@ log = { workspace = true, features = ["std"] }
[features]
default = ["rustcrypto"]
-openssl = ["dep:crypto_provider_openssl", "std"]
-rustcrypto = ["crypto_provider_rustcrypto"]
-std = ["lock_adapter/std", "crypto_provider_rustcrypto/std"]
-crypto_provider_rustcrypto = ["dep:crypto_provider_rustcrypto"]
+openssl = ["crypto_provider_default/openssl", "std"]
+rustcrypto = ["crypto_provider_default/rustcrypto"]
+boringssl = ["crypto_provider_default/boringssl"]
+std = ["lock_adapter/std"]
[lib]
crate_type = ["cdylib"] \ No newline at end of file
diff --git a/nearby/connections/ukey2/ukey2_jni/java/build.gradle.kts b/nearby/connections/ukey2/ukey2_jni/java/build.gradle.kts
index 58c58fc..78b0ebc 100644
--- a/nearby/connections/ukey2/ukey2_jni/java/build.gradle.kts
+++ b/nearby/connections/ukey2/ukey2_jni/java/build.gradle.kts
@@ -44,6 +44,7 @@ dependencies {
implementation("com.google.code.findbugs:jsr305:3.0.2")
implementation(kotlin("stdlib"))
testImplementation("org.junit.jupiter:junit-jupiter:5.9.2")
+ testRuntimeOnly("org.junit.jupiter:junit-jupiter-engine:5.9.2")
}
kotlin {
@@ -51,10 +52,10 @@ kotlin {
}
tasks.jmh {
- jvmArgs.value(mutableListOf("-Djava.library.path=../../../../target/release"))
+ jvmArgs.value(mutableListOf("-Djava.library.path=$projectDir/../../../../target/release"))
}
tasks.test {
useJUnitPlatform()
- jvmArgs = mutableListOf("-Djava.library.path=../../../../target/debug")
+ jvmArgs = mutableListOf("-Djava.library.path=$projectDir/../../../../target/debug")
}
diff --git a/nearby/connections/ukey2/ukey2_jni/java/src/jmh/java/com/google/security/cryptauth/lib/securegcm/Ukey2Benchmark.java b/nearby/connections/ukey2/ukey2_jni/java/src/jmh/java/com/google/security/cryptauth/lib/securegcm/Ukey2Benchmark.java
deleted file mode 100644
index eb063cc..0000000
--- a/nearby/connections/ukey2/ukey2_jni/java/src/jmh/java/com/google/security/cryptauth/lib/securegcm/Ukey2Benchmark.java
+++ /dev/null
@@ -1,83 +0,0 @@
-/*
- * Copyright 2023 Google LLC
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.google.security.cryptauth.lib.securegcm;
-
-import org.openjdk.jmh.annotations.*;
-import org.openjdk.jmh.infra.Blackhole;
-import org.openjdk.jmh.profile.GCProfiler;
-import org.openjdk.jmh.runner.Runner;
-import org.openjdk.jmh.runner.RunnerException;
-import org.openjdk.jmh.runner.options.Options;
-import org.openjdk.jmh.runner.options.OptionsBuilder;
-
-import java.nio.charset.StandardCharsets;
-import java.time.Duration;
-import java.util.concurrent.TimeUnit;
-import java.util.Random;
-
-/**
- * Benchmark for encoding and decoding UKEY2 messages over the JNI, analogous to
- * `ukey2_benches.rs`. The parameters and the operations also roughly matches the that of the Rust
- * Criterion benchmark. That said, since the benchmark infrastructure is different, there will
- * inevitably be differences the skews the number in certain ways – comparison of numbers from the
- * different benchmarks should compared on order-of-magnitudes only. To get the JNI overhead, for
- * example, it would be better use this JMH infra to measure a call into a no-op Rust function,
- * which is a more apples-to-apples comparison.
- *
- * To run this benchmark, run
- * cargo build -p ukey2_jni --release && ./gradlew jmh
- */
-@State(Scope.Benchmark)
-@OutputTimeUnit(TimeUnit.SECONDS)
-@BenchmarkMode(Mode.Throughput)
-public class Ukey2Benchmark {
-
- @State(Scope.Thread)
- public static class ConnectionState {
- D2DConnectionContextV1 connContext;
- D2DConnectionContextV1 serverConnContext;
- @Param({"10", "1024"})
- int sizeKibs;
- byte[] inputBytes;
-
- @Setup
- public void setup() throws Exception {
- D2DHandshakeContext initiatorContext =
- new D2DHandshakeContext(D2DHandshakeContext.Role.Initiator);
- D2DHandshakeContext serverContext =
- new D2DHandshakeContext(D2DHandshakeContext.Role.Responder);
- serverContext.parseHandshakeMessage(initiatorContext.getNextHandshakeMessage());
- initiatorContext.parseHandshakeMessage(serverContext.getNextHandshakeMessage());
- serverContext.parseHandshakeMessage(initiatorContext.getNextHandshakeMessage());
- connContext = initiatorContext.toConnectionContext();
- serverConnContext = serverContext.toConnectionContext();
- Random random = new Random();
- inputBytes = new byte[sizeKibs * 1024];
- random.nextBytes(inputBytes);
- }
- }
-
- @Benchmark
- @Fork(3)
- @Warmup(iterations = 2, time = 500, timeUnit = TimeUnit.MILLISECONDS)
- @Measurement(iterations = 5, time = 500, timeUnit = TimeUnit.MILLISECONDS)
- public void encodeAndDecode(ConnectionState state, Blackhole blackhole) throws Exception {
- byte[] encoded = state.connContext.encodeMessageToPeer(state.inputBytes, null);
- byte[] decoded = state.serverConnContext.decodeMessageFromPeer(encoded, null);
- blackhole.consume(decoded);
- }
-}
diff --git a/nearby/connections/ukey2/ukey2_jni/java/src/jmh/java/com/google/security/cryptauth/lib/securegcm/ukey2/Ukey2Benchmark.java b/nearby/connections/ukey2/ukey2_jni/java/src/jmh/java/com/google/security/cryptauth/lib/securegcm/ukey2/Ukey2Benchmark.java
new file mode 100644
index 0000000..9cca229
--- /dev/null
+++ b/nearby/connections/ukey2/ukey2_jni/java/src/jmh/java/com/google/security/cryptauth/lib/securegcm/ukey2/Ukey2Benchmark.java
@@ -0,0 +1,78 @@
+/*
+ * 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.
+ */
+
+package com.google.security.cryptauth.lib.securegcm.ukey2;
+
+import com.google.security.cryptauth.lib.securegcm.ukey2.D2DHandshakeContext.Role;
+import org.openjdk.jmh.annotations.*;
+import org.openjdk.jmh.infra.Blackhole;
+
+import java.util.concurrent.TimeUnit;
+import java.util.Random;
+
+/**
+ * Benchmark for encoding and decoding UKEY2 messages over the JNI, analogous to `ukey2_benches.rs`.
+ * The parameters and the operations also roughly matches the that of the Rust Criterion benchmark.
+ * That said, since the benchmark infrastructure is different, there will inevitably be differences
+ * the skews the number in certain ways – comparison of numbers from the different benchmarks should
+ * compared on order-of-magnitudes only. To get the JNI overhead, for example, it would be better
+ * use this JMH infra to measure a call into a no-op Rust function, which is a more apples-to-apples
+ * comparison.
+ *
+ * <p>To run this benchmark, run cargo build -p ukey2_jni --release && ./gradlew jmh
+ */
+@State(Scope.Benchmark)
+@OutputTimeUnit(TimeUnit.SECONDS)
+@BenchmarkMode(Mode.Throughput)
+public class Ukey2Benchmark {
+
+ @State(Scope.Thread)
+ public static class ConnectionState {
+ D2DConnectionContextV1 connContext;
+ D2DConnectionContextV1 serverConnContext;
+
+ @Param({"10", "1024"})
+ int sizeKibs;
+
+ byte[] inputBytes;
+
+ @Setup
+ public void setup() throws Exception {
+ D2DHandshakeContext initiatorContext =
+ new D2DHandshakeContext(Role.INITIATOR);
+ D2DHandshakeContext serverContext =
+ new D2DHandshakeContext(Role.RESPONDER);
+ serverContext.parseHandshakeMessage(initiatorContext.getNextHandshakeMessage());
+ initiatorContext.parseHandshakeMessage(serverContext.getNextHandshakeMessage());
+ serverContext.parseHandshakeMessage(initiatorContext.getNextHandshakeMessage());
+ connContext = initiatorContext.toConnectionContext();
+ serverConnContext = serverContext.toConnectionContext();
+ Random random = new Random();
+ inputBytes = new byte[sizeKibs * 1024];
+ random.nextBytes(inputBytes);
+ }
+ }
+
+ @Benchmark
+ @Fork(3)
+ @Warmup(iterations = 2, time = 500, timeUnit = TimeUnit.MILLISECONDS)
+ @Measurement(iterations = 5, time = 500, timeUnit = TimeUnit.MILLISECONDS)
+ public void encodeAndDecode(ConnectionState state, Blackhole blackhole) throws Exception {
+ byte[] encoded = state.connContext.encodeMessageToPeer(state.inputBytes, null);
+ byte[] decoded = state.serverConnContext.decodeMessageFromPeer(encoded, null);
+ blackhole.consume(decoded);
+ }
+}
diff --git a/nearby/connections/ukey2/ukey2_jni/java/src/main/java/com/google/security/cryptauth/lib/securegcm/D2DConnectionContextV1.java b/nearby/connections/ukey2/ukey2_jni/java/src/main/java/com/google/security/cryptauth/lib/securegcm/D2DConnectionContextV1.java
deleted file mode 100644
index 7874cd9..0000000
--- a/nearby/connections/ukey2/ukey2_jni/java/src/main/java/com/google/security/cryptauth/lib/securegcm/D2DConnectionContextV1.java
+++ /dev/null
@@ -1,123 +0,0 @@
-/*
- * Copyright 2023 Google LLC
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.google.security.cryptauth.lib.securegcm;
-
-import javax.annotation.Nonnull;
-import javax.annotation.Nullable;
-
-public class D2DConnectionContextV1 {
-
- static {
- System.loadLibrary("ukey2_jni");
- }
-
- private static native byte[] encode_message_to_peer(long contextPtr, byte[] payload, byte[] associatedData) throws BadHandleException;
-
- private static native byte[] decode_message_from_peer(long contextPtr, byte[] message, byte[] associatedData) throws CryptoException;
-
- private static native byte[] get_session_unique(long contextPtr) throws BadHandleException;
-
- private static native int get_sequence_number_for_encoding(long contextPtr) throws BadHandleException;
-
- private static native int get_sequence_number_for_decoding(long contextPtr) throws BadHandleException;
-
- private static native byte[] save_session(long contextPtr) throws BadHandleException;
-
- private static native long from_saved_session(byte[] savedSessionInfo);
-
- private final long contextPtr;
-
- /**
- * Java wrapper for D2DConnectionContextV1 to interact with the underlying Rust implementation
- *
- * @param contextPtr the handle to the Rust implementation.
- */
- D2DConnectionContextV1(@Nonnull long contextPtr) {
- this.contextPtr = contextPtr;
- }
-
- /**
- * Encode a message to the connection peer using session keys derived from the handshake.
- *
- * @param payload The message to be encrypted.
- * @return The encrypted/encoded message.
- */
- @Nonnull
- public byte[] encodeMessageToPeer(@Nonnull byte[] payload, @Nullable byte[] associatedData) throws BadHandleException {
- return encode_message_to_peer(contextPtr, payload, associatedData);
- }
-
- /**
- * Decodes/decrypts a message from the connection peer.
- *
- * @param message The message received over the connection.
- * @return The decoded message from the connection peer.
- */
- @Nonnull
- public byte[] decodeMessageFromPeer(@Nonnull byte[] message, @Nullable byte[] associatedData) throws CryptoException {
- return decode_message_from_peer(contextPtr, message, associatedData);
- }
-
- /**
- * A unique session identifier derived from session-specific information
- *
- * @return The session unique identifier
- */
- @Nonnull
- public byte[] getSessionUnique() throws BadHandleException {
- return get_session_unique(contextPtr);
- }
-
- /**
- * Returns the encoding sequence number.
- *
- * @return the encoding sequence number.
- */
- public int getSequenceNumberForEncoding() throws BadHandleException {
- return get_sequence_number_for_encoding(contextPtr);
- }
-
- /**
- * Returns the decoding sequence number.
- *
- * @return the decoding sequence number.
- */
- public int getSequenceNumberForDecoding() throws BadHandleException {
- return get_sequence_number_for_decoding(contextPtr);
- }
-
- /**
- * Serializes the current session in a form usable by {@link D2DConnectionContextV1#fromSavedSession}
- *
- * @return a byte array representing the current session.
- */
- @Nonnull
- public byte[] saveSession() throws BadHandleException {
- return save_session(contextPtr);
- }
-
- /**
- * Reconstructs and returns the session originally serialized by {@link D2DConnectionContextV1#saveSession}
- *
- * @param savedSessionInfo the byte array from saveSession()
- * @return a D2DConnectionContextV1 session with the same properties as the context saved.
- */
- public static D2DConnectionContextV1 fromSavedSession(@Nonnull byte[] savedSessionInfo) {
- return new D2DConnectionContextV1(from_saved_session(savedSessionInfo));
- }
-
-}
diff --git a/nearby/connections/ukey2/ukey2_jni/java/src/main/java/com/google/security/cryptauth/lib/securegcm/D2DHandshakeContext.java b/nearby/connections/ukey2/ukey2_jni/java/src/main/java/com/google/security/cryptauth/lib/securegcm/D2DHandshakeContext.java
deleted file mode 100644
index 39f7aa9..0000000
--- a/nearby/connections/ukey2/ukey2_jni/java/src/main/java/com/google/security/cryptauth/lib/securegcm/D2DHandshakeContext.java
+++ /dev/null
@@ -1,122 +0,0 @@
-/*
- * Copyright 2023 Google LLC
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.google.security.cryptauth.lib.securegcm;
-
-import javax.annotation.Nonnull;
-
-public class D2DHandshakeContext {
- static {
- System.loadLibrary("ukey2_jni");
- }
-
- public enum Role {
- INITIATOR,
- RESPONDER,
- }
-
- private final long contextPtr;
-
- private static native boolean is_handshake_complete(long contextPtr) throws BadHandleException;
-
- private static native long create_context(boolean isClient);
-
- private static native byte[] get_next_handshake_message(long contextPtr) throws BadHandleException;
-
- private static native void parse_handshake_message(long contextPtr, byte[] message) throws BadHandleException, HandshakeException;
-
- private static native byte[] get_verification_string(long contextPtr, int length) throws BadHandleException, HandshakeException;
-
- private static native long to_connection_context(long contextPtr) throws HandshakeException;
-
- public D2DHandshakeContext(@Nonnull Role role) {
- this.contextPtr = create_context(role == Role.INITIATOR);
- }
-
- /**
- * Convenience constructor that creates a UKEY2 D2DHandshakeContext for the initiator role.
- *
- * @return a D2DHandshakeContext for the role of initiator in the handshake.
- */
- public static D2DHandshakeContext forInitiator() {
- return new D2DHandshakeContext(Role.INITIATOR);
- }
-
- /**
- * Convenience constructor that creates a UKEY2 D2DHandshakeContext for the initiator role.
- *
- * @return a D2DHandshakeContext for the role of responder/server in the handshake.
- */
- public static D2DHandshakeContext forResponder() {
- return new D2DHandshakeContext(Role.RESPONDER);
- }
-
- /**
- * Function that checks if the handshake is completed.
- *
- * @return true/false depending on if the handshake is complete.
- */
- public boolean isHandshakeComplete() throws BadHandleException {
- return is_handshake_complete(contextPtr);
- }
-
- /**
- * Gets the next handshake message in the exchange.
- *
- * @return handshake message encoded in a SecureMessage.
- */
- @Nonnull
- public byte[] getNextHandshakeMessage() throws BadHandleException {
- return get_next_handshake_message(contextPtr);
- }
-
- /**
- * Parses the handshake message.
- *
- * @param message - handshake message from the other side.
- */
- @Nonnull
- public void parseHandshakeMessage(@Nonnull byte[] message) throws BadHandleException, HandshakeException {
- parse_handshake_message(contextPtr, message);
- }
-
- /**
- * Returns an authentication string suitable for authenticating the handshake out-of-band. Note
- * that the authentication string can be short (e.g., a 6 digit visual confirmation code). Note:
- * this should only be called when {#isHandshakeComplete} returns true.
- * This code is analogous to the authentication string described in the spec.
- *
- * @param length - The length of the returned verification string.
- * @return - The returned verification string as a byte array.
- * @throws BadHandleException - Thrown if the handle is no longer valid, for example after calling {@link D2DHandshakeContext#toConnectionContext}
- * @throws HandshakeException - Thrown if the handshake is not complete when this function is called.
- */
- @Nonnull
- public byte[] getVerificationString(int length) throws BadHandleException, HandshakeException {
- return get_verification_string(contextPtr, length);
- }
-
- /**
- * Function to create a secure communication channel from the handshake after confirming the auth string generated by
- * the handshake out-of-band (i.e. via a user-facing UI).
- *
- * @return a new {@link D2DConnectionContextV1} with the next protocol specified when creating the D2DHandshakeContext.
- * @throws HandshakeException if the handsshake is not complete when this function is called.
- */
- public D2DConnectionContextV1 toConnectionContext() throws HandshakeException {
- return new D2DConnectionContextV1(to_connection_context(contextPtr));
- }
-}
diff --git a/nearby/connections/ukey2/ukey2_jni/java/src/main/java/com/google/security/cryptauth/lib/securegcm/BadHandleException.java b/nearby/connections/ukey2/ukey2_jni/java/src/main/java/com/google/security/cryptauth/lib/securegcm/ukey2/BadHandleException.java
index 2efd7c4..78f0e5e 100644
--- a/nearby/connections/ukey2/ukey2_jni/java/src/main/java/com/google/security/cryptauth/lib/securegcm/BadHandleException.java
+++ b/nearby/connections/ukey2/ukey2_jni/java/src/main/java/com/google/security/cryptauth/lib/securegcm/ukey2/BadHandleException.java
@@ -12,10 +12,11 @@
// See the License for the specific language governing permissions and
// limitations under the License.
-package com.google.security.cryptauth.lib.securegcm;
+package com.google.security.cryptauth.lib.securegcm.ukey2;
/**
- * Represents an unrecoverable error (invalid handle) that has occurred during the handshake/connection.
+ * Represents an unrecoverable error (invalid handle) that has occurred during the
+ * handshake/connection.
*/
public class BadHandleException extends Exception {
public BadHandleException(String message) {
@@ -29,4 +30,4 @@ public class BadHandleException extends Exception {
public BadHandleException(String message, Exception e) {
super(message, e);
}
-} \ No newline at end of file
+}
diff --git a/nearby/connections/ukey2/ukey2_jni/java/src/main/java/com/google/security/cryptauth/lib/securegcm/CryptoException.java b/nearby/connections/ukey2/ukey2_jni/java/src/main/java/com/google/security/cryptauth/lib/securegcm/ukey2/CryptoException.java
index 6abeb53..11b5c9b 100644
--- a/nearby/connections/ukey2/ukey2_jni/java/src/main/java/com/google/security/cryptauth/lib/securegcm/CryptoException.java
+++ b/nearby/connections/ukey2/ukey2_jni/java/src/main/java/com/google/security/cryptauth/lib/securegcm/ukey2/CryptoException.java
@@ -12,11 +12,9 @@
// See the License for the specific language governing permissions and
// limitations under the License.
-package com.google.security.cryptauth.lib.securegcm;
+package com.google.security.cryptauth.lib.securegcm.ukey2;
-/**
- * Represents an unrecoverable error that has occurred during the handshake procedure.
- */
+/** Represents an unrecoverable error that has occurred during the handshake procedure. */
public class CryptoException extends Exception {
public CryptoException(String message) {
super(message);
@@ -29,4 +27,4 @@ public class CryptoException extends Exception {
public CryptoException(String message, Exception e) {
super(message, e);
}
-} \ No newline at end of file
+}
diff --git a/nearby/connections/ukey2/ukey2_jni/java/src/main/java/com/google/security/cryptauth/lib/securegcm/ukey2/D2DConnectionContextV1.java b/nearby/connections/ukey2/ukey2_jni/java/src/main/java/com/google/security/cryptauth/lib/securegcm/ukey2/D2DConnectionContextV1.java
new file mode 100644
index 0000000..9ce2517
--- /dev/null
+++ b/nearby/connections/ukey2/ukey2_jni/java/src/main/java/com/google/security/cryptauth/lib/securegcm/ukey2/D2DConnectionContextV1.java
@@ -0,0 +1,139 @@
+/*
+ * 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.
+ */
+
+package com.google.security.cryptauth.lib.securegcm.ukey2;
+
+import javax.annotation.Nonnull;
+import javax.annotation.Nullable;
+
+public class D2DConnectionContextV1 {
+
+ static {
+ var path = System.getProperty("java.library.path");
+
+ if (path == null) {
+ throw new RuntimeException("Path isn't set.");
+ }
+
+ var paths = java.util.List.of(path.split(";"));
+ paths.forEach(System.out::println);
+
+ System.loadLibrary("ukey2_jni");
+ }
+
+ private static native byte[] encode_message_to_peer(
+ long contextPtr, byte[] payload, byte[] associatedData) throws BadHandleException;
+
+ private static native byte[] decode_message_from_peer(
+ long contextPtr, byte[] message, byte[] associatedData) throws CryptoException;
+
+ private static native byte[] get_session_unique(long contextPtr) throws BadHandleException;
+
+ private static native int get_sequence_number_for_encoding(long contextPtr)
+ throws BadHandleException;
+
+ private static native int get_sequence_number_for_decoding(long contextPtr)
+ throws BadHandleException;
+
+ private static native byte[] save_session(long contextPtr) throws BadHandleException;
+
+ private static native long from_saved_session(byte[] savedSessionInfo);
+
+ private final long contextPtr;
+
+ /**
+ * Java wrapper for D2DConnectionContextV1 to interact with the underlying Rust implementation
+ *
+ * @param contextPtr the handle to the Rust implementation.
+ */
+ D2DConnectionContextV1(@Nonnull long contextPtr) {
+ this.contextPtr = contextPtr;
+ }
+
+ /**
+ * Encode a message to the connection peer using session keys derived from the handshake.
+ *
+ * @param payload The message to be encrypted.
+ * @return The encrypted/encoded message.
+ */
+ @Nonnull
+ public byte[] encodeMessageToPeer(@Nonnull byte[] payload, @Nullable byte[] associatedData)
+ throws BadHandleException {
+ return encode_message_to_peer(contextPtr, payload, associatedData);
+ }
+
+ /**
+ * Decodes/decrypts a message from the connection peer.
+ *
+ * @param message The message received over the connection.
+ * @return The decoded message from the connection peer.
+ */
+ @Nonnull
+ public byte[] decodeMessageFromPeer(@Nonnull byte[] message, @Nullable byte[] associatedData)
+ throws CryptoException {
+ return decode_message_from_peer(contextPtr, message, associatedData);
+ }
+
+ /**
+ * A unique session identifier derived from session-specific information
+ *
+ * @return The session unique identifier
+ */
+ @Nonnull
+ public byte[] getSessionUnique() throws BadHandleException {
+ return get_session_unique(contextPtr);
+ }
+
+ /**
+ * Returns the encoding sequence number.
+ *
+ * @return the encoding sequence number.
+ */
+ public int getSequenceNumberForEncoding() throws BadHandleException {
+ return get_sequence_number_for_encoding(contextPtr);
+ }
+
+ /**
+ * Returns the decoding sequence number.
+ *
+ * @return the decoding sequence number.
+ */
+ public int getSequenceNumberForDecoding() throws BadHandleException {
+ return get_sequence_number_for_decoding(contextPtr);
+ }
+
+ /**
+ * Serializes the current session in a form usable by {@link
+ * D2DConnectionContextV1#fromSavedSession}
+ *
+ * @return a byte array representing the current session.
+ */
+ @Nonnull
+ public byte[] saveSession() throws BadHandleException {
+ return save_session(contextPtr);
+ }
+
+ /**
+ * Reconstructs and returns the session originally serialized by {@link
+ * D2DConnectionContextV1#saveSession}
+ *
+ * @param savedSessionInfo the byte array from saveSession()
+ * @return a D2DConnectionContextV1 session with the same properties as the context saved.
+ */
+ public static D2DConnectionContextV1 fromSavedSession(@Nonnull byte[] savedSessionInfo) {
+ return new D2DConnectionContextV1(from_saved_session(savedSessionInfo));
+ }
+}
diff --git a/nearby/connections/ukey2/ukey2_jni/java/src/main/java/com/google/security/cryptauth/lib/securegcm/ukey2/D2DHandshakeContext.java b/nearby/connections/ukey2/ukey2_jni/java/src/main/java/com/google/security/cryptauth/lib/securegcm/ukey2/D2DHandshakeContext.java
new file mode 100644
index 0000000..429295e
--- /dev/null
+++ b/nearby/connections/ukey2/ukey2_jni/java/src/main/java/com/google/security/cryptauth/lib/securegcm/ukey2/D2DHandshakeContext.java
@@ -0,0 +1,129 @@
+/*
+ * 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.
+ */
+
+package com.google.security.cryptauth.lib.securegcm.ukey2;
+
+import javax.annotation.Nonnull;
+
+public class D2DHandshakeContext {
+ static {
+ System.loadLibrary("ukey2_jni");
+ }
+
+ public enum Role {
+ INITIATOR,
+ RESPONDER,
+ }
+
+ private final long contextPtr;
+
+ private static native boolean is_handshake_complete(long contextPtr) throws BadHandleException;
+
+ private static native long create_context(boolean isClient);
+
+ private static native byte[] get_next_handshake_message(long contextPtr)
+ throws BadHandleException;
+
+ private static native void parse_handshake_message(long contextPtr, byte[] message)
+ throws BadHandleException, HandshakeException;
+
+ private static native byte[] get_verification_string(long contextPtr, int length)
+ throws BadHandleException, HandshakeException;
+
+ private static native long to_connection_context(long contextPtr) throws HandshakeException;
+
+ public D2DHandshakeContext(@Nonnull Role role) {
+ this.contextPtr = create_context(role == Role.INITIATOR);
+ }
+
+ /**
+ * Convenience constructor that creates a UKEY2 D2DHandshakeContext for the initiator role.
+ *
+ * @return a D2DHandshakeContext for the role of initiator in the handshake.
+ */
+ public static D2DHandshakeContext forInitiator() {
+ return new D2DHandshakeContext(Role.INITIATOR);
+ }
+
+ /**
+ * Convenience constructor that creates a UKEY2 D2DHandshakeContext for the initiator role.
+ *
+ * @return a D2DHandshakeContext for the role of responder/server in the handshake.
+ */
+ public static D2DHandshakeContext forResponder() {
+ return new D2DHandshakeContext(Role.RESPONDER);
+ }
+
+ /**
+ * Function that checks if the handshake is completed.
+ *
+ * @return true/false depending on if the handshake is complete.
+ */
+ public boolean isHandshakeComplete() throws BadHandleException {
+ return is_handshake_complete(contextPtr);
+ }
+
+ /**
+ * Gets the next handshake message in the exchange.
+ *
+ * @return handshake message encoded in a SecureMessage.
+ */
+ @Nonnull
+ public byte[] getNextHandshakeMessage() throws BadHandleException {
+ return get_next_handshake_message(contextPtr);
+ }
+
+ /**
+ * Parses the handshake message.
+ *
+ * @param message - handshake message from the other side.
+ */
+ @Nonnull
+ public void parseHandshakeMessage(@Nonnull byte[] message)
+ throws BadHandleException, HandshakeException {
+ parse_handshake_message(contextPtr, message);
+ }
+
+ /**
+ * Returns an authentication string suitable for authenticating the handshake out-of-band. Note
+ * that the authentication string can be short (e.g., a 6 digit visual confirmation code). Note:
+ * this should only be called when {#isHandshakeComplete} returns true. This code is analogous to
+ * the authentication string described in the spec.
+ *
+ * @param length - The length of the returned verification string.
+ * @return - The returned verification string as a byte array.
+ * @throws BadHandleException - Thrown if the handle is no longer valid, for example after calling
+ * {@link D2DHandshakeContext#toConnectionContext}
+ * @throws HandshakeException - Thrown if the handshake is not complete when this function is
+ * called.
+ */
+ @Nonnull
+ public byte[] getVerificationString(int length) throws BadHandleException, HandshakeException {
+ return get_verification_string(contextPtr, length);
+ }
+
+ /**
+ * Function to create a secure communication channel from the handshake after confirming the auth
+ * string generated by the handshake out-of-band (i.e. via a user-facing UI).
+ *
+ * @return a new {@link D2DConnectionContextV1} with the next protocol specified when creating the
+ * D2DHandshakeContext.
+ * @throws HandshakeException if the handsshake is not complete when this function is called.
+ */
+ public D2DConnectionContextV1 toConnectionContext() throws HandshakeException {
+ return new D2DConnectionContextV1(to_connection_context(contextPtr));
+ }
+}
diff --git a/nearby/connections/ukey2/ukey2_jni/java/src/main/java/com/google/security/cryptauth/lib/securegcm/HandshakeException.java b/nearby/connections/ukey2/ukey2_jni/java/src/main/java/com/google/security/cryptauth/lib/securegcm/ukey2/HandshakeException.java
index 17928e9..20d3112 100644
--- a/nearby/connections/ukey2/ukey2_jni/java/src/main/java/com/google/security/cryptauth/lib/securegcm/HandshakeException.java
+++ b/nearby/connections/ukey2/ukey2_jni/java/src/main/java/com/google/security/cryptauth/lib/securegcm/ukey2/HandshakeException.java
@@ -12,21 +12,19 @@
// See the License for the specific language governing permissions and
// limitations under the License.
-package com.google.security.cryptauth.lib.securegcm;
+package com.google.security.cryptauth.lib.securegcm.ukey2;
-/**
- * Represents an unrecoverable error that has occurred during the handshake procedure.
- */
+/** Represents an unrecoverable error that has occurred during the handshake procedure. */
public class HandshakeException extends Exception {
- public HandshakeException(String message) {
- super(message);
- }
+ public HandshakeException(String message) {
+ super(message);
+ }
- public HandshakeException(Exception e) {
- super(e);
- }
+ public HandshakeException(Exception e) {
+ super(e);
+ }
- public HandshakeException(String message, Exception e) {
- super(message, e);
- }
-} \ No newline at end of file
+ public HandshakeException(String message, Exception e) {
+ super(message, e);
+ }
+}
diff --git a/nearby/connections/ukey2/ukey2_jni/java/src/main/java/com/google/security/cryptauth/lib/securegcm/SessionRestoreException.java b/nearby/connections/ukey2/ukey2_jni/java/src/main/java/com/google/security/cryptauth/lib/securegcm/ukey2/SessionRestoreException.java
index c780973..026f8c5 100644
--- a/nearby/connections/ukey2/ukey2_jni/java/src/main/java/com/google/security/cryptauth/lib/securegcm/SessionRestoreException.java
+++ b/nearby/connections/ukey2/ukey2_jni/java/src/main/java/com/google/security/cryptauth/lib/securegcm/ukey2/SessionRestoreException.java
@@ -12,11 +12,9 @@
// See the License for the specific language governing permissions and
// limitations under the License.
-package com.google.security.cryptauth.lib.securegcm;
+package com.google.security.cryptauth.lib.securegcm.ukey2;
-/**
- * Represents an unrecoverable error that has occurred during the handshake procedure.
- */
+/** Represents an unrecoverable error that has occurred during the handshake procedure. */
public class SessionRestoreException extends Exception {
public SessionRestoreException(String message) {
super(message);
@@ -29,4 +27,4 @@ public class SessionRestoreException extends Exception {
public SessionRestoreException(String message, Exception e) {
super(message, e);
}
-} \ No newline at end of file
+}
diff --git a/nearby/connections/ukey2/ukey2_jni/java/src/test/java/com/google/security/cryptauth/lib/securegcm/TestUkey2Protocol.kt b/nearby/connections/ukey2/ukey2_jni/java/src/test/java/com/google/security/cryptauth/lib/securegcm/TestUkey2Protocol.kt
deleted file mode 100644
index 79cbd15..0000000
--- a/nearby/connections/ukey2/ukey2_jni/java/src/test/java/com/google/security/cryptauth/lib/securegcm/TestUkey2Protocol.kt
+++ /dev/null
@@ -1,182 +0,0 @@
-/*
- * Copyright 2023 Google LLC
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-/*
- * This Java source file was generated by the Gradle 'init' task.
- */
-package com.google.security.cryptauth.lib.securegcm
-
-import java.nio.charset.StandardCharsets
-import org.junit.jupiter.api.Assertions.assertArrayEquals
-import org.junit.jupiter.api.Assertions.assertEquals
-import org.junit.jupiter.api.Assertions.assertFalse
-import org.junit.jupiter.api.Assertions.assertNotEquals
-import org.junit.jupiter.api.Assertions.assertTrue
-import org.junit.jupiter.api.Test
-import org.junit.jupiter.api.assertDoesNotThrow
-import org.junit.jupiter.api.assertThrows
-
-// Driver code
-// Tests exception handling and the handshake routine, as well as encrypting/decrypting short message between the server and initiator contexts.
-@Suppress("UNUSED_VARIABLE")
-class TestUkey2Protocol {
- @Test
- fun testHandshake() {
- val initiatorContext =
- D2DHandshakeContext(D2DHandshakeContext.Role.INITIATOR)
- assertFalse(initiatorContext.isHandshakeComplete)
- val serverContext =
- D2DHandshakeContext(D2DHandshakeContext.Role.RESPONDER)
- assertFalse(serverContext.isHandshakeComplete)
- assertDoesNotThrow {
- serverContext.parseHandshakeMessage(initiatorContext.nextHandshakeMessage)
- initiatorContext.parseHandshakeMessage(serverContext.nextHandshakeMessage)
- serverContext.parseHandshakeMessage(initiatorContext.nextHandshakeMessage)
- assertTrue(initiatorContext.isHandshakeComplete)
- assertTrue(serverContext.isHandshakeComplete)
- }
- }
-
- @Test
- fun testSendReceiveMessage() {
- val initiatorContext =
- D2DHandshakeContext(D2DHandshakeContext.Role.INITIATOR)
- val serverContext =
- D2DHandshakeContext(D2DHandshakeContext.Role.RESPONDER)
- assertDoesNotThrow {
- serverContext.parseHandshakeMessage(initiatorContext.nextHandshakeMessage)
- initiatorContext.parseHandshakeMessage(serverContext.nextHandshakeMessage)
- serverContext.parseHandshakeMessage(initiatorContext.nextHandshakeMessage)
- val connContext = initiatorContext.toConnectionContext()
- val serverConnContext = serverContext.toConnectionContext()
- val initialShareString = "Nearby sharing to server"
- val encoded = connContext.encodeMessageToPeer(
- initialShareString.toByteArray(
- StandardCharsets.UTF_8
- ), null
- )
- val response =
- String(serverConnContext.decodeMessageFromPeer(encoded, null), StandardCharsets.UTF_8)
- assertEquals(response, initialShareString)
- }
- }
-
- @Test
- fun testSaveRestoreSession() {
- val initiatorContext =
- D2DHandshakeContext(D2DHandshakeContext.Role.INITIATOR)
- val serverContext =
- D2DHandshakeContext(D2DHandshakeContext.Role.RESPONDER)
- assertDoesNotThrow {
- serverContext.parseHandshakeMessage(initiatorContext.nextHandshakeMessage)
- initiatorContext.parseHandshakeMessage(serverContext.nextHandshakeMessage)
- serverContext.parseHandshakeMessage(initiatorContext.nextHandshakeMessage)
- val connContext = initiatorContext.toConnectionContext()
- val serverConnContext = serverContext.toConnectionContext()
- val initiatorSavedSession = connContext.saveSession()
- val restored = D2DConnectionContextV1.fromSavedSession(initiatorSavedSession)
- assertArrayEquals(connContext.sessionUnique, restored.sessionUnique)
- val initialShareString = "Nearby sharing to server"
- val encoded = serverConnContext.encodeMessageToPeer(
- initialShareString.toByteArray(
- StandardCharsets.UTF_8
- ), null
- )
- val response = String(restored.decodeMessageFromPeer(encoded, null), StandardCharsets.UTF_8)
- assertEquals(response, initialShareString)
- }
- }
-
- @Test
- fun testSaveRestoreBadSession() {
- val initiatorContext =
- D2DHandshakeContext(D2DHandshakeContext.Role.INITIATOR)
- val serverContext =
- D2DHandshakeContext(D2DHandshakeContext.Role.RESPONDER)
- val deriveInitiatorSavedSession = {
- assertDoesNotThrow {
- serverContext.parseHandshakeMessage(initiatorContext.nextHandshakeMessage)
- initiatorContext.parseHandshakeMessage(serverContext.nextHandshakeMessage)
- serverContext.parseHandshakeMessage(initiatorContext.nextHandshakeMessage)
- val connContext = initiatorContext.toConnectionContext()
- val serverConnContext = serverContext.toConnectionContext()
- connContext.saveSession()
- }
- }
- assertThrows<SessionRestoreException> {
- val unused = D2DConnectionContextV1.fromSavedSession(deriveInitiatorSavedSession().copyOfRange(0, 20))
- }
- }
-
- @Test
- fun tryReuseHandshakeContext() {
- val initiatorContext =
- D2DHandshakeContext(D2DHandshakeContext.Role.INITIATOR)
- val serverContext =
- D2DHandshakeContext(D2DHandshakeContext.Role.RESPONDER)
- assertDoesNotThrow {
- serverContext.parseHandshakeMessage(initiatorContext.nextHandshakeMessage)
- initiatorContext.parseHandshakeMessage(serverContext.nextHandshakeMessage)
- serverContext.parseHandshakeMessage(initiatorContext.nextHandshakeMessage)
- val connContext = initiatorContext.toConnectionContext()
- val serverConnContext = serverContext.toConnectionContext()
- }
- assertThrows<BadHandleException> {
- val unused = serverContext.nextHandshakeMessage
- }
- }
-
- @Test
- fun testSendReceiveMessageWithAssociatedData() {
- val initiatorContext =
- D2DHandshakeContext(D2DHandshakeContext.Role.INITIATOR)
- val serverContext =
- D2DHandshakeContext(D2DHandshakeContext.Role.RESPONDER)
- val associatedData = "Associated data.".toByteArray()
- assertDoesNotThrow {
- serverContext.parseHandshakeMessage(initiatorContext.nextHandshakeMessage)
- initiatorContext.parseHandshakeMessage(serverContext.nextHandshakeMessage)
- serverContext.parseHandshakeMessage(initiatorContext.nextHandshakeMessage)
- val connContext = initiatorContext.toConnectionContext()
- val serverConnContext = serverContext.toConnectionContext()
- val initialShareString = "Nearby sharing to server"
- val encoded = connContext.encodeMessageToPeer(
- initialShareString.toByteArray(
- StandardCharsets.UTF_8
- ), associatedData
- )
- val response =
- String(serverConnContext.decodeMessageFromPeer(encoded, associatedData), StandardCharsets.UTF_8)
- assertEquals(response, initialShareString)
- }
- }
-
- @Test
- fun testVerificationString() {
- val initiatorContext =
- D2DHandshakeContext(D2DHandshakeContext.Role.INITIATOR)
- val serverContext =
- D2DHandshakeContext(D2DHandshakeContext.Role.RESPONDER)
- assertDoesNotThrow {
- serverContext.parseHandshakeMessage(initiatorContext.nextHandshakeMessage)
- initiatorContext.parseHandshakeMessage(serverContext.nextHandshakeMessage)
- serverContext.parseHandshakeMessage(initiatorContext.nextHandshakeMessage)
- }
- assert(serverContext.isHandshakeComplete)
- assert(initiatorContext.isHandshakeComplete)
- assertArrayEquals(serverContext.getVerificationString(32), initiatorContext.getVerificationString(32))
- }
-} \ No newline at end of file
diff --git a/nearby/connections/ukey2/ukey2_jni/java/src/test/java/com/google/security/cryptauth/lib/securegcm/ukey2/TestUkey2Protocol.kt b/nearby/connections/ukey2/ukey2_jni/java/src/test/java/com/google/security/cryptauth/lib/securegcm/ukey2/TestUkey2Protocol.kt
new file mode 100644
index 0000000..f770977
--- /dev/null
+++ b/nearby/connections/ukey2/ukey2_jni/java/src/test/java/com/google/security/cryptauth/lib/securegcm/ukey2/TestUkey2Protocol.kt
@@ -0,0 +1,167 @@
+/*
+ * 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.
+ */
+
+/*
+ * This Java source file was generated by the Gradle 'init' task.
+ */
+package com.google.security.cryptauth.lib.securegcm.ukey2
+
+import java.nio.charset.StandardCharsets
+import org.junit.jupiter.api.Assertions.assertArrayEquals
+import org.junit.jupiter.api.Assertions.assertEquals
+import org.junit.jupiter.api.Assertions.assertFalse
+import org.junit.jupiter.api.Assertions.assertTrue
+import org.junit.jupiter.api.Test
+import org.junit.jupiter.api.assertDoesNotThrow
+import org.junit.jupiter.api.assertThrows
+
+// Driver code
+// Tests exception handling and the handshake routine, as well as encrypting/decrypting short
+// message between the server and initiator contexts.
+@Suppress("UNUSED_VARIABLE")
+class TestUkey2Protocol {
+ @Test
+ fun testHandshake() {
+ val initiatorContext = D2DHandshakeContext(D2DHandshakeContext.Role.INITIATOR)
+ println("got initial context")
+ assertFalse(initiatorContext.isHandshakeComplete)
+ val serverContext = D2DHandshakeContext(D2DHandshakeContext.Role.RESPONDER)
+ assertFalse(serverContext.isHandshakeComplete)
+ assertDoesNotThrow {
+ serverContext.parseHandshakeMessage(initiatorContext.nextHandshakeMessage)
+ initiatorContext.parseHandshakeMessage(serverContext.nextHandshakeMessage)
+ serverContext.parseHandshakeMessage(initiatorContext.nextHandshakeMessage)
+ assertTrue(initiatorContext.isHandshakeComplete)
+ assertTrue(serverContext.isHandshakeComplete)
+ }
+ }
+
+ @Test
+ fun testSendReceiveMessage() {
+ val initiatorContext = D2DHandshakeContext(D2DHandshakeContext.Role.INITIATOR)
+ val serverContext = D2DHandshakeContext(D2DHandshakeContext.Role.RESPONDER)
+ assertDoesNotThrow {
+ serverContext.parseHandshakeMessage(initiatorContext.nextHandshakeMessage)
+ initiatorContext.parseHandshakeMessage(serverContext.nextHandshakeMessage)
+ serverContext.parseHandshakeMessage(initiatorContext.nextHandshakeMessage)
+ val connContext = initiatorContext.toConnectionContext()
+ val serverConnContext = serverContext.toConnectionContext()
+ val initialShareString = "Nearby sharing to server"
+ val encoded =
+ connContext.encodeMessageToPeer(
+ initialShareString.toByteArray(StandardCharsets.UTF_8), null)
+ val response =
+ String(serverConnContext.decodeMessageFromPeer(encoded, null), StandardCharsets.UTF_8)
+ assertEquals(response, initialShareString)
+ }
+ }
+
+ @Test
+ fun testSaveRestoreSession() {
+ val initiatorContext = D2DHandshakeContext(D2DHandshakeContext.Role.INITIATOR)
+ val serverContext = D2DHandshakeContext(D2DHandshakeContext.Role.RESPONDER)
+ assertDoesNotThrow {
+ serverContext.parseHandshakeMessage(initiatorContext.nextHandshakeMessage)
+ initiatorContext.parseHandshakeMessage(serverContext.nextHandshakeMessage)
+ serverContext.parseHandshakeMessage(initiatorContext.nextHandshakeMessage)
+ val connContext = initiatorContext.toConnectionContext()
+ val serverConnContext = serverContext.toConnectionContext()
+ val initiatorSavedSession = connContext.saveSession()
+ val restored = D2DConnectionContextV1.fromSavedSession(initiatorSavedSession)
+ assertArrayEquals(connContext.sessionUnique, restored.sessionUnique)
+ val initialShareString = "Nearby sharing to server"
+ val encoded =
+ serverConnContext.encodeMessageToPeer(
+ initialShareString.toByteArray(StandardCharsets.UTF_8), null)
+ val response = String(restored.decodeMessageFromPeer(encoded, null), StandardCharsets.UTF_8)
+ assertEquals(response, initialShareString)
+ }
+ }
+
+ @Test
+ fun testSaveRestoreBadSession() {
+ val initiatorContext = D2DHandshakeContext(D2DHandshakeContext.Role.INITIATOR)
+ val serverContext = D2DHandshakeContext(D2DHandshakeContext.Role.RESPONDER)
+ val deriveInitiatorSavedSession = {
+ assertDoesNotThrow {
+ serverContext.parseHandshakeMessage(initiatorContext.nextHandshakeMessage)
+ initiatorContext.parseHandshakeMessage(serverContext.nextHandshakeMessage)
+ serverContext.parseHandshakeMessage(initiatorContext.nextHandshakeMessage)
+ val connContext = initiatorContext.toConnectionContext()
+ val serverConnContext = serverContext.toConnectionContext()
+ connContext.saveSession()
+ }
+ }
+ assertThrows<SessionRestoreException> {
+ val unused =
+ D2DConnectionContextV1.fromSavedSession(deriveInitiatorSavedSession().copyOfRange(0, 20))
+ }
+ }
+
+ @Test
+ fun tryReuseHandshakeContext() {
+ val initiatorContext = D2DHandshakeContext(D2DHandshakeContext.Role.INITIATOR)
+ val serverContext = D2DHandshakeContext(D2DHandshakeContext.Role.RESPONDER)
+ assertDoesNotThrow {
+ serverContext.parseHandshakeMessage(initiatorContext.nextHandshakeMessage)
+ initiatorContext.parseHandshakeMessage(serverContext.nextHandshakeMessage)
+ serverContext.parseHandshakeMessage(initiatorContext.nextHandshakeMessage)
+ val connContext = initiatorContext.toConnectionContext()
+ val serverConnContext = serverContext.toConnectionContext()
+ }
+ assertThrows<BadHandleException> {
+ val unused = serverContext.nextHandshakeMessage
+ }
+ }
+
+ @Test
+ fun testSendReceiveMessageWithAssociatedData() {
+ val initiatorContext = D2DHandshakeContext(D2DHandshakeContext.Role.INITIATOR)
+ val serverContext = D2DHandshakeContext(D2DHandshakeContext.Role.RESPONDER)
+ val associatedData = "Associated data.".toByteArray()
+ assertDoesNotThrow {
+ serverContext.parseHandshakeMessage(initiatorContext.nextHandshakeMessage)
+ initiatorContext.parseHandshakeMessage(serverContext.nextHandshakeMessage)
+ serverContext.parseHandshakeMessage(initiatorContext.nextHandshakeMessage)
+ val connContext = initiatorContext.toConnectionContext()
+ val serverConnContext = serverContext.toConnectionContext()
+ val initialShareString = "Nearby sharing to server"
+ val encoded =
+ connContext.encodeMessageToPeer(
+ initialShareString.toByteArray(StandardCharsets.UTF_8), associatedData)
+ val response =
+ String(
+ serverConnContext.decodeMessageFromPeer(encoded, associatedData),
+ StandardCharsets.UTF_8)
+ assertEquals(response, initialShareString)
+ }
+ }
+
+ @Test
+ fun testVerificationString() {
+ val initiatorContext = D2DHandshakeContext(D2DHandshakeContext.Role.INITIATOR)
+ val serverContext = D2DHandshakeContext(D2DHandshakeContext.Role.RESPONDER)
+ assertDoesNotThrow {
+ serverContext.parseHandshakeMessage(initiatorContext.nextHandshakeMessage)
+ initiatorContext.parseHandshakeMessage(serverContext.nextHandshakeMessage)
+ serverContext.parseHandshakeMessage(initiatorContext.nextHandshakeMessage)
+ }
+ assert(serverContext.isHandshakeComplete)
+ assert(initiatorContext.isHandshakeComplete)
+ assertArrayEquals(
+ serverContext.getVerificationString(32), initiatorContext.getVerificationString(32))
+ }
+}
diff --git a/nearby/connections/ukey2/ukey2_jni/src/lib.rs b/nearby/connections/ukey2/ukey2_jni/src/lib.rs
index 0e11793..c500ddd 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::JClass;
@@ -34,13 +40,7 @@ use ukey2_connections::{
ServerD2DHandshakeContext,
};
-cfg_if::cfg_if! {
- if #[cfg(feature = "rustcrypto")] {
- use crypto_provider_rustcrypto::RustCrypto as CryptoProvider;
- } else {
- use crypto_provider_openssl::Openssl as CryptoProvider;
- }
-}
+use crypto_provider_default::CryptoProviderImpl as CryptoProvider;
// Handle management
type D2DBox = Box<dyn D2DHandshakeContext>;
@@ -58,14 +58,28 @@ fn generate_handle() -> u64 {
}
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 mut map = HANDLE_MAPPING.lock();
+ while map.contains_key(&handle) {
+ handle = generate_handle();
+ }
+
+ let result = map.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 mut map = CONNECTION_HANDLE_MAPPING.lock();
+ while map.contains_key(&handle) {
+ handle = generate_handle();
+ }
+
+ let result = map.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
}
@@ -77,10 +91,11 @@ enum JniError {
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,
+pub extern "system" fn Java_com_google_security_cryptauth_lib_securegcm_ukey2_D2DHandshakeContext_is_1handshake_1complete(
+ env: JNIEnv,
_: JClass,
context_handle: jlong,
) -> jboolean {
@@ -88,14 +103,15 @@ pub extern "system" fn Java_com_google_security_cryptauth_lib_securegcm_D2DHands
if let Some(ctx) = HANDLE_MAPPING.lock().get(&(context_handle as u64)) {
is_complete = ctx.is_handshake_complete();
} else {
- env.throw_new("com/google/security/cryptauth/lib/securegcm/BadHandleException", "")
+ env.throw_new("com/google/security/cryptauth/lib/securegcm/ukey2/BadHandleException", "")
.expect("failed to find error class");
}
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(
+pub extern "system" fn Java_com_google_security_cryptauth_lib_securegcm_ukey2_D2DHandshakeContext_create_1context(
_: JNIEnv,
_: JClass,
is_client: jboolean,
@@ -113,9 +129,10 @@ pub extern "system" fn Java_com_google_security_cryptauth_lib_securegcm_D2DHands
}
}
+/// 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,
+pub extern "system" fn Java_com_google_security_cryptauth_lib_securegcm_ukey2_D2DHandshakeContext_get_1next_1handshake_1message(
+ env: JNIEnv,
_: JClass,
context_handle: jlong,
) -> jbyteArray {
@@ -123,7 +140,7 @@ pub extern "system" fn Java_com_google_security_cryptauth_lib_securegcm_D2DHands
let next_message = if let Some(ctx) = HANDLE_MAPPING.lock().get(&(context_handle as u64)) {
ctx.get_next_handshake_message()
} else {
- env.throw_new("com/google/security/cryptauth/lib/securegcm/BadHandleException", "")
+ env.throw_new("com/google/security/cryptauth/lib/securegcm/ukey2/BadHandleException", "")
.expect("failed to find error class");
None
};
@@ -135,11 +152,12 @@ pub extern "system" fn Java_com_google_security_cryptauth_lib_securegcm_D2DHands
}
}
-#[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.
-pub extern "system" fn Java_com_google_security_cryptauth_lib_securegcm_D2DHandshakeContext_parse_1handshake_1message(
- mut env: JNIEnv,
+#[no_mangle]
+pub extern "system" fn Java_com_google_security_cryptauth_lib_securegcm_ukey2_D2DHandshakeContext_parse_1handshake_1message(
+ env: JNIEnv,
_: JClass,
context_handle: jlong,
message: jbyteArray,
@@ -148,14 +166,14 @@ pub extern "system" fn Java_com_google_security_cryptauth_lib_securegcm_D2DHands
let result = if let Some(ctx) = HANDLE_MAPPING.lock().get_mut(&(context_handle as u64)) {
ctx.handle_handshake_message(rust_buffer.as_slice()).map_err(JniError::HandleMessageError)
} else {
- env.throw_new("com/google/security/cryptauth/lib/securegcm/BadHandleException", "")
+ env.throw_new("com/google/security/cryptauth/lib/securegcm/ukey2/BadHandleException", "")
.expect("failed to find error class");
Err(JniError::BadHandle)
};
if let Err(e) = result {
if !env.exception_check().unwrap() {
env.throw_new(
- "com/google/security/cryptauth/lib/securegcm/HandshakeException",
+ "com/google/security/cryptauth/lib/securegcm/ukey2/HandshakeException",
match e {
JniError::BadHandle => "Bad handle",
JniError::DecodeError(_) => "Unable to decode message",
@@ -168,9 +186,11 @@ pub extern "system" fn Java_com_google_security_cryptauth_lib_securegcm_D2DHands
}
}
+/// 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,
+pub extern "system" fn Java_com_google_security_cryptauth_lib_securegcm_ukey2_D2DHandshakeContext_get_1verification_1string(
+ env: JNIEnv,
_: JClass,
context_handle: jlong,
length: jint,
@@ -181,14 +201,14 @@ pub extern "system" fn Java_com_google_security_cryptauth_lib_securegcm_D2DHands
.map_err(|_| JniError::HandshakeError(HandshakeError::HandshakeNotComplete))
.map(|h| h.auth_string::<CryptoProvider>().derive_vec(length as usize).unwrap())
} else {
- env.throw_new("com/google/security/cryptauth/lib/securegcm/BadHandleException", "")
+ env.throw_new("com/google/security/cryptauth/lib/securegcm/ukey2/BadHandleException", "")
.expect("failed to find error class");
Err(JniError::BadHandle)
};
if let Err(e) = result {
if !env.exception_check().unwrap() {
env.throw_new(
- "com/google/security/cryptauth/lib/securegcm/HandshakeException",
+ "com/google/security/cryptauth/lib/securegcm/ukey2/HandshakeException",
match e {
JniError::BadHandle => "Bad handle",
JniError::DecodeError(_) => "Unable to decode message",
@@ -205,9 +225,11 @@ pub extern "system" fn Java_com_google_security_cryptauth_lib_securegcm_D2DHands
}
}
+/// 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,
+pub extern "system" fn Java_com_google_security_cryptauth_lib_securegcm_ukey2_D2DHandshakeContext_to_1connection_1context(
+ env: JNIEnv,
_: JClass,
context_handle: jlong,
) -> jlong {
@@ -218,7 +240,7 @@ pub extern "system" fn Java_com_google_security_cryptauth_lib_securegcm_D2DHands
};
if let Err(error) = conn_context {
env.throw_new(
- "com/google/security/cryptauth/lib/securegcm/HandshakeException",
+ "com/google/security/cryptauth/lib/securegcm/ukey2/HandshakeException",
match error {
JniError::BadHandle => "Bad context handle",
JniError::HandshakeError(_) => "Handshake not complete",
@@ -228,18 +250,19 @@ pub extern "system" fn Java_com_google_security_cryptauth_lib_securegcm_D2DHands
.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.
-pub extern "system" fn Java_com_google_security_cryptauth_lib_securegcm_D2DConnectionContextV1_encode_1message_1to_1peer(
- mut env: JNIEnv,
+#[no_mangle]
+pub extern "system" fn Java_com_google_security_cryptauth_lib_securegcm_ukey2_D2DConnectionContextV1_encode_1message_1to_1peer(
+ env: JNIEnv,
_: JClass,
context_handle: jlong,
payload: jbyteArray,
@@ -268,18 +291,21 @@ pub extern "system" fn Java_com_google_security_cryptauth_lib_securegcm_D2DConne
if let Ok(ret_vec) = result {
env.byte_array_from_slice(ret_vec.as_slice()).expect("unable to create jByteArray")
} else {
- env.throw_new("com/google/security/cryptauth/lib/securegcm/BadHandleException", "")
+ env.throw_new("com/google/security/cryptauth/lib/securegcm/ukey2/BadHandleException", "")
.expect("failed to find error class");
empty_array
}
}
-#[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.
-pub extern "system" fn Java_com_google_security_cryptauth_lib_securegcm_D2DConnectionContextV1_decode_1message_1from_1peer(
- mut env: JNIEnv,
+#[no_mangle]
+pub extern "system" fn Java_com_google_security_cryptauth_lib_securegcm_ukey2_D2DConnectionContextV1_decode_1message_1from_1peer(
+ env: JNIEnv,
_: JClass,
context_handle: jlong,
message: jbyteArray,
@@ -308,7 +334,7 @@ pub extern "system" fn Java_com_google_security_cryptauth_lib_securegcm_D2DConne
env.byte_array_from_slice(message.as_slice()).expect("unable to create jByteArray")
} else {
env.throw_new(
- "com/google/security/cryptauth/lib/securegcm/CryptoException",
+ "com/google/security/cryptauth/lib/securegcm/ukey2/CryptoException",
match result.unwrap_err() {
JniError::BadHandle => "Bad context handle",
JniError::DecodeError(e) => match e {
@@ -324,39 +350,43 @@ pub extern "system" fn Java_com_google_security_cryptauth_lib_securegcm_D2DConne
}
}
+/// 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,
+pub extern "system" fn Java_com_google_security_cryptauth_lib_securegcm_ukey2_D2DConnectionContextV1_get_1sequence_1number_1for_1encoding(
+ env: JNIEnv,
_: JClass,
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", "")
+ env.throw_new("com/google/security/cryptauth/lib/securegcm/ukey2/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,
+pub extern "system" fn Java_com_google_security_cryptauth_lib_securegcm_ukey2_D2DConnectionContextV1_get_1sequence_1number_1for_1decoding(
+ env: JNIEnv,
_: JClass,
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", "")
+ env.throw_new("com/google/security/cryptauth/lib/securegcm/ukey2/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,
+pub extern "system" fn Java_com_google_security_cryptauth_lib_securegcm_ukey2_D2DConnectionContextV1_save_1session(
+ env: JNIEnv,
_: JClass,
context_handle: jlong,
) -> jbyteArray {
@@ -364,17 +394,18 @@ pub extern "system" fn Java_com_google_security_cryptauth_lib_securegcm_D2DConne
if let Some(ctx) = CONNECTION_HANDLE_MAPPING.lock().get(&(context_handle as u64)) {
env.byte_array_from_slice(ctx.save_session().as_slice()).expect("unable to save session")
} else {
- env.throw_new("com/google/security/cryptauth/lib/securegcm/BadHandleException", "")
+ env.throw_new("com/google/security/cryptauth/lib/securegcm/ukey2/BadHandleException", "")
.expect("failed to find error class");
empty_array
}
}
+/// 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,
+pub extern "system" fn Java_com_google_security_cryptauth_lib_securegcm_ukey2_D2DConnectionContextV1_from_1saved_1session(
+ env: JNIEnv,
_: JClass,
session_info: jbyteArray,
) -> jlong {
@@ -385,7 +416,7 @@ pub extern "system" fn Java_com_google_security_cryptauth_lib_securegcm_D2DConne
D2DConnectionContextV1::from_saved_session::<CryptoProvider>(session_info_rust.as_slice());
if ctx.is_err() {
env.throw_new(
- "com/google/security/cryptauth/lib/securegcm/SessionRestoreException",
+ "com/google/security/cryptauth/lib/securegcm/ukey2/SessionRestoreException",
match ctx.err().unwrap() {
DeserializeError::BadDataLength => "DeserializeError: bad session_info length",
DeserializeError::BadProtocolVersion => "DeserializeError: bad protocol version",
@@ -400,9 +431,12 @@ pub extern "system" fn Java_com_google_security_cryptauth_lib_securegcm_D2DConne
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,
+pub extern "system" fn Java_com_google_security_cryptauth_lib_securegcm_ukey2_D2DConnectionContextV1_get_1session_1unique(
+ env: JNIEnv,
_: JClass,
context_handle: jlong,
) -> jbyteArray {
@@ -411,7 +445,7 @@ pub extern "system" fn Java_com_google_security_cryptauth_lib_securegcm_D2DConne
env.byte_array_from_slice(ctx.get_session_unique::<CryptoProvider>().as_slice())
.expect("unable to get unique session id")
} else {
- env.throw_new("com/google/security/cryptauth/lib/securegcm/BadHandleException", "")
+ env.throw_new("com/google/security/cryptauth/lib/securegcm/ukey2/BadHandleException", "")
.expect("failed to find error class");
empty_array
}
diff --git a/nearby/connections/ukey2/ukey2_proto/src/ukey2_all_proto/device_to_device_messages.rs b/nearby/connections/ukey2/ukey2_proto/src/ukey2_all_proto/device_to_device_messages.rs
index 3c2da35..71c247b 100644
--- a/nearby/connections/ukey2/ukey2_proto/src/ukey2_all_proto/device_to_device_messages.rs
+++ b/nearby/connections/ukey2/ukey2_proto/src/ukey2_all_proto/device_to_device_messages.rs
@@ -13,7 +13,7 @@
// limitations under the License.
// This file is generated by rust-protobuf 3.2.0. Do not edit
-// .proto file is parsed by protoc 3.19.1
+// .proto file is parsed by protoc 3.21.12
// @generated
// https://github.com/rust-lang/rust-clippy/issues/702
diff --git a/nearby/connections/ukey2/ukey2_proto/src/ukey2_all_proto/securegcm.rs b/nearby/connections/ukey2/ukey2_proto/src/ukey2_all_proto/securegcm.rs
index 3231440..70a927d 100644
--- a/nearby/connections/ukey2/ukey2_proto/src/ukey2_all_proto/securegcm.rs
+++ b/nearby/connections/ukey2/ukey2_proto/src/ukey2_all_proto/securegcm.rs
@@ -13,7 +13,7 @@
// limitations under the License.
// This file is generated by rust-protobuf 3.2.0. Do not edit
-// .proto file is parsed by protoc 3.19.1
+// .proto file is parsed by protoc 3.21.12
// @generated
// https://github.com/rust-lang/rust-clippy/issues/702
diff --git a/nearby/connections/ukey2/ukey2_proto/src/ukey2_all_proto/securemessage.rs b/nearby/connections/ukey2/ukey2_proto/src/ukey2_all_proto/securemessage.rs
index 161e0be..d83d137 100644
--- a/nearby/connections/ukey2/ukey2_proto/src/ukey2_all_proto/securemessage.rs
+++ b/nearby/connections/ukey2/ukey2_proto/src/ukey2_all_proto/securemessage.rs
@@ -13,7 +13,7 @@
// limitations under the License.
// This file is generated by rust-protobuf 3.2.0. Do not edit
-// .proto file is parsed by protoc 3.19.1
+// .proto file is parsed by protoc 3.21.12
// @generated
// https://github.com/rust-lang/rust-clippy/issues/702
diff --git a/nearby/connections/ukey2/ukey2_proto/src/ukey2_all_proto/ukey.rs b/nearby/connections/ukey2/ukey2_proto/src/ukey2_all_proto/ukey.rs
index 5370207..9be6b3d 100644
--- a/nearby/connections/ukey2/ukey2_proto/src/ukey2_all_proto/ukey.rs
+++ b/nearby/connections/ukey2/ukey2_proto/src/ukey2_all_proto/ukey.rs
@@ -13,7 +13,7 @@
// limitations under the License.
// This file is generated by rust-protobuf 3.2.0. Do not edit
-// .proto file is parsed by protoc 3.19.1
+// .proto file is parsed by protoc 3.21.12
// @generated
// https://github.com/rust-lang/rust-clippy/issues/702
diff --git a/nearby/connections/ukey2/ukey2_shell/Cargo.toml b/nearby/connections/ukey2/ukey2_shell/Cargo.toml
index d1f48e4..2ac12fa 100644
--- a/nearby/connections/ukey2/ukey2_shell/Cargo.toml
+++ b/nearby/connections/ukey2/ukey2_shell/Cargo.toml
@@ -4,11 +4,11 @@ version.workspace = true
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"] }
+clap = { workspace = true, 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 @@ fn main() {
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 60f8626..1a588bb 100644
--- a/nearby/crypto/crypto_provider/Cargo.toml
+++ b/nearby/crypto/crypto_provider/Cargo.toml
@@ -4,11 +4,17 @@ version.workspace = true
edition.workspace = true
publish.workspace = true
+[lints]
+workspace = true
+
+[dependencies]
+tinyvec.workspace = true
+
[dev-dependencies]
criterion.workspace = true
hex-literal.workspace = true
crypto_provider_openssl.workspace = true
-crypto_provider_rustcrypto.workspace = true
+crypto_provider_rustcrypto = { workspace = true, features = ["std"] }
rand_ext.workspace = true
rand.workspace = true
@@ -17,6 +23,7 @@ default = ["alloc"]
std = []
alloc = []
test_vectors = []
+raw_private_key_permit = []
[[bench]]
name = "hmac_bench"
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 constant_time_eq_equals(c: &mut Criterion) {
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 @@ fn hkdf_sha256_operations<C: CryptoProvider>(c: &mut Criterion) {
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 @@ fn hmac_sha256_operations<C: CryptoProvider>(c: &mut Criterion) {
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 @@ fn hmac_sha512_operations<C: CryptoProvider>(c: &mut Criterion) {
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/aead.rs b/nearby/crypto/crypto_provider/src/aead.rs
new file mode 100644
index 0000000..165272e
--- /dev/null
+++ b/nearby/crypto/crypto_provider/src/aead.rs
@@ -0,0 +1,86 @@
+// 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.
+
+#[cfg(feature = "alloc")]
+extern crate alloc;
+#[cfg(feature = "alloc")]
+use alloc::vec::Vec;
+
+/// An implementation of AES-GCM-SIV.
+///
+/// An AesGcmSiv impl may be used for encryption and decryption.
+pub trait AesGcmSiv: Aead<Nonce = [u8; 12]> {}
+
+/// An implementation of AES-GCM.
+///
+/// An AesGcm impl may be used for encryption and decryption.
+pub trait AesGcm: Aead<Nonce = [u8; 12]> {}
+
+/// Error returned on unsuccessful AEAD operation.
+#[derive(Debug)]
+pub struct AeadError;
+
+/// Initializes an AEAD
+pub trait AeadInit<K: crate::aes::AesKey> {
+ /// Instantiates a new instance of the AEAD from key material.
+ fn new(key: &K) -> Self;
+}
+
+/// Authenticated Encryption with Associated Data (AEAD) algorithm, where `N` is the size of the
+/// Nonce. Encrypts and decrypts buffers in-place.
+pub trait Aead {
+ /// The size of the authentication tag, this is appended to the message on the encrypt operation
+ /// and truncated from the plaintext after decrypting.
+ const TAG_SIZE: usize;
+
+ /// The cryptographic nonce used by the AEAD. The nonce must be unique for all messages with
+ /// the same key. This is critically important - nonce reuse may completely undermine the
+ /// security of the AEAD. Nonces may be predictable and public, so long as they are unique.
+ type Nonce: AsRef<[u8]>;
+
+ /// The type of the tag, which should always be [u8; Self::TAG_SIZE].
+ type Tag: AsRef<[u8]>;
+
+ /// Encrypt the given buffer containing a plaintext message. On success returns the encrypted
+ /// `msg` and appended auth tag, which will result in a Vec which is `Self::TAG_SIZE` bytes
+ /// greater than the initial message.
+ #[cfg(feature = "alloc")]
+ fn encrypt(&self, msg: &[u8], aad: &[u8], nonce: &Self::Nonce) -> Result<Vec<u8>, AeadError>;
+
+ /// Encrypt the given buffer containing a plaintext message in-place, and returns the tag in the
+ /// result value.
+ fn encrypt_detached(
+ &self,
+ msg: &mut [u8],
+ aad: &[u8],
+ nonce: &Self::Nonce,
+ ) -> Result<Self::Tag, AeadError>;
+
+ /// Decrypt the message, returning the decrypted plaintext or an error in the event the
+ /// provided authentication tag does not match the given ciphertext. On success the returned
+ /// Vec will only contain the plaintext and so will be `Self::TAG_SIZE` bytes less than the
+ /// initial message.
+ #[cfg(feature = "alloc")]
+ fn decrypt(&self, msg: &[u8], aad: &[u8], nonce: &Self::Nonce) -> Result<Vec<u8>, AeadError>;
+
+ /// Decrypt the message in-place, returning an error and leaving the input `msg` unchanged in
+ /// the event the provided authentication tag does not match the given ciphertext.
+ fn decrypt_detached(
+ &self,
+ msg: &mut [u8],
+ aad: &[u8],
+ nonce: &Self::Nonce,
+ tag: &Self::Tag,
+ ) -> Result<(), AeadError>;
+}
diff --git a/nearby/crypto/crypto_provider/src/aead/mod.rs b/nearby/crypto/crypto_provider/src/aead/mod.rs
deleted file mode 100644
index 27284a9..0000000
--- a/nearby/crypto/crypto_provider/src/aead/mod.rs
+++ /dev/null
@@ -1,50 +0,0 @@
-// Copyright 2023 Google LLC
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-// http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-extern crate alloc;
-use alloc::vec::Vec;
-
-/// Contains traits for the AES-GCM-SIV AEAD algorithm.
-pub mod aes_gcm_siv;
-
-/// Error returned on unsuccessful AEAD operation.
-pub struct AeadError;
-
-/// Authenticated Encryption with Associated Data (AEAD) algorithm, where `N` is the size of the
-/// Nonce. Encrypts and decrypts buffers in-place.
-pub trait Aead {
- /// The size of the authentication tag, this is appended to the message on the encrypt operation
- /// and truncated from the plaintext after decrypting.
- const TAG_SIZE: usize;
-
- /// The cryptographic nonce used by the AEAD. The nonce must be unique for all messages with
- /// the same key. This is critically important - nonce reuse may completely undermine the
- /// security of the AEAD. Nonces may be predictable and public, so long as they are unique.
- type Nonce;
-
- /// The key material used to initialize the AEAD.
- type Key;
-
- /// Instantiates a new instance of the AEAD from key material.
- fn new(key: &Self::Key) -> Self;
-
- /// Encrypt the given buffer containing a plaintext message in-place. On success increases the
- /// buffer by `Self::TAG_SIZE` bytes and appends the auth tag to the end of `msg`.
- fn encrypt(&self, msg: &mut Vec<u8>, aad: &[u8], nonce: &Self::Nonce) -> Result<(), AeadError>;
-
- /// Decrypt the message in-place, returning an error in the event the provided authentication
- /// tag does not match the given ciphertext. The buffer will be truncated to the length of the
- /// original plaintext message upon success.
- fn decrypt(&self, msg: &mut Vec<u8>, aad: &[u8], nonce: &Self::Nonce) -> Result<(), AeadError>;
-}
diff --git a/nearby/crypto/crypto_provider/src/aes/cbc.rs b/nearby/crypto/crypto_provider/src/aes/cbc.rs
index e32c588..82a857b 100644
--- a/nearby/crypto/crypto_provider/src/aes/cbc.rs
+++ b/nearby/crypto/crypto_provider/src/aes/cbc.rs
@@ -14,7 +14,10 @@
//! Traits for AES-CBC 256 with PKCS7 padding.
+#[cfg(feature = "alloc")]
extern crate alloc;
+use crate::tinyvec::SliceVec;
+#[cfg(feature = "alloc")]
use alloc::vec::Vec;
use super::Aes256Key;
@@ -24,15 +27,54 @@ pub type AesCbcIv = [u8; 16];
/// Trait for implementing AES-CBC with PKCS7 padding.
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)
+ .expect("Padded output length is larger than usize::MAX")
+ }
+
/// Encrypt message using `key` and `iv`, returning a ciphertext.
+ #[cfg(feature = "alloc")]
fn encrypt(key: &Aes256Key, iv: &AesCbcIv, message: &[u8]) -> Vec<u8>;
+
+ /// Encrypt message using `key` and `iv` in-place in the given `message` vec. The given slice
+ /// vec should have enough capacity to contain both the ciphertext and the padding (which can be
+ /// calculated from `padded_output_len()`). If it doesn't have enough capacity, an error will be
+ /// returned. The contents of the input `message` buffer is undefined in that case.
+ fn encrypt_in_place(
+ key: &Aes256Key,
+ iv: &AesCbcIv,
+ message: &mut SliceVec<u8>,
+ ) -> Result<(), EncryptionError>;
+
/// Decrypt ciphertext using `key` and `iv`, returning the original message if `Ok()` otherwise
/// a `DecryptionError` indicating the type of error that occurred while decrypting.
+ #[cfg(feature = "alloc")]
fn decrypt(
key: &Aes256Key,
iv: &AesCbcIv,
ciphertext: &[u8],
) -> Result<Vec<u8>, DecryptionError>;
+
+ /// Decrypt ciphertext using `key` and `iv` and unpad it in-place. Returning the original
+ /// message if `Ok()` otherwise a `DecryptionError` indicating the type of error that occurred
+ /// while decrypting. In that case, the contents of the `ciphertext` buffer is undefined.
+ fn decrypt_in_place(
+ key: &Aes256Key,
+ iv: &AesCbcIv,
+ ciphertext: &mut SliceVec<u8>,
+ ) -> Result<(), DecryptionError>;
+}
+
+/// Error type for describing what went wrong encrypting a message.
+#[derive(Debug, PartialEq, Eq)]
+pub enum EncryptionError {
+ /// Failed to add PKCS7 padding to the output when encrypting a message. This typically happens
+ /// when the given output buffer does not have enough capacity to append the padding.
+ PaddingFailed,
}
/// Error type for describing what went wrong decrypting a ciphertext.
@@ -43,3 +85,65 @@ pub enum DecryptionError {
/// correctly. Exposing padding errors can cause a padding oracle vulnerability.
BadPadding,
}
+
+#[cfg(test)]
+mod test {
+ #[cfg(feature = "alloc")]
+ extern crate alloc;
+ #[cfg(feature = "alloc")]
+ use alloc::vec::Vec;
+
+ use crate::aes::Aes256Key;
+ use crate::tinyvec::SliceVec;
+
+ use super::{AesCbcIv, AesCbcPkcs7Padded, DecryptionError, EncryptionError};
+
+ #[test]
+ fn test_padded_output_len() {
+ assert_eq!(AesCbcPkcs7PaddedStub::padded_output_len(0), 16);
+ assert_eq!(AesCbcPkcs7PaddedStub::padded_output_len(15), 16);
+ assert_eq!(AesCbcPkcs7PaddedStub::padded_output_len(16), 32);
+ assert_eq!(AesCbcPkcs7PaddedStub::padded_output_len(30), 32);
+ assert_eq!(AesCbcPkcs7PaddedStub::padded_output_len(32), 48);
+ }
+
+ #[test]
+ #[should_panic]
+ fn test_padded_output_len_overflow() {
+ let _ = AesCbcPkcs7PaddedStub::padded_output_len(usize::MAX);
+ }
+
+ struct AesCbcPkcs7PaddedStub;
+
+ impl AesCbcPkcs7Padded for AesCbcPkcs7PaddedStub {
+ #[cfg(feature = "alloc")]
+ fn encrypt(_key: &Aes256Key, _iv: &AesCbcIv, _message: &[u8]) -> Vec<u8> {
+ unimplemented!()
+ }
+
+ fn encrypt_in_place(
+ _key: &Aes256Key,
+ _iv: &AesCbcIv,
+ _message: &mut SliceVec<u8>,
+ ) -> Result<(), EncryptionError> {
+ unimplemented!()
+ }
+
+ #[cfg(feature = "alloc")]
+ fn decrypt(
+ _key: &Aes256Key,
+ _iv: &AesCbcIv,
+ _ciphertext: &[u8],
+ ) -> Result<Vec<u8>, DecryptionError> {
+ unimplemented!()
+ }
+
+ fn decrypt_in_place(
+ _key: &Aes256Key,
+ _iv: &AesCbcIv,
+ _ciphertext: &mut SliceVec<u8>,
+ ) -> Result<(), DecryptionError> {
+ unimplemented!()
+ }
+ }
+}
diff --git a/nearby/crypto/crypto_provider/src/aes/ctr.rs b/nearby/crypto/crypto_provider/src/aes/ctr.rs
index 73d4f55..6779fe8 100644
--- a/nearby/crypto/crypto_provider/src/aes/ctr.rs
+++ b/nearby/crypto/crypto_provider/src/aes/ctr.rs
@@ -36,10 +36,8 @@ pub trait AesCtr {
/// Build a `Self` from key material.
fn new(key: &Self::Key, nonce_and_counter: NonceAndCounter) -> Self;
- /// Encrypt the data in place, advancing the counter state appropriately.
- fn encrypt(&mut self, data: &mut [u8]);
- /// Decrypt the data in place, advancing the counter state appropriately.
- fn decrypt(&mut self, data: &mut [u8]);
+ /// Applies the key stream to the data in place, advancing the counter state appropriately.
+ fn apply_keystream(&mut self, data: &mut [u8]);
}
/// The combined nonce and counter that CTR increments and encrypts to form the keystream.
diff --git a/nearby/crypto/crypto_provider/src/aes/mod.rs b/nearby/crypto/crypto_provider/src/aes/mod.rs
index 83e48de..e359bad 100644
--- a/nearby/crypto/crypto_provider/src/aes/mod.rs
+++ b/nearby/crypto/crypto_provider/src/aes/mod.rs
@@ -20,7 +20,6 @@ use core::{array, fmt};
pub mod ctr;
-#[cfg(feature = "alloc")]
pub mod cbc;
/// Block size in bytes for AES (and XTS-AES)
diff --git a/nearby/crypto/crypto_provider/src/ed25519.rs b/nearby/crypto/crypto_provider/src/ed25519.rs
index 48c2c50..b435ca2 100644
--- a/nearby/crypto/crypto_provider/src/ed25519.rs
+++ b/nearby/crypto/crypto_provider/src/ed25519.rs
@@ -43,6 +43,59 @@ pub type RawPublicKey = [u8; PUBLIC_KEY_LENGTH];
/// A byte buffer the size of a ed25519 `PrivateKey`.
pub type RawPrivateKey = [u8; PRIVATE_KEY_LENGTH];
+/// A permission token which may be supplied to methods which allow
+/// converting private keys to/from raw bytes.
+///
+/// In general, operations of this kind should only be done in
+/// development-tools, tests, or in credential storage layers
+/// to prevent accidental exposure of the private key.
+pub struct RawPrivateKeyPermit {
+ _marker: (),
+}
+
+impl RawPrivateKeyPermit {
+ pub(crate) fn new() -> Self {
+ Self { _marker: () }
+ }
+}
+
+#[cfg(feature = "raw_private_key_permit")]
+impl core::default::Default for RawPrivateKeyPermit {
+ fn default() -> Self {
+ Self::new()
+ }
+}
+
+/// A crypto-provider-independent representation of the private
+/// key of an ed25519 key-pair, kept in such a way that
+/// it does not permit de-structuring it into raw bytes,
+/// nor constructing one from raw bytes.
+///
+/// Useful for when you want a data-structure to be
+/// crypto-provider independent and contain a private key.
+#[derive(Clone)]
+pub struct PrivateKey {
+ wrapped: RawPrivateKey,
+}
+
+impl PrivateKey {
+ /// Derives the public key corresponding to this private key.
+ pub fn derive_public_key<E: Ed25519Provider>(&self) -> E::PublicKey {
+ let key_pair = E::KeyPair::from_private_key(self);
+ key_pair.public()
+ }
+ /// Returns the raw bytes of this private key.
+ /// This operation is only possible while holding a [`RawPrivateKeyPermit`].
+ pub fn raw_private_key(&self, _permit: &RawPrivateKeyPermit) -> RawPrivateKey {
+ self.wrapped
+ }
+ /// Constructs a private key from the raw bytes of the key.
+ /// This operation is only possible while holding a [`RawPrivateKeyPermit`].
+ pub fn from_raw_private_key(wrapped: RawPrivateKey, _permit: &RawPrivateKeyPermit) -> Self {
+ Self { wrapped }
+ }
+}
+
/// The keypair which includes both public and secret halves of an asymmetric key.
pub trait KeyPair: Sized {
/// The ed25519 public key, used when verifying a message
@@ -52,15 +105,37 @@ pub trait KeyPair: Sized {
type Signature: Signature;
/// Returns the private key bytes of the `KeyPair`.
- /// This method should only ever be called by code which securely stores private credentials.
- fn private_key(&self) -> RawPrivateKey;
+ /// This operation is only possible while holding a [`RawPrivateKeyPermit`].
+ fn raw_private_key(&self, _permit: &RawPrivateKeyPermit) -> RawPrivateKey;
/// Builds a key-pair from a `RawPrivateKey` array of bytes.
- /// This should only ever be called by code which securely stores private credentials.
- fn from_private_key(bytes: &RawPrivateKey) -> Self
+ /// This operation is only possible while holding a [`RawPrivateKeyPermit`].
+ fn from_raw_private_key(bytes: &RawPrivateKey, _permit: &RawPrivateKeyPermit) -> Self
where
Self: Sized;
+ /// Returns the private key of the `KeyPair` in an opaque form.
+ fn private_key(&self) -> PrivateKey {
+ // We're okay to reach in and grab the bytes of the private key,
+ // since the way that we're exposing it would require a valid
+ // [`RawPrivateKeyPermit`] to extract them again.
+ let wrapped = self.raw_private_key(&RawPrivateKeyPermit::new());
+ PrivateKey { wrapped }
+ }
+
+ /// Builds a key-pair from a [`PrivateKey`], given in an opaque form.
+ fn from_private_key(private_key: &PrivateKey) -> Self
+ where
+ Self: Sized,
+ {
+ // We're okay to reach in and construct an instance from
+ // the bytes of the private key, since the way that they
+ // were originally expressed would still require a valid
+ // [`RawPrivateKeyPermit`] to access them.
+ let raw_private_key = &private_key.wrapped;
+ Self::from_raw_private_key(raw_private_key, &RawPrivateKeyPermit::new())
+ }
+
/// Sign the given message and return a digital signature
fn sign(&self, msg: &[u8]) -> Self::Signature;
diff --git a/nearby/crypto/crypto_provider/src/elliptic_curve.rs b/nearby/crypto/crypto_provider/src/elliptic_curve.rs
index d06a21d..d176769 100644
--- a/nearby/crypto/crypto_provider/src/elliptic_curve.rs
+++ b/nearby/crypto/crypto_provider/src/elliptic_curve.rs
@@ -12,12 +12,8 @@
// See the License for the specific language governing permissions and
// limitations under the License.
-extern crate alloc;
-
use core::fmt::Debug;
-use alloc::vec::Vec;
-
/// Marker trait for an elliptic curve used for diffie-hellman.
pub trait Curve {}
@@ -42,12 +38,16 @@ pub trait EphemeralSecret<C: Curve>: Send {
/// The random number generator to be used for generating a secret
type Rng: crate::CryptoRng;
+ /// The for encoded public key bytes, for example a `[u8; N]` array if the size is fixed, or
+ /// `ArrayVec<[u8; N]>` if the size is bounded but not fixed.
+ type EncodedPublicKey: AsRef<[u8]> + Debug;
+
/// Generates a new random ephemeral secret.
fn generate_random(rng: &mut Self::Rng) -> Self;
/// Returns the bytes of the public key for this ephemeral secret that is suitable for sending
/// over the wire for key exchange.
- fn public_key_bytes(&self) -> Vec<u8>;
+ fn public_key_bytes(&self) -> Self::EncodedPublicKey;
/// Performs diffie-hellman key exchange using this ephemeral secret with the given public key
/// `other_pub`.
@@ -59,6 +59,8 @@ pub trait EphemeralSecret<C: Curve>: Send {
/// Trait for a public key used for elliptic curve diffie hellman.
pub trait PublicKey<E: Curve>: Sized + PartialEq + Debug {
+ /// The type for an encoded public key.
+ type EncodedPublicKey: AsRef<[u8]> + Debug;
/// The error type associated with Public Key.
type Error: Debug;
@@ -71,5 +73,5 @@ pub trait PublicKey<E: Curve>: Sized + PartialEq + Debug {
/// the sec1 encoding, may return equivalent but different byte-representations due to point
/// compression, so it is not necessarily true that `from_bytes(bytes)?.to_bytes() == bytes`
/// (but it is always true that `from_bytes(key.to_bytes())? == key`).
- fn to_bytes(&self) -> Vec<u8>;
+ fn to_bytes(&self) -> Self::EncodedPublicKey;
}
diff --git a/nearby/crypto/crypto_provider/src/lib.rs b/nearby/crypto/crypto_provider/src/lib.rs
index 624072a..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};
@@ -48,6 +47,8 @@ pub mod aead;
/// mod containing traits for ed25519 key generation, signing, and verification
pub mod ed25519;
+pub use tinyvec;
+
/// Uber crypto trait which defines the traits for all crypto primitives as associated types
pub trait CryptoProvider: Clone + Debug + PartialEq + Eq + Send {
/// The Hkdf type which implements the hkdf trait
@@ -80,10 +81,13 @@ pub trait CryptoProvider: Clone + Debug + PartialEq + Eq + Send {
/// using SHA-512 (SHA-2) and Curve25519
type Ed25519: ed25519::Ed25519Provider;
/// The trait defining AES-128-GCM-SIV, a nonce-misuse resistant AEAD with a key size of 16 bytes.
- type Aes128GcmSiv: aead::aes_gcm_siv::AesGcmSiv<Key = Aes128Key>;
+ type Aes128GcmSiv: aead::AesGcmSiv + aead::AeadInit<Aes128Key>;
/// The trait defining AES-256-GCM-SIV, a nonce-misuse resistant AEAD with a key size of 32 bytes.
- type Aes256GcmSiv: aead::aes_gcm_siv::AesGcmSiv<Key = Aes256Key>;
-
+ type Aes256GcmSiv: aead::AesGcmSiv + aead::AeadInit<Aes256Key>;
+ /// The trait defining AES-128-GCM, an AEAD with a key size of 16 bytes.
+ type Aes128Gcm: aead::AesGcm + aead::AeadInit<Aes128Key>;
+ /// The trait defining AES-256-GCM, an AEAD with a key size of 32 bytes.
+ type Aes256Gcm: aead::AesGcm + aead::AeadInit<Aes256Key>;
/// The cryptographically secure random number generator
type CryptoRng: CryptoRng;
diff --git a/nearby/crypto/crypto_provider/src/p256.rs b/nearby/crypto/crypto_provider/src/p256.rs
index 90d3542..fd7b531 100644
--- a/nearby/crypto/crypto_provider/src/p256.rs
+++ b/nearby/crypto/crypto_provider/src/p256.rs
@@ -12,10 +12,9 @@
// See the License for the specific language governing permissions and
// limitations under the License.
-extern crate alloc;
+use tinyvec::ArrayVec;
use crate::elliptic_curve::{Curve, EcdhProvider, PublicKey};
-use alloc::vec::Vec;
use core::fmt::Debug;
/// Marker type for P256 implementation. This is used by EcdhProvider as its type parameter.
@@ -23,6 +22,24 @@ use core::fmt::Debug;
pub enum P256 {}
impl Curve for P256 {}
+/// Longest length for a sec-1 encoded P256 public key, which is the uncompressed format
+/// `04 || X || Y` as defined in section 2.3.3 of the SECG SEC 1 ("Elliptic Curve Cryptography")
+/// standard.
+const P256_PUBLIC_KEY_MAX_LENGTH: usize = 65;
+
+/// Whether an elliptic curve point should be compressed or not.
+#[derive(Debug, Clone, Copy, PartialEq, Eq)]
+pub enum PointCompression {
+ /// The elliptic curve point should be compressed (`02 || X` or `03 || X`),
+ /// as defined in section 2.3.3 of the SECG SEC 1 ("Elliptic Curve
+ /// Cryptography").
+ Compressed,
+ /// The elliptic curve point should be uncompressed (`04 || X || Y`), as
+ /// defined in section 2.3.3 of the SECG SEC 1 ("Elliptic Curve
+ /// Cryptography").
+ Uncompressed,
+}
+
/// Trait for a NIST-P256 public key.
pub trait P256PublicKey: Sized + PartialEq + Debug {
/// The error type associated with this implementation.
@@ -36,7 +53,10 @@ pub trait P256PublicKey: Sized + PartialEq + Debug {
/// ("Elliptic Curve Cryptography") standard. Note that it is not necessarily true that
/// `from_sec1_bytes(bytes)?.to_sec1_bytes() == bytes` because of point compression. (But it is
/// always true that `from_sec1_bytes(key.to_sec1_bytes())? == key`).
- fn to_sec1_bytes(&self) -> Vec<u8>;
+ fn to_sec1_bytes(
+ &self,
+ point_compression: PointCompression,
+ ) -> ArrayVec<[u8; P256_PUBLIC_KEY_MAX_LENGTH]>;
/// Converts this public key's x and y coordinates on the elliptic curve to big endian octet
/// strings.
@@ -48,13 +68,14 @@ pub trait P256PublicKey: Sized + PartialEq + Debug {
impl<P: P256PublicKey> PublicKey<P256> for P {
type Error = <Self as P256PublicKey>::Error;
+ type EncodedPublicKey = ArrayVec<[u8; P256_PUBLIC_KEY_MAX_LENGTH]>;
fn from_bytes(bytes: &[u8]) -> Result<Self, Self::Error> {
Self::from_sec1_bytes(bytes)
}
- fn to_bytes(&self) -> Vec<u8> {
- Self::to_sec1_bytes(self)
+ fn to_bytes(&self) -> Self::EncodedPublicKey {
+ Self::to_sec1_bytes(self, PointCompression::Uncompressed)
}
}
diff --git a/nearby/crypto/crypto_provider_boringssl/.cargo/config.toml b/nearby/crypto/crypto_provider_boringssl/.cargo/config.toml
deleted file mode 100644
index f5ab7fa..0000000
--- a/nearby/crypto/crypto_provider_boringssl/.cargo/config.toml
+++ /dev/null
@@ -1,3 +0,0 @@
-paths = [
- "../../../boringssl-build/boringssl/rust/bssl-crypto",
-] \ No newline at end of file
diff --git a/nearby/crypto/crypto_provider_boringssl/Cargo.lock b/nearby/crypto/crypto_provider_boringssl/Cargo.lock
index 14402c1..11ec2e3 100644
--- a/nearby/crypto/crypto_provider_boringssl/Cargo.lock
+++ b/nearby/crypto/crypto_provider_boringssl/Cargo.lock
@@ -3,14 +3,30 @@
version = 3
[[package]]
+name = "aho-corasick"
+version = "1.1.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "b2969dcb958b36655471fc61f7e416fa76033bdd4bfed0678d8fee1e2d07a1f0"
+dependencies = [
+ "memchr",
+]
+
+[[package]]
name = "base64"
-version = "0.13.1"
+version = "0.21.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "9e1b586273c5702936fe7b7d6896644d8be71e6314cfe09d3167c95f712589e8"
+checksum = "9ba43ea6f343b788c8764558649e08df62f86c6ef251fdaeb1ffd010a9ae50a2"
[[package]]
name = "bssl-crypto"
version = "0.1.0"
+dependencies = [
+ "bssl-sys",
+]
+
+[[package]]
+name = "bssl-sys"
+version = "0.1.0"
[[package]]
name = "cfg-if"
@@ -21,6 +37,9 @@ checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd"
[[package]]
name = "crypto_provider"
version = "0.1.0"
+dependencies = [
+ "tinyvec",
+]
[[package]]
name = "crypto_provider_boringssl"
@@ -28,18 +47,10 @@ version = "0.1.0"
dependencies = [
"bssl-crypto",
"crypto_provider",
- "crypto_provider_stubs",
"crypto_provider_test",
]
[[package]]
-name = "crypto_provider_stubs"
-version = "0.1.0"
-dependencies = [
- "crypto_provider",
-]
-
-[[package]]
name = "crypto_provider_test"
version = "0.1.0"
dependencies = [
@@ -56,9 +67,9 @@ dependencies = [
[[package]]
name = "getrandom"
-version = "0.2.9"
+version = "0.2.10"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "c85e1d9ab2eadba7e5040d4e09cbd6d072b76a557ad64e797c2cb9d4da21d7e4"
+checksum = "be4136b2a15dd319360be1c07d9933517ccf0be8f16bf62a3bee4f0d618df427"
dependencies = [
"cfg-if",
"libc",
@@ -66,6 +77,12 @@ dependencies = [
]
[[package]]
+name = "glob"
+version = "0.3.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d2fabcfbdc87f4758337ca535fb41a6d701b65693ce38287d856d1674551ec9b"
+
+[[package]]
name = "hex"
version = "0.4.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -79,24 +96,27 @@ checksum = "6fe2267d4ed49bc07b63801559be28c718ea06c4738b7a03c94df7386d2cde46"
[[package]]
name = "itoa"
-version = "1.0.6"
+version = "1.0.9"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "453ad9f582a441959e5f0d088b02ce04cfe8d51a8eaf077f12ac6d3e94164ca6"
+checksum = "af150ab688ff2122fcef229be89cb50dd66af9e01a4ff320cc137eecc9bacc38"
[[package]]
name = "libc"
-version = "0.2.144"
+version = "0.2.147"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "2b00cc1c228a6782d0f076e7b232802e0c5689d41bb5df366f2a6b6621cfdfe1"
+checksum = "b4668fb0ea861c1df094127ac5f1da3409a82116a4ba74fca2e58ef927159bb3"
[[package]]
name = "log"
-version = "0.4.17"
+version = "0.4.20"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "abb12e687cfb44aa40f41fc3978ef76448f9b6038cad6aef4259d3c095a2382e"
-dependencies = [
- "cfg-if",
-]
+checksum = "b5e6163cb8c49088c2c36f57875e58ccd8c87c7427f7fbd50ea6710b2f3f2e8f"
+
+[[package]]
+name = "memchr"
+version = "2.7.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "523dc4f511e55ab87b694dc30d0f820d60906ef06413f93d4d7a1385599cc149"
[[package]]
name = "ppv-lite86"
@@ -106,18 +126,18 @@ checksum = "5b40af805b3121feab8a3c29f04d8ad262fa8e0561883e7653e024ae4479e6de"
[[package]]
name = "proc-macro2"
-version = "1.0.56"
+version = "1.0.74"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "2b63bdb0cd06f1f4dedf69b254734f9b45af66e4a031e42a7480257d9898b435"
+checksum = "2de98502f212cfcea8d0bb305bd0f49d7ebdd75b64ba0a68f937d888f4e0d6db"
dependencies = [
"unicode-ident",
]
[[package]]
name = "quote"
-version = "1.0.27"
+version = "1.0.35"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "8f4f29d145265ec1c483c7c654450edde0bfe043d3938d6972630663356d9500"
+checksum = "291ec9ab5efd934aaf503a6466c5d5251535d108ee747472c3977cc5acc868ef"
dependencies = [
"proc-macro2",
]
@@ -172,10 +192,45 @@ dependencies = [
]
[[package]]
+name = "regex"
+version = "1.10.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "380b951a9c5e80ddfd6136919eef32310721aa4aacd4889a8d39124b026ab343"
+dependencies = [
+ "aho-corasick",
+ "memchr",
+ "regex-automata",
+ "regex-syntax",
+]
+
+[[package]]
+name = "regex-automata"
+version = "0.4.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "5f804c7828047e88b2d32e2d7fe5a105da8ee3264f01902f796c8e067dc2483f"
+dependencies = [
+ "aho-corasick",
+ "memchr",
+ "regex-syntax",
+]
+
+[[package]]
+name = "regex-syntax"
+version = "0.8.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "c08c74e62047bb2de4ff487b251e4a92e24f48745648451635cec7d591162d9f"
+
+[[package]]
+name = "relative-path"
+version = "1.9.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "e898588f33fdd5b9420719948f9f2a32c922a246964576f71ba7f24f80610fbc"
+
+[[package]]
name = "rstest"
-version = "0.17.0"
+version = "0.18.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "de1bb486a691878cd320c2f0d319ba91eeaa2e894066d8b5f8f117c000e9d962"
+checksum = "97eeab2f3c0a199bc4be135c36c924b6590b88c377d416494288c14f2db30199"
dependencies = [
"rstest_macros",
"rustc_version",
@@ -183,28 +238,31 @@ dependencies = [
[[package]]
name = "rstest_macros"
-version = "0.17.0"
+version = "0.18.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "290ca1a1c8ca7edb7c3283bd44dc35dd54fdec6253a3912e201ba1072018fca8"
+checksum = "d428f8247852f894ee1be110b375111b586d4fa431f6c46e64ba5a0dcccbe605"
dependencies = [
"cfg-if",
+ "glob",
"proc-macro2",
"quote",
+ "regex",
+ "relative-path",
"rustc_version",
- "syn 1.0.109",
+ "syn",
"unicode-ident",
]
[[package]]
name = "rstest_reuse"
-version = "0.5.0"
+version = "0.6.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "45f80dcc84beab3a327bbe161f77db25f336a1452428176787c8c79ac79d7073"
+checksum = "88530b681abe67924d42cca181d070e3ac20e0740569441a9e35a7cedd2b34a4"
dependencies = [
"quote",
"rand",
"rustc_version",
- "syn 1.0.109",
+ "syn",
]
[[package]]
@@ -218,41 +276,41 @@ dependencies = [
[[package]]
name = "ryu"
-version = "1.0.13"
+version = "1.0.15"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "f91339c0467de62360649f8d3e185ca8de4224ff281f66000de5eb2a77a79041"
+checksum = "1ad4cc8da4ef723ed60bced201181d83791ad433213d8c24efffda1eec85d741"
[[package]]
name = "semver"
-version = "1.0.17"
+version = "1.0.18"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "bebd363326d05ec3e2f532ab7660680f3b02130d780c299bca73469d521bc0ed"
+checksum = "b0293b4b29daaf487284529cc2f5675b8e57c61f70167ba415a463651fd6a918"
[[package]]
name = "serde"
-version = "1.0.162"
+version = "1.0.194"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "71b2f6e1ab5c2b98c05f0f35b236b22e8df7ead6ffbf51d7808da7f8817e7ab6"
+checksum = "0b114498256798c94a0689e1a15fec6005dee8ac1f41de56404b67afc2a4b773"
dependencies = [
"serde_derive",
]
[[package]]
name = "serde_derive"
-version = "1.0.162"
+version = "1.0.194"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "a2a0814352fd64b58489904a44ea8d90cb1a91dcb6b4f5ebabc32c8318e93cb6"
+checksum = "a3385e45322e8f9931410f01b3031ec534c3947d0e94c18049af4d9f9907d4e0"
dependencies = [
"proc-macro2",
"quote",
- "syn 2.0.15",
+ "syn",
]
[[package]]
name = "serde_json"
-version = "1.0.96"
+version = "1.0.110"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "057d394a50403bcac12672b2b18fb387ab6d289d957dab67dd201875391e52f1"
+checksum = "6fbd975230bada99c8bb618e0c365c2eefa219158d5c6c29610fd09ff1833257"
dependencies = [
"itoa",
"ryu",
@@ -261,20 +319,9 @@ dependencies = [
[[package]]
name = "syn"
-version = "1.0.109"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "72b64191b275b66ffe2469e8af2c1cfe3bafa67b529ead792a6d0160888b4237"
-dependencies = [
- "proc-macro2",
- "quote",
- "unicode-ident",
-]
-
-[[package]]
-name = "syn"
-version = "2.0.15"
+version = "2.0.46"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "a34fcf3e8b60f57e6a14301a2e916d323af98b0ea63c599441eec8558660c822"
+checksum = "89456b690ff72fddcecf231caedbe615c59480c93358a93dfae7fc29e3ebbf0e"
dependencies = [
"proc-macro2",
"quote",
@@ -290,10 +337,16 @@ dependencies = [
]
[[package]]
+name = "tinyvec"
+version = "1.6.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "87cc5ceb3875bb20c2890005a4e226a4651264a5c75edb2421b52861a0a0cb50"
+
+[[package]]
name = "unicode-ident"
-version = "1.0.8"
+version = "1.0.11"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "e5464a87b239f13a63a501f2701565754bae92d243d4bb7eb12f6d57d2269bf4"
+checksum = "301abaae475aa91687eb82514b328ab47a211a533026cb25fc3e519b86adfc3c"
[[package]]
name = "wasi"
@@ -303,9 +356,9 @@ checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423"
[[package]]
name = "wycheproof"
-version = "0.4.0"
+version = "0.5.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "183c789620c674b79dac33cd3aadb6c8006b66cba6a680402235aaebc743e3df"
+checksum = "e639f57253b80c6584b378011aec0fed61c4c21d7a4b97c4d9d7eaf35ca77d12"
dependencies = [
"base64",
"hex",
diff --git a/nearby/crypto/crypto_provider_boringssl/Cargo.toml b/nearby/crypto/crypto_provider_boringssl/Cargo.toml
index 24ce66c..fb3bfdf 100644
--- a/nearby/crypto/crypto_provider_boringssl/Cargo.toml
+++ b/nearby/crypto/crypto_provider_boringssl/Cargo.toml
@@ -6,10 +6,9 @@ publish = false
[dependencies]
crypto_provider = { path = "../crypto_provider", features = ["alloc", "std"] }
-crypto_provider_stubs = { path = "../crypto_provider_stubs" }
-# Note: before this crate will work you need to run `scripts/prepare-boringssl.sh`
-bssl-crypto = {path = "../bssl-crypto"}
+# Note: before this crate will work you need to run `cargo run -p build_scripts -- build-boringssl`
+bssl-crypto = {path = "../../../third_party/boringssl/rust/bssl-crypto"}
[dev-dependencies]
crypto_provider_test = {path = "../crypto_provider_test"}
diff --git a/nearby/crypto/crypto_provider_boringssl/src/aead/aes_gcm.rs b/nearby/crypto/crypto_provider_boringssl/src/aead/aes_gcm.rs
new file mode 100644
index 0000000..03bb087
--- /dev/null
+++ b/nearby/crypto/crypto_provider_boringssl/src/aead/aes_gcm.rs
@@ -0,0 +1,89 @@
+// 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.
+
+extern crate alloc;
+
+use alloc::vec::Vec;
+use bssl_crypto::aead::Aead as _;
+use crypto_provider::aead::{Aead, AeadError, AeadInit};
+use crypto_provider::aes::{Aes128Key, Aes256Key, AesKey};
+
+pub struct AesGcm(bssl_crypto::aead::AesGcm);
+
+impl AeadInit<Aes128Key> for AesGcm {
+ fn new(key: &Aes128Key) -> Self {
+ Self(bssl_crypto::aead::new_aes_128_gcm(key.as_array()))
+ }
+}
+
+impl AeadInit<Aes256Key> for AesGcm {
+ fn new(key: &Aes256Key) -> Self {
+ Self(bssl_crypto::aead::new_aes_256_gcm(key.as_array()))
+ }
+}
+
+impl crypto_provider::aead::AesGcm for AesGcm {}
+
+impl Aead for AesGcm {
+ const TAG_SIZE: usize = 16;
+ type Nonce = [u8; 12];
+ type Tag = [u8; 16];
+
+ fn encrypt(&self, msg: &[u8], aad: &[u8], nonce: &Self::Nonce) -> Result<Vec<u8>, AeadError> {
+ self.0.encrypt(msg, aad, nonce).map_err(|_| AeadError)
+ }
+
+ fn encrypt_detached(
+ &self,
+ _msg: &mut [u8],
+ _aad: &[u8],
+ _nonce: &Self::Nonce,
+ ) -> Result<Self::Tag, AeadError> {
+ unimplemented!("Not yet supported by boringssl")
+ }
+
+ fn decrypt(&self, msg: &[u8], aad: &[u8], nonce: &Self::Nonce) -> Result<Vec<u8>, AeadError> {
+ self.0.decrypt(msg, aad, nonce).map_err(|_| AeadError)
+ }
+
+ fn decrypt_detached(
+ &self,
+ _msg: &mut [u8],
+ _aad: &[u8],
+ _nonce: &Self::Nonce,
+ _tag: &Self::Tag,
+ ) -> Result<(), AeadError> {
+ unimplemented!("Not yet supported by boringssl")
+ }
+}
+
+#[cfg(test)]
+mod tests {
+ use core::marker::PhantomData;
+
+ use crypto_provider_test::aead::aes_gcm::*;
+ use crypto_provider_test::aes::*;
+
+ use super::*;
+
+ #[apply(aes_128_gcm_test_cases)]
+ fn aes_gcm_128_test(testcase: CryptoProviderTestCase<AesGcm>) {
+ testcase(PhantomData);
+ }
+
+ #[apply(aes_256_gcm_test_cases)]
+ fn aes_gcm_256_test(testcase: CryptoProviderTestCase<AesGcm>) {
+ testcase(PhantomData);
+ }
+}
diff --git a/nearby/crypto/crypto_provider_boringssl/src/aead/aes_gcm_siv.rs b/nearby/crypto/crypto_provider_boringssl/src/aead/aes_gcm_siv.rs
new file mode 100644
index 0000000..d1fb0e7
--- /dev/null
+++ b/nearby/crypto/crypto_provider_boringssl/src/aead/aes_gcm_siv.rs
@@ -0,0 +1,88 @@
+// 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.
+extern crate alloc;
+
+use alloc::vec::Vec;
+use bssl_crypto::aead::Aead as _;
+use crypto_provider::aead::{Aead, AeadError, AeadInit};
+use crypto_provider::aes::{Aes128Key, Aes256Key, AesKey};
+
+pub struct AesGcmSiv(bssl_crypto::aead::AesGcmSiv);
+
+impl AeadInit<Aes128Key> for AesGcmSiv {
+ fn new(key: &Aes128Key) -> Self {
+ Self(bssl_crypto::aead::new_aes_128_gcm_siv(key.as_array()))
+ }
+}
+
+impl AeadInit<Aes256Key> for AesGcmSiv {
+ fn new(key: &Aes256Key) -> Self {
+ Self(bssl_crypto::aead::new_aes_256_gcm_siv(key.as_array()))
+ }
+}
+
+impl crypto_provider::aead::AesGcmSiv for AesGcmSiv {}
+
+impl Aead for AesGcmSiv {
+ const TAG_SIZE: usize = 16;
+ type Nonce = [u8; 12];
+ type Tag = [u8; 16];
+
+ fn encrypt(&self, msg: &[u8], aad: &[u8], nonce: &Self::Nonce) -> Result<Vec<u8>, AeadError> {
+ self.0.encrypt(msg, aad, nonce).map_err(|_| AeadError)
+ }
+
+ fn encrypt_detached(
+ &self,
+ _msg: &mut [u8],
+ _aad: &[u8],
+ _nonce: &Self::Nonce,
+ ) -> Result<Self::Tag, AeadError> {
+ unimplemented!("Not yet supported by boringssl")
+ }
+
+ fn decrypt(&self, msg: &[u8], aad: &[u8], nonce: &Self::Nonce) -> Result<Vec<u8>, AeadError> {
+ self.0.decrypt(msg, aad, nonce).map_err(|_| AeadError)
+ }
+
+ fn decrypt_detached(
+ &self,
+ _msg: &mut [u8],
+ _aad: &[u8],
+ _nonce: &Self::Nonce,
+ _tag: &Self::Tag,
+ ) -> Result<(), AeadError> {
+ unimplemented!("Not yet supported by boringssl")
+ }
+}
+
+#[cfg(test)]
+mod tests {
+ use core::marker::PhantomData;
+
+ use crypto_provider_test::aead::aes_gcm_siv::*;
+ use crypto_provider_test::aes::*;
+
+ use super::*;
+
+ #[apply(aes_128_gcm_siv_test_cases)]
+ fn aes_gcm_siv_128_test(testcase: CryptoProviderTestCase<AesGcmSiv>) {
+ testcase(PhantomData);
+ }
+
+ #[apply(aes_256_gcm_siv_test_cases)]
+ fn aes_gcm_siv_256_test(testcase: CryptoProviderTestCase<AesGcmSiv>) {
+ testcase(PhantomData);
+ }
+}
diff --git a/nearby/crypto/bssl-crypto/src/lib.rs b/nearby/crypto/crypto_provider_boringssl/src/aead/mod.rs
index 89e6968..424f16e 100644
--- a/nearby/crypto/bssl-crypto/src/lib.rs
+++ b/nearby/crypto/crypto_provider_boringssl/src/aead/mod.rs
@@ -4,7 +4,7 @@
// 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
+// 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,
@@ -12,5 +12,5 @@
// See the License for the specific language governing permissions and
// limitations under the License.
-//! Placeholder crate to satisfy cargo. If actually using boring ssl, please run the
-//! `build-boringssl` subcommand of the top level crate.
+pub(crate) mod aes_gcm;
+pub(crate) mod aes_gcm_siv;
diff --git a/nearby/crypto/crypto_provider_boringssl/src/aes/cbc.rs b/nearby/crypto/crypto_provider_boringssl/src/aes/cbc.rs
new file mode 100644
index 0000000..4d3fd3f
--- /dev/null
+++ b/nearby/crypto/crypto_provider_boringssl/src/aes/cbc.rs
@@ -0,0 +1,81 @@
+// 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.
+
+extern crate alloc;
+
+use alloc::vec::Vec;
+use bssl_crypto::cipher::BlockCipher;
+use crypto_provider::{
+ aes::{
+ cbc::{AesCbcIv, DecryptionError, EncryptionError},
+ Aes256Key, AesKey,
+ },
+ tinyvec::SliceVec,
+};
+
+/// BoringSSL implementation of AES-CBC with PKCS7 padding
+pub enum AesCbcPkcs7Padded {}
+impl crypto_provider::aes::cbc::AesCbcPkcs7Padded for AesCbcPkcs7Padded {
+ #[allow(clippy::expect_used)]
+ fn encrypt(key: &Aes256Key, iv: &AesCbcIv, message: &[u8]) -> Vec<u8> {
+ let encryptor = bssl_crypto::cipher::aes_cbc::Aes256Cbc::new_encrypt(key.as_array(), iv);
+ encryptor.encrypt_padded(message).expect("Encrypting AES-CBC should be infallible")
+ }
+
+ fn encrypt_in_place(
+ key: &Aes256Key,
+ iv: &AesCbcIv,
+ message: &mut SliceVec<u8>,
+ ) -> Result<(), EncryptionError> {
+ let result = Self::encrypt(key, iv, message);
+ if message.capacity() < result.len() {
+ return Err(EncryptionError::PaddingFailed);
+ }
+ message.clear();
+ message.extend_from_slice(&result);
+ Ok(())
+ }
+
+ fn decrypt(
+ key: &Aes256Key,
+ iv: &AesCbcIv,
+ ciphertext: &[u8],
+ ) -> Result<Vec<u8>, DecryptionError> {
+ let decryptor = bssl_crypto::cipher::aes_cbc::Aes256Cbc::new_decrypt(key.as_array(), iv);
+ decryptor.decrypt_padded(ciphertext).map_err(|_| DecryptionError::BadPadding)
+ }
+
+ fn decrypt_in_place(
+ key: &Aes256Key,
+ iv: &AesCbcIv,
+ ciphertext: &mut SliceVec<u8>,
+ ) -> Result<(), DecryptionError> {
+ Self::decrypt(key, iv, ciphertext).map(|result| {
+ ciphertext.clear();
+ ciphertext.extend_from_slice(&result);
+ })
+ }
+}
+
+#[cfg(test)]
+mod tests {
+ use super::AesCbcPkcs7Padded;
+ use core::marker::PhantomData;
+ use crypto_provider_test::aes::cbc::*;
+
+ #[apply(aes_256_cbc_test_cases)]
+ fn aes_256_cbc_test(testcase: CryptoProviderTestCase<AesCbcPkcs7Padded>) {
+ testcase(PhantomData);
+ }
+}
diff --git a/nearby/crypto/crypto_provider_boringssl/src/aes/ctr.rs b/nearby/crypto/crypto_provider_boringssl/src/aes/ctr.rs
new file mode 100644
index 0000000..c13ab31
--- /dev/null
+++ b/nearby/crypto/crypto_provider_boringssl/src/aes/ctr.rs
@@ -0,0 +1,74 @@
+// 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 bssl_crypto::cipher::StreamCipher;
+use crypto_provider::aes::{ctr::NonceAndCounter, Aes128Key, Aes256Key, AesKey};
+
+/// BoringSSL implementation of AES-CTR 128.
+pub struct AesCtr128(bssl_crypto::cipher::aes_ctr::Aes128Ctr);
+
+impl crypto_provider::aes::ctr::AesCtr for AesCtr128 {
+ type Key = Aes128Key;
+
+ fn new(key: &Self::Key, nonce_and_counter: NonceAndCounter) -> Self {
+ Self(bssl_crypto::cipher::aes_ctr::Aes128Ctr::new(
+ key.as_array(),
+ &nonce_and_counter.as_block_array(),
+ ))
+ }
+
+ #[allow(clippy::expect_used)]
+ fn apply_keystream(&mut self, data: &mut [u8]) {
+ assert!(data.len() < i32::MAX as usize);
+ self.0.apply_keystream(data).expect("Data length should fit inside of a i32")
+ }
+}
+
+/// BoringSSL implementation of AES-CTR 256.
+pub struct AesCtr256(bssl_crypto::cipher::aes_ctr::Aes256Ctr);
+
+impl crypto_provider::aes::ctr::AesCtr for AesCtr256 {
+ type Key = Aes256Key;
+
+ fn new(key: &Self::Key, nonce_and_counter: NonceAndCounter) -> Self {
+ Self(bssl_crypto::cipher::aes_ctr::Aes256Ctr::new(
+ key.as_array(),
+ &nonce_and_counter.as_block_array(),
+ ))
+ }
+
+ #[allow(clippy::expect_used)]
+ fn apply_keystream(&mut self, data: &mut [u8]) {
+ assert!(data.len() < i32::MAX as usize);
+ self.0.apply_keystream(data).expect("Data length should fit inside of a i32")
+ }
+}
+
+#[cfg(test)]
+mod tests {
+ use super::*;
+ use core::marker::PhantomData;
+ use crypto_provider_test::aes::ctr::*;
+ use crypto_provider_test::aes::*;
+
+ #[apply(aes_128_ctr_test_cases)]
+ fn aes_128_ctr_test(testcase: CryptoProviderTestCase<AesCtr128>) {
+ testcase(PhantomData);
+ }
+
+ #[apply(aes_256_ctr_test_cases)]
+ fn aes_256_ctr_test(testcase: CryptoProviderTestCase<AesCtr256>) {
+ testcase(PhantomData);
+ }
+}
diff --git a/nearby/crypto/crypto_provider_boringssl/src/aes.rs b/nearby/crypto/crypto_provider_boringssl/src/aes/mod.rs
index 88bfeac..d5cbe58 100644
--- a/nearby/crypto/crypto_provider_boringssl/src/aes.rs
+++ b/nearby/crypto/crypto_provider_boringssl/src/aes/mod.rs
@@ -17,6 +17,12 @@ use crypto_provider::aes::{
Aes, Aes128Key, Aes256Key, AesBlock, AesCipher, AesDecryptCipher, AesEncryptCipher, AesKey,
};
+/// AES_CTR implementations.
+pub mod ctr;
+
+/// AES_CBC implementations.
+pub mod cbc;
+
/// BoringSSL AES-128 operations
pub struct Aes128;
impl Aes for Aes128 {
diff --git a/nearby/crypto/crypto_provider_boringssl/src/ed25519.rs b/nearby/crypto/crypto_provider_boringssl/src/ed25519.rs
index 7c671ae..b5c374a 100644
--- a/nearby/crypto/crypto_provider_boringssl/src/ed25519.rs
+++ b/nearby/crypto/crypto_provider_boringssl/src/ed25519.rs
@@ -13,7 +13,8 @@
// limitations under the License.
use crypto_provider::ed25519::{
- InvalidBytes, RawPrivateKey, RawPublicKey, RawSignature, Signature as _, SignatureError,
+ InvalidBytes, RawPrivateKey, RawPrivateKeyPermit, RawPublicKey, RawSignature, Signature as _,
+ SignatureError,
};
pub struct Ed25519;
@@ -30,11 +31,11 @@ impl crypto_provider::ed25519::KeyPair for KeyPair {
type PublicKey = PublicKey;
type Signature = Signature;
- fn private_key(&self) -> RawPrivateKey {
+ fn raw_private_key(&self, _permit: &RawPrivateKeyPermit) -> RawPrivateKey {
self.0.to_seed()
}
- fn from_private_key(bytes: &RawPrivateKey) -> Self
+ fn from_raw_private_key(bytes: &RawPrivateKey, _permit: &RawPrivateKeyPermit) -> Self
where
Self: Sized,
{
diff --git a/nearby/crypto/crypto_provider_boringssl/src/lib.rs b/nearby/crypto/crypto_provider_boringssl/src/lib.rs
index 3b78898..75758b6 100644
--- a/nearby/crypto/crypto_provider_boringssl/src/lib.rs
+++ b/nearby/crypto/crypto_provider_boringssl/src/lib.rs
@@ -21,14 +21,12 @@
clippy::expect_used
)]
-//! Crate which provides impls for CryptoProvider backed by BoringSSL.
-
-use bssl_crypto::digest::{Sha256, Sha512};
+//! Crate which provides impls for CryptoProvider backed by BoringSSL
+//!
use bssl_crypto::rand::rand_bytes;
use crypto_provider::{CryptoProvider, CryptoRng};
-use crypto_provider_stubs::*;
-/// Implementation of `crypto_provider::aes` types using BoringSSL.
+/// Implementation of `crypto_provider::aes` types using BoringSSL
pub mod aes;
/// Implementations of crypto_provider::hkdf traits backed by BoringSSL
@@ -40,31 +38,45 @@ pub mod hmac;
/// Implementations of crypto_provider::ed25519 traits backed by BoringSSL
mod ed25519;
+/// Implementations of crypto_provider::aead traits backed by BoringSSL
+mod aead;
+
+/// Implementations of crypto_provider::p256 traits backed by BoringSSL
+mod p256;
+
+/// Implementations of crypto_provider::x25519 traits backed by BoringSSL
+mod x25519;
+
+/// Implementations of crypto_provider::sha2 traits backed by BoringSSL
+mod sha2;
+
/// The BoringSSL backed struct which implements CryptoProvider
#[derive(Default, Clone, Debug, PartialEq, Eq)]
pub struct Boringssl;
impl CryptoProvider for Boringssl {
- type HkdfSha256 = hkdf::Hkdf<Sha256>;
+ type HkdfSha256 = hkdf::Hkdf<bssl_crypto::digest::Sha256>;
type HmacSha256 = hmac::HmacSha256;
- type HkdfSha512 = hkdf::Hkdf<Sha512>;
+ type HkdfSha512 = hkdf::Hkdf<bssl_crypto::digest::Sha512>;
type HmacSha512 = hmac::HmacSha512;
- type AesCbcPkcs7Padded = AesCbcPkcs7PaddedStubs;
- type X25519 = X25519Stubs;
- type P256 = P256Stubs;
- type Sha256 = Sha2Stubs;
- type Sha512 = Sha2Stubs;
+ type AesCbcPkcs7Padded = aes::cbc::AesCbcPkcs7Padded;
+ type X25519 = x25519::X25519Ecdh;
+ type P256 = p256::P256Ecdh;
+ type Sha256 = sha2::Sha256;
+ type Sha512 = sha2::Sha512;
type Aes128 = aes::Aes128;
type Aes256 = aes::Aes256;
- type AesCtr128 = Aes128Stubs;
- type AesCtr256 = Aes256Stubs;
+ type AesCtr128 = aes::ctr::AesCtr128;
+ type AesCtr256 = aes::ctr::AesCtr256;
type Ed25519 = ed25519::Ed25519;
- type Aes128GcmSiv = Aes128Stubs;
- type Aes256GcmSiv = Aes256Stubs;
+ type Aes128GcmSiv = aead::aes_gcm_siv::AesGcmSiv;
+ type Aes256GcmSiv = aead::aes_gcm_siv::AesGcmSiv;
+ type Aes128Gcm = aead::aes_gcm::AesGcm;
+ type Aes256Gcm = aead::aes_gcm::AesGcm;
type CryptoRng = BoringSslRng;
- fn constant_time_eq(_a: &[u8], _b: &[u8]) -> bool {
- unimplemented!()
+ fn constant_time_eq(a: &[u8], b: &[u8]) -> bool {
+ bssl_crypto::mem::crypto_memcmp(a, b)
}
}
@@ -86,3 +98,17 @@ impl CryptoRng for BoringSslRng {
rand_bytes(dest)
}
}
+
+#[cfg(test)]
+mod tests {
+ use core::marker::PhantomData;
+ use crypto_provider_test::prelude::*;
+ use crypto_provider_test::sha2::*;
+
+ use crate::Boringssl;
+
+ #[apply(sha2_test_cases)]
+ fn sha2_tests(testcase: CryptoProviderTestCase<Boringssl>) {
+ testcase(PhantomData);
+ }
+}
diff --git a/nearby/crypto/crypto_provider_boringssl/src/p256.rs b/nearby/crypto/crypto_provider_boringssl/src/p256.rs
new file mode 100644
index 0000000..7fefecd
--- /dev/null
+++ b/nearby/crypto/crypto_provider_boringssl/src/p256.rs
@@ -0,0 +1,117 @@
+// 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.
+
+extern crate alloc;
+
+use bssl_crypto::ecdh::{PrivateKey, PublicKey};
+use crypto_provider::{
+ elliptic_curve::{EcdhProvider, EphemeralSecret, PublicKey as _},
+ p256::{PointCompression, P256},
+ tinyvec::ArrayVec,
+};
+
+/// Implementation of NIST-P256 using bssl-crypto crate.
+pub struct P256Ecdh;
+
+impl EcdhProvider<P256> for P256Ecdh {
+ type PublicKey = P256PublicKey;
+ type EphemeralSecret = P256EphemeralSecret;
+ type SharedSecret = [u8; 32];
+}
+
+/// A NIST-P256 public key.
+#[derive(Debug, PartialEq, Eq)]
+pub struct P256PublicKey(PublicKey<bssl_crypto::ecdh::P256>);
+impl crypto_provider::p256::P256PublicKey for P256PublicKey {
+ type Error = bssl_crypto::ecdh::Error;
+
+ fn from_sec1_bytes(bytes: &[u8]) -> Result<Self, Self::Error> {
+ PublicKey::try_from(bytes).map(Self)
+ }
+
+ fn to_sec1_bytes(&self, point_compression: PointCompression) -> ArrayVec<[u8; 65]> {
+ match point_compression {
+ PointCompression::Compressed => unimplemented!(),
+ PointCompression::Uncompressed => {
+ let mut bytes = ArrayVec::<[u8; 65]>::new();
+ bytes.extend_from_slice(&self.0.to_vec());
+ bytes
+ }
+ }
+ }
+
+ #[allow(clippy::expect_used)]
+ fn to_affine_coordinates(&self) -> Result<([u8; 32], [u8; 32]), Self::Error> {
+ Ok(self.0.to_affine_coordinates())
+ }
+ fn from_affine_coordinates(x: &[u8; 32], y: &[u8; 32]) -> Result<Self, Self::Error> {
+ PublicKey::<bssl_crypto::ecdh::P256>::from_affine_coordinates(x, y).map(Self)
+ }
+}
+
+/// Ephemeral secrect for use in a P256 Diffie-Hellman
+pub struct P256EphemeralSecret {
+ secret: PrivateKey<bssl_crypto::ecdh::P256>,
+}
+
+impl EphemeralSecret<P256> for P256EphemeralSecret {
+ type Impl = P256Ecdh;
+ type Error = bssl_crypto::ecdh::Error;
+ type Rng = ();
+ type EncodedPublicKey =
+ <P256PublicKey as crypto_provider::elliptic_curve::PublicKey<P256>>::EncodedPublicKey;
+
+ fn generate_random(_rng: &mut Self::Rng) -> Self {
+ Self { secret: PrivateKey::generate() }
+ }
+
+ fn public_key_bytes(&self) -> Self::EncodedPublicKey {
+ P256PublicKey((&self.secret).into()).to_bytes()
+ }
+
+ fn diffie_hellman(
+ self,
+ other_pub: &P256PublicKey,
+ ) -> Result<<Self::Impl as EcdhProvider<P256>>::SharedSecret, Self::Error> {
+ let shared_secret = self.secret.diffie_hellman(&other_pub.0)?;
+ let bytes: <Self::Impl as EcdhProvider<P256>>::SharedSecret = shared_secret.to_bytes();
+ Ok(bytes)
+ }
+}
+
+#[cfg(test)]
+impl crypto_provider_test::elliptic_curve::EphemeralSecretForTesting<P256> for P256EphemeralSecret {
+ #[allow(clippy::unwrap_used)]
+ fn from_private_components(
+ private_bytes: &[u8; 32],
+ _public_key: &P256PublicKey,
+ ) -> Result<Self, Self::Error> {
+ Ok(Self { secret: PrivateKey::from_private_bytes(private_bytes).unwrap() })
+ }
+}
+
+#[cfg(test)]
+mod tests {
+ use super::P256Ecdh;
+ use core::marker::PhantomData;
+ use crypto_provider_test::p256::*;
+
+ #[apply(p256_test_cases)]
+ fn p256_tests(testcase: CryptoProviderTestCase<P256Ecdh>, name: &str) {
+ if name == "to_bytes_compressed" {
+ return; // EC point compression not supported by bssl-crypto yet
+ }
+ testcase(PhantomData::<P256Ecdh>)
+ }
+}
diff --git a/nearby/crypto/crypto_provider_boringssl/src/sha2.rs b/nearby/crypto/crypto_provider_boringssl/src/sha2.rs
new file mode 100644
index 0000000..d95294d
--- /dev/null
+++ b/nearby/crypto/crypto_provider_boringssl/src/sha2.rs
@@ -0,0 +1,35 @@
+// 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.
+
+/// BoringSSL implementation for SHA256
+pub struct Sha256;
+
+impl crypto_provider::sha2::Sha256 for Sha256 {
+ fn sha256(input: &[u8]) -> [u8; 32] {
+ let mut digest = bssl_crypto::digest::Sha256::new_digest();
+ digest.update(input);
+ digest.finalize()
+ }
+}
+
+/// BoringSSL implementation for SHA512
+pub struct Sha512;
+
+impl crypto_provider::sha2::Sha512 for Sha512 {
+ fn sha512(input: &[u8]) -> [u8; 64] {
+ let mut digest = bssl_crypto::digest::Sha512::new_digest();
+ digest.update(input);
+ digest.finalize()
+ }
+}
diff --git a/nearby/crypto/crypto_provider_boringssl/src/x25519.rs b/nearby/crypto/crypto_provider_boringssl/src/x25519.rs
new file mode 100644
index 0000000..58d040b
--- /dev/null
+++ b/nearby/crypto/crypto_provider_boringssl/src/x25519.rs
@@ -0,0 +1,102 @@
+// 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::elliptic_curve::{EcdhProvider, EphemeralSecret, PublicKey};
+use crypto_provider::x25519::X25519;
+
+/// The BoringSSL implementation of X25519 ECDH.
+pub struct X25519Ecdh;
+
+impl EcdhProvider<X25519> for X25519Ecdh {
+ type PublicKey = X25519PublicKey;
+ type EphemeralSecret = X25519EphemeralSecret;
+ type SharedSecret = [u8; 32];
+}
+
+/// A X25519 ephemeral secret used for Diffie-Hellman.
+pub struct X25519EphemeralSecret {
+ secret: bssl_crypto::x25519::PrivateKey,
+}
+
+impl EphemeralSecret<X25519> for X25519EphemeralSecret {
+ type Impl = X25519Ecdh;
+ type Error = bssl_crypto::x25519::DiffieHellmanError;
+ type Rng = ();
+ type EncodedPublicKey = [u8; 32];
+
+ fn generate_random(_rng: &mut Self::Rng) -> Self {
+ Self { secret: bssl_crypto::x25519::PrivateKey::generate() }
+ }
+
+ fn public_key_bytes(&self) -> Self::EncodedPublicKey {
+ let pubkey: bssl_crypto::x25519::PublicKey = (&self.secret).into();
+ pubkey.to_bytes()
+ }
+
+ fn diffie_hellman(
+ self,
+ other_pub: &<X25519Ecdh as EcdhProvider<X25519>>::PublicKey,
+ ) -> Result<<X25519Ecdh as EcdhProvider<X25519>>::SharedSecret, Self::Error> {
+ self.secret.diffie_hellman(&other_pub.0).map(|secret| secret.to_bytes())
+ }
+}
+
+#[cfg(test)]
+impl crypto_provider_test::elliptic_curve::EphemeralSecretForTesting<X25519>
+ for X25519EphemeralSecret
+{
+ fn from_private_components(
+ private_bytes: &[u8; 32],
+ _public_key: &X25519PublicKey,
+ ) -> Result<Self, Self::Error> {
+ Ok(Self { secret: bssl_crypto::x25519::PrivateKey::from_private_bytes(private_bytes) })
+ }
+}
+
+/// A X25519 public key.
+#[derive(Debug, PartialEq, Eq)]
+pub struct X25519PublicKey(bssl_crypto::x25519::PublicKey);
+
+impl PublicKey<X25519> for X25519PublicKey {
+ type Error = Error;
+ type EncodedPublicKey = [u8; 32];
+
+ fn from_bytes(bytes: &[u8]) -> Result<Self, Self::Error> {
+ let byte_arr: [u8; 32] = bytes.try_into().map_err(|_| Error::WrongSize)?;
+ Ok(Self(bssl_crypto::x25519::PublicKey::from(&byte_arr)))
+ }
+
+ fn to_bytes(&self) -> Self::EncodedPublicKey {
+ self.0.to_bytes()
+ }
+}
+
+/// Error type for the BoringSSL implementation of x25519.
+#[derive(Debug)]
+pub enum Error {
+ /// Unexpected size for the given input.
+ WrongSize,
+}
+
+#[cfg(test)]
+mod tests {
+ use super::X25519Ecdh;
+ use core::marker::PhantomData;
+ use crypto_provider_test::x25519::*;
+
+ #[apply(x25519_test_cases)]
+ fn x25519_tests(testcase: CryptoProviderTestCase<X25519Ecdh>) {
+ testcase(PhantomData::<X25519Ecdh>)
+ }
+}
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 @@ version.workspace = true
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 @@ version.workspace = true
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 f5d5b00..857be74 100644
--- a/nearby/crypto/crypto_provider_openssl/src/aes.rs
+++ b/nearby/crypto/crypto_provider_openssl/src/aes.rs
@@ -26,10 +26,13 @@
use openssl::symm::{Cipher, Crypter, Mode};
-use crypto_provider::aes::{
- cbc::{AesCbcIv, DecryptionError},
- ctr::NonceAndCounter,
- Aes, Aes128Key, Aes256Key, AesBlock, AesCipher, AesDecryptCipher, AesEncryptCipher, AesKey,
+use crypto_provider::{
+ aes::{
+ cbc::{AesCbcIv, DecryptionError, EncryptionError},
+ ctr::NonceAndCounter,
+ Aes, Aes128Key, Aes256Key, AesBlock, AesCipher, AesDecryptCipher, AesEncryptCipher, AesKey,
+ },
+ tinyvec::SliceVec,
};
/// Uber struct which contains impls for AES-128 fns
@@ -68,7 +71,7 @@ impl AesEncryptCipher for Aes128Cipher {
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]);
}
}
@@ -80,7 +83,7 @@ impl AesDecryptCipher for Aes128Cipher {
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]);
}
}
@@ -103,7 +106,7 @@ impl AesEncryptCipher for Aes256Cipher {
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]);
}
}
@@ -115,7 +118,7 @@ impl AesDecryptCipher for Aes256Cipher {
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]);
}
}
@@ -126,7 +129,23 @@ pub struct OpenSslAesCbcPkcs7;
impl crypto_provider::aes::cbc::AesCbcPkcs7Padded for OpenSslAesCbcPkcs7 {
fn encrypt(key: &crypto_provider::aes::Aes256Key, iv: &AesCbcIv, message: &[u8]) -> Vec<u8> {
openssl::symm::encrypt(Cipher::aes_256_cbc(), key.as_slice(), Some(iv.as_slice()), message)
- .unwrap()
+ // The output buffer is allocated by the openssl crate and guarantees to have enough
+ // space to hold the output value and does not overlap with the input slice.
+ .expect("encrypt should always succeed")
+ }
+
+ fn encrypt_in_place(
+ key: &Aes256Key,
+ iv: &AesCbcIv,
+ message: &mut SliceVec<u8>,
+ ) -> Result<(), EncryptionError> {
+ let encrypted = Self::encrypt(key, iv, message);
+ if encrypted.len() > message.capacity() {
+ return Err(EncryptionError::PaddingFailed);
+ }
+ message.clear();
+ message.extend_from_slice(&encrypted);
+ Ok(())
}
fn decrypt(
@@ -142,12 +161,22 @@ impl crypto_provider::aes::cbc::AesCbcPkcs7Padded for OpenSslAesCbcPkcs7 {
)
.map_err(|_| DecryptionError::BadPadding)
}
+
+ fn decrypt_in_place(
+ key: &Aes256Key,
+ iv: &AesCbcIv,
+ ciphertext: &mut SliceVec<u8>,
+ ) -> Result<(), DecryptionError> {
+ Self::decrypt(key, iv, ciphertext).map(|result| {
+ ciphertext.clear();
+ ciphertext.extend_from_slice(&result);
+ })
+ }
}
/// OpenSSL implementation of AES-CTR-128
pub struct OpenSslAesCtr128 {
enc_cipher: Crypter,
- dec_cipher: Crypter,
}
impl crypto_provider::aes::ctr::AesCtr for OpenSslAesCtr128 {
@@ -161,33 +190,19 @@ impl crypto_provider::aes::ctr::AesCtr for OpenSslAesCtr128 {
Some(&nonce_and_counter.as_block_array()),
)
.unwrap(),
- dec_cipher: Crypter::new(
- Cipher::aes_128_ctr(),
- Mode::Decrypt,
- key.as_slice(),
- Some(&nonce_and_counter.as_block_array()),
- )
- .unwrap(),
}
}
- fn encrypt(&mut self, data: &mut [u8]) {
+ fn apply_keystream(&mut self, data: &mut [u8]) {
let mut in_slice = vec![0u8; data.len()];
in_slice.copy_from_slice(data);
let _ = self.enc_cipher.update(&in_slice, data);
}
-
- fn decrypt(&mut self, data: &mut [u8]) {
- let mut in_slice = vec![0u8; data.len()];
- in_slice.copy_from_slice(data);
- let _ = self.dec_cipher.update(&in_slice, data);
- }
}
/// OpenSSL implementation of AES-CTR-256
pub struct OpenSslAesCtr256 {
enc_cipher: Crypter,
- dec_cipher: Crypter,
}
impl crypto_provider::aes::ctr::AesCtr for OpenSslAesCtr256 {
@@ -201,27 +216,14 @@ impl crypto_provider::aes::ctr::AesCtr for OpenSslAesCtr256 {
Some(&nonce_and_counter.as_block_array()),
)
.unwrap(),
- dec_cipher: Crypter::new(
- Cipher::aes_256_ctr(),
- Mode::Decrypt,
- key.as_slice(),
- Some(&nonce_and_counter.as_block_array()),
- )
- .unwrap(),
}
}
- fn encrypt(&mut self, data: &mut [u8]) {
+ fn apply_keystream(&mut self, data: &mut [u8]) {
let mut in_slice = vec![0u8; data.len()];
in_slice.copy_from_slice(data);
let _ = self.enc_cipher.update(&in_slice, data);
}
-
- fn decrypt(&mut self, data: &mut [u8]) {
- let mut in_slice = vec![0u8; data.len()];
- in_slice.copy_from_slice(data);
- let _ = self.dec_cipher.update(&in_slice, data);
- }
}
#[cfg(test)]
diff --git a/nearby/crypto/crypto_provider_openssl/src/ed25519.rs b/nearby/crypto/crypto_provider_openssl/src/ed25519.rs
index 110daf2..111348a 100644
--- a/nearby/crypto/crypto_provider_openssl/src/ed25519.rs
+++ b/nearby/crypto/crypto_provider_openssl/src/ed25519.rs
@@ -13,7 +13,8 @@
// limitations under the License.
use crypto_provider::ed25519::{
- InvalidBytes, RawPrivateKey, RawPublicKey, RawSignature, Signature as _, SignatureError,
+ InvalidBytes, RawPrivateKey, RawPrivateKeyPermit, RawPublicKey, RawSignature, Signature as _,
+ SignatureError,
};
use openssl::pkey::{Id, PKey, Private};
use openssl::sign::{Signer, Verifier};
@@ -32,7 +33,7 @@ impl crypto_provider::ed25519::KeyPair for KeyPair {
type PublicKey = PublicKey;
type Signature = Signature;
- fn private_key(&self) -> RawPrivateKey {
+ fn raw_private_key(&self, _permit: &RawPrivateKeyPermit) -> RawPrivateKey {
let private_key = self.0.raw_private_key().unwrap();
let mut public_key = self.0.raw_public_key().unwrap();
let mut result = private_key;
@@ -40,7 +41,7 @@ impl crypto_provider::ed25519::KeyPair for KeyPair {
result.try_into().unwrap()
}
- fn from_private_key(bytes: &RawPrivateKey) -> Self {
+ fn from_raw_private_key(bytes: &RawPrivateKey, _permit: &RawPrivateKeyPermit) -> Self {
Self(PKey::private_key_from_raw_bytes(bytes, Id::ED25519).unwrap())
}
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 @@ impl<H: OpenSslHash> crypto_provider::hkdf::Hkdf for Hkdf<H> {
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 @@ impl<const N: usize, H: Hash<N>> crypto_provider::hmac::Hmac<N> for Hmac<H> {
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 d8157d2..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;
@@ -82,6 +85,8 @@ impl crypto_provider::CryptoProvider for Openssl {
type Ed25519 = ed25519::Ed25519;
type Aes128GcmSiv = crypto_provider_stubs::Aes128Stubs;
type Aes256GcmSiv = crypto_provider_stubs::Aes256Stubs;
+ type Aes128Gcm = crypto_provider_stubs::Aes128Stubs;
+ type Aes256Gcm = crypto_provider_stubs::Aes256Stubs;
type CryptoRng = OpenSslRng;
fn constant_time_eq(a: &[u8], b: &[u8]) -> bool {
@@ -113,7 +118,7 @@ mod tests {
use core::marker::PhantomData;
use crypto_provider_test::sha2::*;
- use crypto_provider_test::*;
+ use crypto_provider_test::{prelude::*, *};
use crate::Openssl;
diff --git a/nearby/crypto/crypto_provider_openssl/src/p256.rs b/nearby/crypto/crypto_provider_openssl/src/p256.rs
index 2f5b0b1..17dd6ce 100644
--- a/nearby/crypto/crypto_provider_openssl/src/p256.rs
+++ b/nearby/crypto/crypto_provider_openssl/src/p256.rs
@@ -12,8 +12,11 @@
// See the License for the specific language governing permissions and
// limitations under the License.
-use crypto_provider::elliptic_curve::{EcdhProvider, EphemeralSecret};
-use crypto_provider::p256::P256;
+use crypto_provider::{
+ elliptic_curve::{EcdhProvider, EphemeralSecret},
+ p256::{PointCompression, P256},
+ tinyvec::ArrayVec,
+};
use openssl::bn::{BigNum, BigNumContext};
use openssl::derive::Deriver;
use openssl::ec::{EcGroup, EcKey, EcPoint, PointConversionForm};
@@ -65,15 +68,24 @@ impl crypto_provider::p256::P256PublicKey for P256PublicKey {
Ok(Self(eckey.try_into()?))
}
- fn to_sec1_bytes(&self) -> Vec<u8> {
+ fn to_sec1_bytes(&self, point_compression: PointCompression) -> ArrayVec<[u8; 65]> {
+ let point_conversion_form = match point_compression {
+ PointCompression::Compressed => PointConversionForm::COMPRESSED,
+ PointCompression::Uncompressed => PointConversionForm::UNCOMPRESSED,
+ };
let ecgroup = EcGroup::from_curve_name(Nid::X9_62_PRIME256V1).unwrap();
let mut bncontext = BigNumContext::new().unwrap();
- self.0
- .ec_key()
- .unwrap()
- .public_key()
- .to_bytes(&ecgroup, PointConversionForm::COMPRESSED, &mut bncontext)
- .unwrap()
+ let mut bytes = ArrayVec::<[u8; 65]>::new();
+ bytes.extend_from_slice(
+ &self
+ .0
+ .ec_key()
+ .unwrap()
+ .public_key()
+ .to_bytes(&ecgroup, point_conversion_form, &mut bncontext)
+ .unwrap(),
+ );
+ bytes
}
fn from_affine_coordinates(x: &[u8; 32], y: &[u8; 32]) -> Result<Self, Self::Error> {
@@ -115,6 +127,7 @@ impl EphemeralSecret<P256> for P256EphemeralSecret {
type Impl = P256Ecdh;
type Error = Error;
type Rng = ();
+ type EncodedPublicKey = ArrayVec<[u8; 65]>;
fn generate_random(_rng: &mut Self::Rng) -> Self {
let ecgroup = EcGroup::from_curve_name(Nid::X9_62_PRIME256V1).unwrap();
@@ -122,15 +135,20 @@ impl EphemeralSecret<P256> for P256EphemeralSecret {
Self(eckey.try_into().unwrap())
}
- fn public_key_bytes(&self) -> Vec<u8> {
+ fn public_key_bytes(&self) -> Self::EncodedPublicKey {
let ecgroup = EcGroup::from_curve_name(Nid::X9_62_PRIME256V1).unwrap();
let mut bncontext = BigNumContext::new().unwrap();
- self.0
- .ec_key()
- .unwrap()
- .public_key()
- .to_bytes(&ecgroup, PointConversionForm::COMPRESSED, &mut bncontext)
- .unwrap()
+ let mut bytes = Self::EncodedPublicKey::new();
+ bytes.extend_from_slice(
+ &self
+ .0
+ .ec_key()
+ .unwrap()
+ .public_key()
+ .to_bytes(&ecgroup, PointConversionForm::COMPRESSED, &mut bncontext)
+ .unwrap(),
+ );
+ bytes
}
fn diffie_hellman(
@@ -178,7 +196,7 @@ mod tests {
use crypto_provider_test::p256::*;
#[apply(p256_test_cases)]
- fn p256_tests(testcase: CryptoProviderTestCase<P256Ecdh>) {
- testcase(PhantomData::<P256Ecdh>)
+ fn p256_tests(testcase: CryptoProviderTestCase<P256Ecdh>, _name: &str) {
+ testcase(PhantomData)
}
}
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 @@ impl crypto_provider::sha2::Sha256 for OpenSslSha256 {
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 @@ impl crypto_provider::sha2::Sha512 for OpenSslSha512 {
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_openssl/src/x25519.rs b/nearby/crypto/crypto_provider_openssl/src/x25519.rs
index ff6f3b0..2cb4cab 100644
--- a/nearby/crypto/crypto_provider_openssl/src/x25519.rs
+++ b/nearby/crypto/crypto_provider_openssl/src/x25519.rs
@@ -30,14 +30,15 @@ impl PartialEq for X25519PublicKey {
impl PublicKey<X25519> for X25519PublicKey {
type Error = ErrorStack;
+ type EncodedPublicKey = [u8; 32];
fn from_bytes(bytes: &[u8]) -> Result<Self, Self::Error> {
let key = PKey::public_key_from_raw_bytes(bytes, Id::X25519)?;
Ok(X25519PublicKey(key))
}
- fn to_bytes(&self) -> Vec<u8> {
- self.0.raw_public_key().unwrap()
+ fn to_bytes(&self) -> Self::EncodedPublicKey {
+ self.0.raw_public_key().unwrap().try_into().unwrap()
}
}
@@ -48,14 +49,15 @@ impl EphemeralSecret<X25519> for X25519PrivateKey {
type Impl = X25519Ecdh;
type Error = ErrorStack;
type Rng = ();
+ type EncodedPublicKey = [u8; 32];
fn generate_random(_rng: &mut Self::Rng) -> Self {
let private_key = openssl::pkey::PKey::generate_x25519().unwrap();
Self(private_key)
}
- fn public_key_bytes(&self) -> Vec<u8> {
- self.0.raw_public_key().unwrap()
+ fn public_key_bytes(&self) -> Self::EncodedPublicKey {
+ self.0.raw_public_key().unwrap().try_into().unwrap()
}
fn diffie_hellman(
diff --git a/nearby/crypto/crypto_provider_rustcrypto/Cargo.toml b/nearby/crypto/crypto_provider_rustcrypto/Cargo.toml
index e0bf534..14e8ad9 100644
--- a/nearby/crypto/crypto_provider_rustcrypto/Cargo.toml
+++ b/nearby/crypto/crypto_provider_rustcrypto/Cargo.toml
@@ -4,17 +4,27 @@ version.workspace = true
edition.workspace = true
publish.workspace = true
+[lints]
+workspace = true
+
[dependencies]
aead = "0.5.1"
-aes-gcm-siv = { version = "0.11.1", features = ["aes"] }
-crypto_provider.workspace = true
+aes-gcm-siv = { version = "0.11.1", features = [
+ "aes",
+], default-features = false }
+aes-gcm = { version = "0.10.3", features = [
+ "aes",
+], default-features = false }
+crypto_provider = { workspace = true }
hmac.workspace = true
hkdf.workspace = true
sha2.workspace = true
x25519-dalek.workspace = true
p256 = { workspace = true, features = ["ecdh"], default-features = false }
sec1.workspace = true
-ed25519-dalek = { workspace = true, default-features = false, features = ["rand_core"] }
+ed25519-dalek = { workspace = true, default-features = false, features = [
+ "rand_core",
+] }
rand = { workspace = true, default-features = false }
rand_core.workspace = true
subtle.workspace = true
@@ -31,5 +41,12 @@ crypto_provider_rustcrypto = { path = ".", features = ["std"] }
[features]
default = ["alloc", "rand_chacha"]
-std = ["ed25519-dalek/default", "rand/std", "rand/std_rng", "crypto_provider/std", "crypto_provider/alloc"]
-alloc = ["aead/bytes"]
+std = [
+ "alloc",
+ "ed25519-dalek/default",
+ "rand/std",
+ "rand/std_rng",
+ "crypto_provider/std",
+ "crypto_provider/alloc",
+]
+alloc = ["aead/bytes", "aead/alloc", "cbc/alloc", "crypto_provider/alloc"]
diff --git a/nearby/crypto/crypto_provider_rustcrypto/src/aead/aes_gcm.rs b/nearby/crypto/crypto_provider_rustcrypto/src/aead/aes_gcm.rs
new file mode 100644
index 0000000..f1d9e0f
--- /dev/null
+++ b/nearby/crypto/crypto_provider_rustcrypto/src/aead/aes_gcm.rs
@@ -0,0 +1,123 @@
+// 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.
+
+#[cfg(feature = "alloc")]
+extern crate alloc;
+#[cfg(feature = "alloc")]
+use aead::Payload;
+#[cfg(feature = "alloc")]
+use alloc::vec::Vec;
+
+// RustCrypto defined traits and types
+use aes::cipher::typenum::consts::{U12, U16};
+use aes::cipher::BlockCipher;
+use aes::cipher::BlockEncrypt;
+#[cfg(feature = "alloc")]
+use aes_gcm::aead::Aead as _;
+use aes_gcm::aead::KeyInit;
+use aes_gcm::AeadInPlace as _;
+
+// CryptoProvider traits and types
+use crypto_provider::aead::{Aead, AeadError, AeadInit};
+
+pub struct AesGcm<A: BlockCipher<BlockSize = U16> + BlockEncrypt + KeyInit>(
+ aes_gcm::AesGcm<A, U12>,
+);
+
+impl<A: BlockCipher<BlockSize = U16> + BlockEncrypt + KeyInit> crypto_provider::aead::AesGcm
+ for AesGcm<A>
+{
+}
+
+impl<K: crypto_provider::aes::AesKey, A: BlockCipher<BlockSize = U16> + BlockEncrypt + KeyInit>
+ AeadInit<K> for AesGcm<A>
+{
+ fn new(key: &K) -> Self {
+ Self(aes_gcm::AesGcm::<A, U12>::new(key.as_slice().into()))
+ }
+}
+
+impl<A: aes::cipher::BlockCipher<BlockSize = U16> + BlockEncrypt + KeyInit> Aead for AesGcm<A> {
+ const TAG_SIZE: usize = 16;
+ type Nonce = [u8; 12];
+ type Tag = [u8; 16];
+
+ #[cfg(feature = "alloc")]
+ fn encrypt(&self, msg: &[u8], aad: &[u8], nonce: &Self::Nonce) -> Result<Vec<u8>, AeadError> {
+ self.0
+ .encrypt(aes_gcm::Nonce::from_slice(nonce), Payload { msg, aad })
+ .map_err(|_| AeadError)
+ }
+
+ fn encrypt_detached(
+ &self,
+ msg: &mut [u8],
+ aad: &[u8],
+ nonce: &Self::Nonce,
+ ) -> Result<Self::Tag, AeadError> {
+ self.0
+ .encrypt_in_place_detached(aes_gcm::Nonce::from_slice(nonce), aad, msg)
+ .map(|arr| arr.into())
+ .map_err(|_| AeadError)
+ }
+
+ #[cfg(feature = "alloc")]
+ fn decrypt(&self, msg: &[u8], aad: &[u8], nonce: &Self::Nonce) -> Result<Vec<u8>, AeadError> {
+ self.0
+ .decrypt(aes_gcm::Nonce::from_slice(nonce), Payload { msg, aad })
+ .map_err(|_| AeadError)
+ }
+
+ fn decrypt_detached(
+ &self,
+ msg: &mut [u8],
+ aad: &[u8],
+ nonce: &Self::Nonce,
+ tag: &Self::Tag,
+ ) -> Result<(), AeadError> {
+ self.0
+ .decrypt_in_place_detached(aes_gcm::Nonce::from_slice(nonce), aad, msg, tag.into())
+ .map_err(|_| AeadError)
+ }
+}
+
+#[cfg(test)]
+mod tests {
+ use core::marker::PhantomData;
+
+ use crypto_provider_test::aead::aes_gcm::*;
+ use crypto_provider_test::aes::*;
+
+ use super::*;
+
+ #[apply(aes_128_gcm_test_cases)]
+ fn aes_gcm_128_test(testcase: CryptoProviderTestCase<AesGcm<aes::Aes128>>) {
+ testcase(PhantomData);
+ }
+
+ #[apply(aes_128_gcm_test_cases_detached)]
+ fn aes_128_gcm_test_detached(testcase: CryptoProviderTestCase<AesGcm<aes::Aes128>>) {
+ testcase(PhantomData);
+ }
+
+ #[apply(aes_256_gcm_test_cases)]
+ fn aes_gcm_256_test(testcase: CryptoProviderTestCase<AesGcm<aes::Aes256>>) {
+ testcase(PhantomData);
+ }
+
+ #[apply(aes_256_gcm_test_cases_detached)]
+ fn aes_256_gcm_test_detached(testcase: CryptoProviderTestCase<AesGcm<aes::Aes256>>) {
+ testcase(PhantomData);
+ }
+}
diff --git a/nearby/crypto/crypto_provider_rustcrypto/src/aead/aes_gcm_siv.rs b/nearby/crypto/crypto_provider_rustcrypto/src/aead/aes_gcm_siv.rs
index 402c2ed..408017f 100644
--- a/nearby/crypto/crypto_provider_rustcrypto/src/aead/aes_gcm_siv.rs
+++ b/nearby/crypto/crypto_provider_rustcrypto/src/aead/aes_gcm_siv.rs
@@ -12,74 +12,118 @@
// See the License for the specific language governing permissions and
// limitations under the License.
-use aes_gcm_siv::{AeadInPlace, Aes128GcmSiv, Aes256GcmSiv, KeyInit, Nonce};
+#[cfg(feature = "alloc")]
extern crate alloc;
+#[cfg(feature = "alloc")]
+use aead::Payload;
+#[cfg(feature = "alloc")]
use alloc::vec::Vec;
-use crypto_provider::aead::{Aead, AeadError};
-use crypto_provider::aead::aes_gcm_siv::AesGcmSiv;
-use crypto_provider::aes::{Aes128Key, Aes256Key, AesKey};
-
-pub struct AesGcmSiv128(Aes128GcmSiv);
-
-impl AesGcmSiv for AesGcmSiv128 {}
-
-impl Aead for AesGcmSiv128 {
- const TAG_SIZE: usize = 16;
- type Nonce = [u8; 12];
- type Key = Aes128Key;
-
- fn new(key: &Self::Key) -> Self {
- Self(Aes128GcmSiv::new(key.as_slice().into()))
- }
-
- fn encrypt(&self, msg: &mut Vec<u8>, aad: &[u8], nonce: &[u8; 12]) -> Result<(), AeadError> {
- self.0.encrypt_in_place(Nonce::from_slice(nonce), aad, msg).map_err(|_| AeadError)
- }
+// RustCrypto defined traits and types
+use aes::cipher::typenum::consts::U16;
+use aes::cipher::BlockCipher;
+use aes::cipher::BlockEncrypt;
+#[cfg(feature = "alloc")]
+use aes_gcm_siv::aead::Aead as _;
+use aes_gcm_siv::aead::KeyInit;
+use aes_gcm_siv::AeadInPlace as _;
+
+// CryptoProvider traits and types
+use crypto_provider::aead::{Aead, AeadError, AeadInit};
+
+pub struct AesGcmSiv<A: BlockCipher<BlockSize = U16> + BlockEncrypt + KeyInit>(
+ aes_gcm_siv::AesGcmSiv<A>,
+);
+
+impl<A: BlockCipher<BlockSize = U16> + BlockEncrypt + KeyInit> crypto_provider::aead::AesGcmSiv
+ for AesGcmSiv<A>
+{
+}
- fn decrypt(&self, msg: &mut Vec<u8>, aad: &[u8], nonce: &[u8; 12]) -> Result<(), AeadError> {
- self.0.decrypt_in_place(Nonce::from_slice(nonce), aad, msg).map_err(|_| AeadError)
+impl<K: crypto_provider::aes::AesKey, A: BlockCipher<BlockSize = U16> + BlockEncrypt + KeyInit>
+ AeadInit<K> for AesGcmSiv<A>
+{
+ fn new(key: &K) -> Self {
+ Self(aes_gcm_siv::AesGcmSiv::<A>::new(key.as_slice().into()))
}
}
-pub struct AesGcmSiv256(Aes256GcmSiv);
-
-impl AesGcmSiv for AesGcmSiv256 {}
-
-impl Aead for AesGcmSiv256 {
+impl<A: aes::cipher::BlockCipher<BlockSize = U16> + BlockEncrypt + KeyInit> Aead for AesGcmSiv<A> {
const TAG_SIZE: usize = 16;
type Nonce = [u8; 12];
- type Key = Aes256Key;
+ type Tag = [u8; 16];
+
+ #[cfg(feature = "alloc")]
+ fn encrypt(&self, msg: &[u8], aad: &[u8], nonce: &Self::Nonce) -> Result<Vec<u8>, AeadError> {
+ self.0
+ .encrypt(aes_gcm_siv::Nonce::from_slice(nonce), Payload { msg, aad })
+ .map_err(|_| AeadError)
+ }
- fn new(key: &Self::Key) -> Self {
- Self(Aes256GcmSiv::new(key.as_slice().into()))
+ fn encrypt_detached(
+ &self,
+ msg: &mut [u8],
+ aad: &[u8],
+ nonce: &Self::Nonce,
+ ) -> Result<Self::Tag, AeadError> {
+ self.0
+ .encrypt_in_place_detached(aes_gcm_siv::Nonce::from_slice(nonce), aad, msg)
+ .map(|arr| arr.into())
+ .map_err(|_| AeadError)
}
- fn encrypt(&self, msg: &mut Vec<u8>, aad: &[u8], nonce: &[u8; 12]) -> Result<(), AeadError> {
- self.0.encrypt_in_place(Nonce::from_slice(nonce), aad, msg).map_err(|_| AeadError)
+ #[cfg(feature = "alloc")]
+ fn decrypt(&self, msg: &[u8], aad: &[u8], nonce: &Self::Nonce) -> Result<Vec<u8>, AeadError> {
+ self.0
+ .decrypt(aes_gcm_siv::Nonce::from_slice(nonce), Payload { msg, aad })
+ .map_err(|_| AeadError)
}
- fn decrypt(&self, msg: &mut Vec<u8>, aad: &[u8], nonce: &[u8; 12]) -> Result<(), AeadError> {
- self.0.decrypt_in_place(Nonce::from_slice(nonce), aad, msg).map_err(|_| AeadError)
+ fn decrypt_detached(
+ &self,
+ msg: &mut [u8],
+ aad: &[u8],
+ nonce: &Self::Nonce,
+ tag: &Self::Tag,
+ ) -> Result<(), AeadError> {
+ self.0
+ .decrypt_in_place_detached(aes_gcm_siv::Nonce::from_slice(nonce), aad, msg, tag.into())
+ .map_err(|_| AeadError)
}
}
#[cfg(test)]
mod tests {
use core::marker::PhantomData;
-
use crypto_provider_test::aead::aes_gcm_siv::*;
use crypto_provider_test::aes::*;
-
- use super::*;
+ use crypto_provider_test::prelude::apply;
#[apply(aes_128_gcm_siv_test_cases)]
- fn aes_gcm_siv_128_test(testcase: CryptoProviderTestCase<AesGcmSiv128>) {
+ fn aes_gcm_siv_128_test(
+ testcase: CryptoProviderTestCase<crate::aead::aes_gcm_siv::AesGcmSiv<aes::Aes128>>,
+ ) {
+ testcase(PhantomData);
+ }
+
+ #[apply(aes_128_gcm_siv_test_cases_detached)]
+ fn aes_gcm_siv_128_test_detached(
+ testcase: CryptoProviderTestCase<crate::aead::aes_gcm_siv::AesGcmSiv<aes::Aes128>>,
+ ) {
testcase(PhantomData);
}
#[apply(aes_256_gcm_siv_test_cases)]
- fn aes_gcm_siv_256_test(testcase: CryptoProviderTestCase<AesGcmSiv256>) {
+ fn aes_gcm_siv_256_test(
+ testcase: CryptoProviderTestCase<crate::aead::aes_gcm_siv::AesGcmSiv<aes::Aes256>>,
+ ) {
+ testcase(PhantomData);
+ }
+
+ #[apply(aes_256_gcm_siv_test_cases_detached)]
+ fn aes_gcm_siv_256_test_detached(
+ testcase: CryptoProviderTestCase<crate::aead::aes_gcm_siv::AesGcmSiv<aes::Aes256>>,
+ ) {
testcase(PhantomData);
}
}
diff --git a/nearby/crypto/crypto_provider_rustcrypto/src/aead/mod.rs b/nearby/crypto/crypto_provider_rustcrypto/src/aead/mod.rs
index 7fc561b..e4c2c60 100644
--- a/nearby/crypto/crypto_provider_rustcrypto/src/aead/mod.rs
+++ b/nearby/crypto/crypto_provider_rustcrypto/src/aead/mod.rs
@@ -13,3 +13,5 @@
// limitations under the License.
pub(crate) mod aes_gcm_siv;
+
+pub(crate) mod aes_gcm;
diff --git a/nearby/crypto/crypto_provider_rustcrypto/src/aes/cbc.rs b/nearby/crypto/crypto_provider_rustcrypto/src/aes/cbc.rs
deleted file mode 100644
index 06d7224..0000000
--- a/nearby/crypto/crypto_provider_rustcrypto/src/aes/cbc.rs
+++ /dev/null
@@ -1,53 +0,0 @@
-// Copyright 2022 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.
-
-extern crate alloc;
-use aes::cipher::{block_padding::Pkcs7, BlockDecryptMut, BlockEncryptMut, KeyIvInit};
-use aes::Aes256;
-use alloc::vec::Vec;
-use crypto_provider::aes::{
- cbc::{AesCbcIv, DecryptionError},
- Aes256Key, AesKey,
-};
-
-/// RustCrypto implementation of AES-CBC with PKCS7 padding
-pub enum AesCbcPkcs7Padded {}
-impl crypto_provider::aes::cbc::AesCbcPkcs7Padded for AesCbcPkcs7Padded {
- fn encrypt(key: &Aes256Key, iv: &AesCbcIv, message: &[u8]) -> Vec<u8> {
- let encryptor = cbc::Encryptor::<Aes256>::new(key.as_array().into(), iv.into());
- encryptor.encrypt_padded_vec_mut::<Pkcs7>(message)
- }
-
- fn decrypt(
- key: &Aes256Key,
- iv: &AesCbcIv,
- ciphertext: &[u8],
- ) -> Result<Vec<u8>, DecryptionError> {
- cbc::Decryptor::<Aes256>::new(key.as_array().into(), iv.into())
- .decrypt_padded_vec_mut::<Pkcs7>(ciphertext)
- .map_err(|_| DecryptionError::BadPadding)
- }
-}
-
-#[cfg(test)]
-mod tests {
- use super::AesCbcPkcs7Padded;
- use core::marker::PhantomData;
- use crypto_provider_test::aes::cbc::*;
-
- #[apply(aes_256_cbc_test_cases)]
- fn aes_256_cbc_test(testcase: CryptoProviderTestCase<AesCbcPkcs7Padded>) {
- testcase(PhantomData);
- }
-}
diff --git a/nearby/crypto/crypto_provider_rustcrypto/src/aes_cp/cbc.rs b/nearby/crypto/crypto_provider_rustcrypto/src/aes_cp/cbc.rs
new file mode 100644
index 0000000..c6d74f0
--- /dev/null
+++ b/nearby/crypto/crypto_provider_rustcrypto/src/aes_cp/cbc.rs
@@ -0,0 +1,102 @@
+// Copyright 2022 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.
+
+#[cfg(feature = "alloc")]
+extern crate alloc;
+
+use aes::cipher::{block_padding::Pkcs7, BlockDecryptMut, BlockEncryptMut, KeyIvInit};
+use aes::Aes256;
+#[cfg(feature = "alloc")]
+use alloc::vec::Vec;
+use crypto_provider::{
+ aes::{
+ cbc::{AesCbcIv, DecryptionError, EncryptionError},
+ Aes256Key, AesKey,
+ },
+ tinyvec::SliceVec,
+};
+
+/// RustCrypto implementation of AES-CBC with PKCS7 padding
+pub enum AesCbcPkcs7Padded {}
+impl crypto_provider::aes::cbc::AesCbcPkcs7Padded for AesCbcPkcs7Padded {
+ #[cfg(feature = "alloc")]
+ fn encrypt(key: &Aes256Key, iv: &AesCbcIv, message: &[u8]) -> Vec<u8> {
+ let encryptor = cbc::Encryptor::<Aes256>::new(key.as_array().into(), iv.into());
+ encryptor.encrypt_padded_vec_mut::<Pkcs7>(message)
+ }
+
+ fn encrypt_in_place(
+ key: &Aes256Key,
+ iv: &AesCbcIv,
+ message: &mut SliceVec<u8>,
+ ) -> Result<(), EncryptionError> {
+ let encryptor = cbc::Encryptor::<Aes256>::new(key.as_array().into(), iv.into());
+ let message_len = message.len();
+ // Set the length so encrypt_padded_mut can write using the full capacity
+ // (Unlike `Vec.set_len`, `SliceVec.set_len` is safe and won't panic if len <= capacity)
+ message.set_len(message.capacity());
+ encryptor
+ .encrypt_padded_mut::<Pkcs7>(message, message_len)
+ .map(|result| result.len())
+ // `SliceVec.set_len` is safe, and won't panic because `encrypt_padded_mut` never
+ // returns a slice longer than the given buffer.
+ .map(|new_len| message.set_len(new_len))
+ .map_err(|_| {
+ message.set_len(message_len); // Set the buffer back to its original length
+ EncryptionError::PaddingFailed
+ })
+ }
+
+ #[cfg(feature = "alloc")]
+ fn decrypt(
+ key: &Aes256Key,
+ iv: &AesCbcIv,
+ ciphertext: &[u8],
+ ) -> Result<Vec<u8>, DecryptionError> {
+ cbc::Decryptor::<Aes256>::new(key.as_array().into(), iv.into())
+ .decrypt_padded_vec_mut::<Pkcs7>(ciphertext)
+ .map_err(|_| DecryptionError::BadPadding)
+ }
+
+ fn decrypt_in_place(
+ key: &Aes256Key,
+ iv: &AesCbcIv,
+ ciphertext: &mut SliceVec<u8>,
+ ) -> Result<(), DecryptionError> {
+ // Decrypted size is always smaller than the input size because of padding, so we don't need
+ // to set the length to the full capacity.
+ cbc::Decryptor::<Aes256>::new(key.as_array().into(), iv.into())
+ .decrypt_padded_mut::<Pkcs7>(ciphertext)
+ .map(|result| result.len())
+ // `SliceVec.set_len` is safe, and won't panic because decrypted result length is always
+ // smaller than the input size.
+ .map(|new_len| ciphertext.set_len(new_len))
+ .map_err(|_| {
+ ciphertext.as_mut().fill(0);
+ DecryptionError::BadPadding
+ })
+ }
+}
+
+#[cfg(test)]
+mod tests {
+ use super::AesCbcPkcs7Padded;
+ use core::marker::PhantomData;
+ use crypto_provider_test::aes::cbc::*;
+
+ #[apply(aes_256_cbc_test_cases)]
+ fn aes_256_cbc_test(testcase: CryptoProviderTestCase<AesCbcPkcs7Padded>) {
+ testcase(PhantomData);
+ }
+}
diff --git a/nearby/crypto/crypto_provider_rustcrypto/src/aes/ctr.rs b/nearby/crypto/crypto_provider_rustcrypto/src/aes_cp/ctr.rs
index 85cdac6..09ae557 100644
--- a/nearby/crypto/crypto_provider_rustcrypto/src/aes/ctr.rs
+++ b/nearby/crypto/crypto_provider_rustcrypto/src/aes_cp/ctr.rs
@@ -33,11 +33,7 @@ impl crypto_provider::aes::ctr::AesCtr for AesCtr128 {
}
}
- fn encrypt(&mut self, data: &mut [u8]) {
- self.cipher.apply_keystream(data);
- }
-
- fn decrypt(&mut self, data: &mut [u8]) {
+ fn apply_keystream(&mut self, data: &mut [u8]) {
self.cipher.apply_keystream(data);
}
}
@@ -59,11 +55,7 @@ impl crypto_provider::aes::ctr::AesCtr for AesCtr256 {
}
}
- fn encrypt(&mut self, data: &mut [u8]) {
- self.cipher.apply_keystream(data);
- }
-
- fn decrypt(&mut self, data: &mut [u8]) {
+ fn apply_keystream(&mut self, data: &mut [u8]) {
self.cipher.apply_keystream(data);
}
}
diff --git a/nearby/crypto/crypto_provider_rustcrypto/src/aes/mod.rs b/nearby/crypto/crypto_provider_rustcrypto/src/aes_cp/mod.rs
index c71f2ec..4c04bf5 100644
--- a/nearby/crypto/crypto_provider_rustcrypto/src/aes/mod.rs
+++ b/nearby/crypto/crypto_provider_rustcrypto/src/aes_cp/mod.rs
@@ -22,7 +22,6 @@ use crypto_provider::aes::{
};
/// Module implementing AES-CBC.
-#[cfg(feature = "alloc")]
pub(crate) mod cbc;
pub(crate) mod ctr;
diff --git a/nearby/crypto/crypto_provider_rustcrypto/src/ed25519.rs b/nearby/crypto/crypto_provider_rustcrypto/src/ed25519.rs
index d11a5ea..ce8089e 100644
--- a/nearby/crypto/crypto_provider_rustcrypto/src/ed25519.rs
+++ b/nearby/crypto/crypto_provider_rustcrypto/src/ed25519.rs
@@ -15,8 +15,8 @@
use ed25519_dalek::Signer;
use crypto_provider::ed25519::{
- InvalidBytes, RawPrivateKey, RawPublicKey, RawSignature, Signature as _, SignatureError,
- PRIVATE_KEY_LENGTH, PUBLIC_KEY_LENGTH, SIGNATURE_LENGTH,
+ InvalidBytes, RawPrivateKey, RawPrivateKeyPermit, RawPublicKey, RawSignature, Signature as _,
+ SignatureError, PRIVATE_KEY_LENGTH, PUBLIC_KEY_LENGTH, SIGNATURE_LENGTH,
};
pub struct Ed25519;
@@ -33,11 +33,11 @@ impl crypto_provider::ed25519::KeyPair for KeyPair {
type PublicKey = PublicKey;
type Signature = Signature;
- fn private_key(&self) -> [u8; PRIVATE_KEY_LENGTH] {
+ fn raw_private_key(&self, _permit: &RawPrivateKeyPermit) -> [u8; PRIVATE_KEY_LENGTH] {
self.0.to_bytes()
}
- fn from_private_key(bytes: &RawPrivateKey) -> Self {
+ fn from_raw_private_key(bytes: &RawPrivateKey, _permit: &RawPrivateKeyPermit) -> Self {
Self(ed25519_dalek::SigningKey::from_bytes(bytes))
}
diff --git a/nearby/crypto/crypto_provider_rustcrypto/src/hkdf_rc.rs b/nearby/crypto/crypto_provider_rustcrypto/src/hkdf_cp.rs
index aba0d4a..48380fa 100644
--- a/nearby/crypto/crypto_provider_rustcrypto/src/hkdf_rc.rs
+++ b/nearby/crypto/crypto_provider_rustcrypto/src/hkdf_cp.rs
@@ -22,7 +22,7 @@ use hmac::digest::typenum::{IsLess, Le, NonZero};
use hmac::digest::{HashMarker, OutputSizeUser};
/// RustCrypto based hkdf implementation
-pub struct Hkdf<D>
+pub struct Hkdf<D>(hkdf::Hkdf<D>)
where
D: OutputSizeUser,
D: CoreProxy,
@@ -33,10 +33,7 @@ where
+ Default
+ Clone,
<D::Core as BlockSizeUser>::BlockSize: IsLess<U256>,
- Le<<D::Core as BlockSizeUser>::BlockSize, U256>: NonZero,
-{
- hkdf_impl: hkdf::Hkdf<D>,
-}
+ Le<<D::Core as BlockSizeUser>::BlockSize, U256>: NonZero;
impl<D> crypto_provider::hkdf::Hkdf for Hkdf<D>
where
@@ -52,7 +49,7 @@ where
Le<<D::Core as BlockSizeUser>::BlockSize, U256>: NonZero,
{
fn new(salt: Option<&[u8]>, ikm: &[u8]) -> Self {
- Hkdf { hkdf_impl: hkdf::Hkdf::new(salt, ikm) }
+ Hkdf(hkdf::Hkdf::new(salt, ikm))
}
fn expand_multi_info(
@@ -60,11 +57,11 @@ where
info_components: &[&[u8]],
okm: &mut [u8],
) -> Result<(), InvalidLength> {
- self.hkdf_impl.expand_multi_info(info_components, okm).map_err(|_| InvalidLength)
+ self.0.expand_multi_info(info_components, okm).map_err(|_| InvalidLength)
}
fn expand(&self, info: &[u8], okm: &mut [u8]) -> Result<(), InvalidLength> {
- self.hkdf_impl.expand(info, okm).map_err(|_| InvalidLength)
+ self.0.expand(info, okm).map_err(|_| InvalidLength)
}
}
diff --git a/nearby/crypto/crypto_provider_rustcrypto/src/hmac_rc.rs b/nearby/crypto/crypto_provider_rustcrypto/src/hmac_cp.rs
index dfda208..d8cef33 100644
--- a/nearby/crypto/crypto_provider_rustcrypto/src/hmac_rc.rs
+++ b/nearby/crypto/crypto_provider_rustcrypto/src/hmac_cp.rs
@@ -23,7 +23,7 @@ use hmac::digest::{HashMarker, OutputSizeUser};
use hmac::Mac;
/// RustCrypto based hmac implementation
-pub struct Hmac<D>
+pub struct Hmac<D>(hmac::Hmac<D>)
where
D: OutputSizeUser,
D: CoreProxy,
@@ -34,43 +34,38 @@ where
+ Default
+ Clone,
<D::Core as BlockSizeUser>::BlockSize: IsLess<U256>,
- Le<<D::Core as BlockSizeUser>::BlockSize, U256>: NonZero,
-{
- hmac_impl: hmac::Hmac<D>,
-}
+ Le<<D::Core as BlockSizeUser>::BlockSize, U256>: NonZero;
impl crypto_provider::hmac::Hmac<32> for Hmac<sha2::Sha256> {
#[allow(clippy::expect_used)]
fn new_from_key(key: [u8; 32]) -> Self {
hmac::Hmac::new_from_slice(&key)
- .map(|hmac| Self { hmac_impl: hmac })
+ .map(Self)
.expect("length will always be valid because input key is of fixed size")
}
fn new_from_slice(key: &[u8]) -> Result<Self, InvalidLength> {
- hmac::Hmac::new_from_slice(key)
- .map(|hmac| Self { hmac_impl: hmac })
- .map_err(|_| InvalidLength)
+ hmac::Hmac::new_from_slice(key).map(Self).map_err(|_| InvalidLength)
}
fn update(&mut self, data: &[u8]) {
- self.hmac_impl.update(data);
+ self.0.update(data);
}
fn finalize(self) -> [u8; 32] {
- self.hmac_impl.finalize().into_bytes().into()
+ self.0.finalize().into_bytes().into()
}
fn verify_slice(self, tag: &[u8]) -> Result<(), MacError> {
- self.hmac_impl.verify_slice(tag).map_err(|_| MacError)
+ self.0.verify_slice(tag).map_err(|_| MacError)
}
fn verify(self, tag: [u8; 32]) -> Result<(), MacError> {
- self.hmac_impl.verify(&tag.into()).map_err(|_| MacError)
+ self.0.verify(&tag.into()).map_err(|_| MacError)
}
fn verify_truncated_left(self, tag: &[u8]) -> Result<(), MacError> {
- self.hmac_impl.verify_truncated_left(tag).map_err(|_| MacError)
+ self.0.verify_truncated_left(tag).map_err(|_| MacError)
}
}
@@ -78,34 +73,32 @@ impl crypto_provider::hmac::Hmac<64> for Hmac<sha2::Sha512> {
#[allow(clippy::expect_used)]
fn new_from_key(key: [u8; 64]) -> Self {
hmac::Hmac::new_from_slice(&key)
- .map(|hmac| Self { hmac_impl: hmac })
+ .map(Self)
.expect("length will always be valid because input key is of fixed size")
}
fn new_from_slice(key: &[u8]) -> Result<Self, InvalidLength> {
- hmac::Hmac::new_from_slice(key)
- .map(|hmac| Self { hmac_impl: hmac })
- .map_err(|_| InvalidLength)
+ hmac::Hmac::new_from_slice(key).map(Self).map_err(|_| InvalidLength)
}
fn update(&mut self, data: &[u8]) {
- self.hmac_impl.update(data);
+ self.0.update(data);
}
fn finalize(self) -> [u8; 64] {
- self.hmac_impl.finalize().into_bytes().into()
+ self.0.finalize().into_bytes().into()
}
fn verify_slice(self, tag: &[u8]) -> Result<(), MacError> {
- self.hmac_impl.verify_slice(tag).map_err(|_| MacError)
+ self.0.verify_slice(tag).map_err(|_| MacError)
}
fn verify(self, tag: [u8; 64]) -> Result<(), MacError> {
- self.hmac_impl.verify(&tag.into()).map_err(|_| MacError)
+ self.0.verify(&tag.into()).map_err(|_| MacError)
}
fn verify_truncated_left(self, tag: &[u8]) -> Result<(), MacError> {
- self.hmac_impl.verify_truncated_left(tag).map_err(|_| MacError)
+ self.0.verify_truncated_left(tag).map_err(|_| MacError)
}
}
diff --git a/nearby/crypto/crypto_provider_rustcrypto/src/lib.rs b/nearby/crypto/crypto_provider_rustcrypto/src/lib.rs
index e95712c..2985dec 100644
--- a/nearby/crypto/crypto_provider_rustcrypto/src/lib.rs
+++ b/nearby/crypto/crypto_provider_rustcrypto/src/lib.rs
@@ -11,20 +11,14 @@
// 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
use core::{fmt::Debug, marker::PhantomData};
+pub use aes;
use cfg_if::cfg_if;
pub use hkdf;
pub use hmac;
@@ -35,17 +29,17 @@ use subtle::ConstantTimeEq;
/// Contains the RustCrypto backed impls for AES-GCM-SIV operations
mod aead;
/// Contains the RustCrypto backed AES impl for CryptoProvider
-pub mod aes;
+pub mod aes_cp;
/// Contains the RustCrypto backed impl for ed25519 key generation, signing, and verification
mod ed25519;
/// Contains the RustCrypto backed hkdf impl for CryptoProvider
-mod hkdf_rc;
+mod hkdf_cp;
/// Contains the RustCrypto backed hmac impl for CryptoProvider
-mod hmac_rc;
+mod hmac_cp;
/// Contains the RustCrypto backed P256 impl for CryptoProvider
mod p256;
/// Contains the RustCrypto backed SHA2 impl for CryptoProvider
-mod sha2_rc;
+mod sha2_cp;
/// Contains the RustCrypto backed X25519 impl for CryptoProvider
mod x25519;
@@ -54,9 +48,11 @@ cfg_if! {
/// Providing a type alias for compatibility with existing usage of RustCrypto
/// by default we use StdRng for the underlying csprng
pub type RustCrypto = RustCryptoImpl<rand::rngs::StdRng>;
- } else {
+ } else if #[cfg(feature = "rand_chacha")] {
/// A no_std compatible implementation of CryptoProvider backed by RustCrypto crates
pub type RustCrypto = RustCryptoImpl<rand_chacha::ChaCha20Rng>;
+ } else {
+ compile_error!("Must specify either --features std or --features rand_chacha");
}
}
@@ -76,23 +72,24 @@ impl<R: CryptoRng + SeedableRng + RngCore> RustCryptoImpl<R> {
impl<R: CryptoRng + SeedableRng + RngCore + Eq + PartialEq + Debug + Clone + Send>
crypto_provider::CryptoProvider for RustCryptoImpl<R>
{
- type HkdfSha256 = hkdf_rc::Hkdf<sha2::Sha256>;
- type HmacSha256 = hmac_rc::Hmac<sha2::Sha256>;
- type HkdfSha512 = hkdf_rc::Hkdf<sha2::Sha512>;
- type HmacSha512 = hmac_rc::Hmac<sha2::Sha512>;
- #[cfg(feature = "alloc")]
- type AesCbcPkcs7Padded = aes::cbc::AesCbcPkcs7Padded;
+ type HkdfSha256 = hkdf_cp::Hkdf<sha2::Sha256>;
+ type HmacSha256 = hmac_cp::Hmac<sha2::Sha256>;
+ type HkdfSha512 = hkdf_cp::Hkdf<sha2::Sha512>;
+ type HmacSha512 = hmac_cp::Hmac<sha2::Sha512>;
+ type AesCbcPkcs7Padded = aes_cp::cbc::AesCbcPkcs7Padded;
type X25519 = x25519::X25519Ecdh<R>;
type P256 = p256::P256Ecdh<R>;
- type Sha256 = sha2_rc::RustCryptoSha256;
- type Sha512 = sha2_rc::RustCryptoSha512;
- type Aes128 = aes::Aes128;
- type Aes256 = aes::Aes256;
- type AesCtr128 = aes::ctr::AesCtr128;
- type AesCtr256 = aes::ctr::AesCtr256;
+ type Sha256 = sha2_cp::RustCryptoSha256;
+ type Sha512 = sha2_cp::RustCryptoSha512;
+ type Aes128 = aes_cp::Aes128;
+ type Aes256 = aes_cp::Aes256;
+ type AesCtr128 = aes_cp::ctr::AesCtr128;
+ type AesCtr256 = aes_cp::ctr::AesCtr256;
type Ed25519 = ed25519::Ed25519;
- type Aes128GcmSiv = aead::aes_gcm_siv::AesGcmSiv128;
- type Aes256GcmSiv = aead::aes_gcm_siv::AesGcmSiv256;
+ type Aes128GcmSiv = aead::aes_gcm_siv::AesGcmSiv<aes::Aes128>;
+ type Aes256GcmSiv = aead::aes_gcm_siv::AesGcmSiv<aes::Aes256>;
+ type Aes128Gcm = aead::aes_gcm::AesGcm<aes::Aes128>;
+ type Aes256Gcm = aead::aes_gcm::AesGcm<aes::Aes256>;
type CryptoRng = RcRng<R>;
fn constant_time_eq(a: &[u8], b: &[u8]) -> bool {
@@ -124,6 +121,7 @@ mod testing;
mod tests {
use core::marker::PhantomData;
+ use crypto_provider_test::prelude::*;
use crypto_provider_test::sha2::*;
use crate::RustCrypto;
diff --git a/nearby/crypto/crypto_provider_rustcrypto/src/p256.rs b/nearby/crypto/crypto_provider_rustcrypto/src/p256.rs
index 539ccc0..98a6cd4 100644
--- a/nearby/crypto/crypto_provider_rustcrypto/src/p256.rs
+++ b/nearby/crypto/crypto_provider_rustcrypto/src/p256.rs
@@ -12,14 +12,12 @@
// See the License for the specific language governing permissions and
// limitations under the License.
-extern crate alloc;
-
use crate::RcRng;
-use alloc::vec::Vec;
use core::marker::PhantomData;
use crypto_provider::{
elliptic_curve::{EcdhProvider, EphemeralSecret},
- p256::P256,
+ p256::{PointCompression, P256},
+ tinyvec::ArrayVec,
};
use p256::{
elliptic_curve,
@@ -52,8 +50,12 @@ impl crypto_provider::p256::P256PublicKey for P256PublicKey {
p256::PublicKey::from_sec1_bytes(bytes).map(Self)
}
- fn to_sec1_bytes(&self) -> Vec<u8> {
- self.0.to_encoded_point(true).as_bytes().to_vec()
+ fn to_sec1_bytes(&self, point_compression: PointCompression) -> ArrayVec<[u8; 65]> {
+ let mut bytes = ArrayVec::<[u8; 65]>::new();
+ bytes.extend_from_slice(
+ self.0.to_encoded_point(point_compression == PointCompression::Compressed).as_bytes(),
+ );
+ bytes
}
#[allow(clippy::expect_used)]
@@ -87,6 +89,7 @@ impl<R: CryptoRng + SeedableRng + RngCore + Send> EphemeralSecret<P256> for P256
type Impl = P256Ecdh<R>;
type Error = sec1::Error;
type Rng = RcRng<R>;
+ type EncodedPublicKey = ArrayVec<[u8; 65]>;
fn generate_random(rng: &mut Self::Rng) -> Self {
Self {
@@ -95,8 +98,10 @@ impl<R: CryptoRng + SeedableRng + RngCore + Send> EphemeralSecret<P256> for P256
}
}
- fn public_key_bytes(&self) -> Vec<u8> {
- self.secret.public_key().to_encoded_point(false).as_bytes().into()
+ fn public_key_bytes(&self) -> Self::EncodedPublicKey {
+ let mut bytes = Self::EncodedPublicKey::new();
+ bytes.extend_from_slice(self.secret.public_key().to_encoded_point(false).as_bytes());
+ bytes
}
fn diffie_hellman(
@@ -136,7 +141,7 @@ mod tests {
use rand::rngs::StdRng;
#[apply(p256_test_cases)]
- fn p256_tests(testcase: CryptoProviderTestCase<P256Ecdh<StdRng>>) {
+ fn p256_tests(testcase: CryptoProviderTestCase<P256Ecdh<StdRng>>, _name: &str) {
testcase(PhantomData::<P256Ecdh<StdRng>>)
}
}
diff --git a/nearby/crypto/crypto_provider_rustcrypto/src/sha2_rc.rs b/nearby/crypto/crypto_provider_rustcrypto/src/sha2_cp.rs
index 977eb83..977eb83 100644
--- a/nearby/crypto/crypto_provider_rustcrypto/src/sha2_rc.rs
+++ b/nearby/crypto/crypto_provider_rustcrypto/src/sha2_cp.rs
diff --git a/nearby/crypto/crypto_provider_rustcrypto/src/x25519.rs b/nearby/crypto/crypto_provider_rustcrypto/src/x25519.rs
index 445e858..ad67777 100644
--- a/nearby/crypto/crypto_provider_rustcrypto/src/x25519.rs
+++ b/nearby/crypto/crypto_provider_rustcrypto/src/x25519.rs
@@ -12,10 +12,7 @@
// See the License for the specific language governing permissions and
// limitations under the License.
-extern crate alloc;
-
use crate::RcRng;
-use alloc::vec::Vec;
use core::marker::PhantomData;
use crypto_provider::elliptic_curve::{EcdhProvider, EphemeralSecret, PublicKey};
use crypto_provider::x25519::X25519;
@@ -45,6 +42,7 @@ impl<R: CryptoRng + RngCore + SeedableRng + Send> EphemeralSecret<X25519>
type Impl = X25519Ecdh<R>;
type Error = Error;
type Rng = RcRng<R>;
+ type EncodedPublicKey = [u8; 32];
fn generate_random(rng: &mut Self::Rng) -> Self {
Self {
@@ -53,9 +51,9 @@ impl<R: CryptoRng + RngCore + SeedableRng + Send> EphemeralSecret<X25519>
}
}
- fn public_key_bytes(&self) -> Vec<u8> {
+ fn public_key_bytes(&self) -> Self::EncodedPublicKey {
let pubkey: x25519_dalek::PublicKey = (&self.secret).into();
- pubkey.to_bytes().into()
+ pubkey.to_bytes()
}
fn diffie_hellman(
@@ -90,14 +88,15 @@ pub struct X25519PublicKey(x25519_dalek::PublicKey);
impl PublicKey<X25519> for X25519PublicKey {
type Error = Error;
+ type EncodedPublicKey = [u8; 32];
fn from_bytes(bytes: &[u8]) -> Result<Self, Self::Error> {
let byte_sized: [u8; 32] = bytes.try_into().map_err(|_| Error::WrongSize)?;
Ok(Self(byte_sized.into()))
}
- fn to_bytes(&self) -> Vec<u8> {
- self.0.as_bytes().to_vec()
+ fn to_bytes(&self) -> Self::EncodedPublicKey {
+ self.0.to_bytes()
}
}
diff --git a/nearby/crypto/crypto_provider_stubs/Cargo.toml b/nearby/crypto/crypto_provider_stubs/Cargo.toml
index d7c4d45..4e8bdec 100644
--- a/nearby/crypto/crypto_provider_stubs/Cargo.toml
+++ b/nearby/crypto/crypto_provider_stubs/Cargo.toml
@@ -5,4 +5,4 @@ edition.workspace = true
publish.workspace = true
[dependencies]
-crypto_provider = {workspace = true, features = ["std", "alloc"] } \ No newline at end of file
+crypto_provider = {workspace = true, features = ["std", "alloc"] }
diff --git a/nearby/crypto/crypto_provider_stubs/src/lib.rs b/nearby/crypto/crypto_provider_stubs/src/lib.rs
index 5d54b72..32c9381 100644
--- a/nearby/crypto/crypto_provider_stubs/src/lib.rs
+++ b/nearby/crypto/crypto_provider_stubs/src/lib.rs
@@ -20,21 +20,23 @@
use std::fmt::Debug;
-use crypto_provider::ed25519::{RawPrivateKey, RawPublicKey, RawSignature};
+use crypto_provider::aead::AeadInit;
use crypto_provider::{
- aead::aes_gcm_siv::AesGcmSiv,
- aead::{Aead, AeadError},
+ aead::{Aead, AeadError, AesGcm, AesGcmSiv},
aes::{
- cbc::{AesCbcIv, AesCbcPkcs7Padded, DecryptionError},
+ cbc::{AesCbcIv, AesCbcPkcs7Padded, DecryptionError, EncryptionError},
ctr::{AesCtr, NonceAndCounter},
Aes, Aes128Key, Aes256Key, AesBlock, AesCipher, AesDecryptCipher, AesEncryptCipher,
},
- ed25519,
- ed25519::{Ed25519Provider, InvalidBytes, KeyPair, Signature, SignatureError},
+ ed25519::{
+ self, Ed25519Provider, InvalidBytes, KeyPair, RawPrivateKey, RawPrivateKeyPermit,
+ RawPublicKey, RawSignature, Signature, SignatureError,
+ },
elliptic_curve::{EcdhProvider, EphemeralSecret, PublicKey},
hkdf::{Hkdf, InvalidLength},
hmac::{Hmac, MacError},
- p256::{P256PublicKey, P256},
+ p256::{P256PublicKey, PointCompression, P256},
+ tinyvec::{ArrayVec, SliceVec},
x25519::X25519,
};
@@ -58,6 +60,8 @@ impl crypto_provider::CryptoProvider for CryptoProviderStubs {
type Ed25519 = Ed25519Stubs;
type Aes128GcmSiv = Aes128Stubs;
type Aes256GcmSiv = Aes256Stubs;
+ type Aes128Gcm = Aes128Stubs;
+ type Aes256Gcm = Aes256Stubs;
type CryptoRng = ();
fn constant_time_eq(_a: &[u8], _b: &[u8]) -> bool {
@@ -171,6 +175,14 @@ impl AesCbcPkcs7Padded for AesCbcPkcs7PaddedStubs {
unimplemented!()
}
+ fn encrypt_in_place(
+ key: &Aes256Key,
+ iv: &AesCbcIv,
+ message: &mut SliceVec<u8>,
+ ) -> Result<(), EncryptionError> {
+ unimplemented!()
+ }
+
fn decrypt(
_key: &Aes256Key,
_iv: &AesCbcIv,
@@ -178,6 +190,14 @@ impl AesCbcPkcs7Padded for AesCbcPkcs7PaddedStubs {
) -> Result<Vec<u8>, DecryptionError> {
unimplemented!()
}
+
+ fn decrypt_in_place(
+ key: &Aes256Key,
+ iv: &AesCbcIv,
+ ciphertext: &mut SliceVec<u8>,
+ ) -> Result<(), DecryptionError> {
+ unimplemented!()
+ }
}
pub struct X25519Stubs;
@@ -194,12 +214,13 @@ impl EphemeralSecret<X25519> for EphSecretStubs {
type Impl = X25519Stubs;
type Error = ();
type Rng = ();
+ type EncodedPublicKey = [u8; 32];
fn generate_random(_rng: &mut Self::Rng) -> Self {
unimplemented!()
}
- fn public_key_bytes(&self) -> Vec<u8> {
+ fn public_key_bytes(&self) -> Self::EncodedPublicKey {
unimplemented!()
}
@@ -215,12 +236,13 @@ impl EphemeralSecret<P256> for EphSecretStubs {
type Impl = P256Stubs;
type Error = ();
type Rng = ();
+ type EncodedPublicKey = ArrayVec<[u8; 65]>;
fn generate_random(_rng: &mut Self::Rng) -> Self {
unimplemented!()
}
- fn public_key_bytes(&self) -> Vec<u8> {
+ fn public_key_bytes(&self) -> Self::EncodedPublicKey {
unimplemented!()
}
@@ -237,12 +259,13 @@ pub struct EcdhPubKey;
impl PublicKey<X25519> for EcdhPubKey {
type Error = ();
+ type EncodedPublicKey = [u8; 32];
fn from_bytes(_bytes: &[u8]) -> Result<Self, Self::Error> {
unimplemented!()
}
- fn to_bytes(&self) -> Vec<u8> {
+ fn to_bytes(&self) -> Self::EncodedPublicKey {
unimplemented!()
}
}
@@ -257,7 +280,7 @@ impl P256PublicKey for PublicKeyStubs {
unimplemented!()
}
- fn to_sec1_bytes(&self) -> Vec<u8> {
+ fn to_sec1_bytes(&self, _point_compression: PointCompression) -> ArrayVec<[u8; 65]> {
unimplemented!()
}
@@ -294,6 +317,12 @@ impl crypto_provider::sha2::Sha512 for Sha2Stubs {
pub struct Aes128Stubs;
+impl AeadInit<Aes128Key> for Aes128Stubs {
+ fn new(key: &Aes128Key) -> Self {
+ unimplemented!()
+ }
+}
+
impl AesCipher for Aes128Stubs {
type Key = Aes128Key;
@@ -321,11 +350,7 @@ impl AesCtr for Aes128Stubs {
unimplemented!()
}
- fn encrypt(&mut self, _data: &mut [u8]) {
- unimplemented!()
- }
-
- fn decrypt(&mut self, _data: &mut [u8]) {
+ fn apply_keystream(&mut self, data: &mut [u8]) {
unimplemented!()
}
}
@@ -333,25 +358,48 @@ impl AesCtr for Aes128Stubs {
impl Aead for Aes128Stubs {
const TAG_SIZE: usize = 16;
type Nonce = [u8; 12];
- type Key = Aes128Key;
+ type Tag = [u8; 16];
- fn new(key: &Self::Key) -> Self {
+ fn encrypt(&self, msg: &[u8], aad: &[u8], nonce: &[u8; 12]) -> Result<Vec<u8>, AeadError> {
unimplemented!()
}
- fn encrypt(&self, msg: &mut Vec<u8>, aad: &[u8], nonce: &[u8; 12]) -> Result<(), AeadError> {
+ fn encrypt_detached(
+ &self,
+ msg: &mut [u8],
+ aad: &[u8],
+ nonce: &Self::Nonce,
+ ) -> Result<Self::Tag, AeadError> {
unimplemented!()
}
- fn decrypt(&self, msg: &mut Vec<u8>, aad: &[u8], nonce: &[u8; 12]) -> Result<(), AeadError> {
+ fn decrypt(&self, msg: &[u8], aad: &[u8], nonce: &[u8; 12]) -> Result<Vec<u8>, AeadError> {
+ unimplemented!()
+ }
+
+ fn decrypt_detached(
+ &self,
+ msg: &mut [u8],
+ aad: &[u8],
+ nonce: &Self::Nonce,
+ tag: &Self::Tag,
+ ) -> Result<(), AeadError> {
unimplemented!()
}
}
impl AesGcmSiv for Aes128Stubs {}
+impl AesGcm for Aes128Stubs {}
+
pub struct Aes256Stubs;
+impl AeadInit<Aes256Key> for Aes256Stubs {
+ fn new(key: &Aes256Key) -> Self {
+ unimplemented!()
+ }
+}
+
impl AesCipher for Aes256Stubs {
type Key = Aes256Key;
@@ -379,11 +427,7 @@ impl AesCtr for Aes256Stubs {
unimplemented!()
}
- fn encrypt(&mut self, _data: &mut [u8]) {
- unimplemented!()
- }
-
- fn decrypt(&mut self, _data: &mut [u8]) {
+ fn apply_keystream(&mut self, data: &mut [u8]) {
unimplemented!()
}
}
@@ -391,23 +435,40 @@ impl AesCtr for Aes256Stubs {
impl Aead for Aes256Stubs {
const TAG_SIZE: usize = 16;
type Nonce = [u8; 12];
- type Key = Aes256Key;
+ type Tag = [u8; 16];
- fn new(key: &Self::Key) -> Self {
+ fn encrypt(&self, msg: &[u8], aad: &[u8], nonce: &[u8; 12]) -> Result<Vec<u8>, AeadError> {
unimplemented!()
}
- fn encrypt(&self, msg: &mut Vec<u8>, aad: &[u8], nonce: &[u8; 12]) -> Result<(), AeadError> {
+ fn encrypt_detached(
+ &self,
+ msg: &mut [u8],
+ aad: &[u8],
+ nonce: &Self::Nonce,
+ ) -> Result<Self::Tag, AeadError> {
unimplemented!()
}
- fn decrypt(&self, msg: &mut Vec<u8>, aad: &[u8], nonce: &[u8; 12]) -> Result<(), AeadError> {
+ fn decrypt(&self, msg: &[u8], aad: &[u8], nonce: &[u8; 12]) -> Result<Vec<u8>, AeadError> {
+ unimplemented!()
+ }
+
+ fn decrypt_detached(
+ &self,
+ msg: &mut [u8],
+ aad: &[u8],
+ nonce: &Self::Nonce,
+ tag: &Self::Tag,
+ ) -> Result<(), AeadError> {
unimplemented!()
}
}
impl AesGcmSiv for Aes256Stubs {}
+impl AesGcm for Aes256Stubs {}
+
pub struct Ed25519Stubs;
impl Ed25519Provider for Ed25519Stubs {
@@ -457,11 +518,11 @@ impl KeyPair for KeyPairStubs {
type PublicKey = PublicKeyStubs;
type Signature = SignatureStubs;
- fn private_key(&self) -> RawPrivateKey {
+ fn raw_private_key(&self, _permit: &RawPrivateKeyPermit) -> RawPrivateKey {
unimplemented!()
}
- fn from_private_key(_bytes: &RawPrivateKey) -> Self
+ fn from_raw_private_key(_bytes: &RawPrivateKey, _permit: &RawPrivateKeyPermit) -> Self
where
Self: Sized,
{
diff --git a/nearby/crypto/crypto_provider_test/Cargo.toml b/nearby/crypto/crypto_provider_test/Cargo.toml
index a4d92ec..4064330 100644
--- a/nearby/crypto/crypto_provider_test/Cargo.toml
+++ b/nearby/crypto/crypto_provider_test/Cargo.toml
@@ -5,7 +5,7 @@ edition.workspace = true
publish.workspace = true
[dependencies]
-crypto_provider = { workspace = true, features = ["test_vectors"] }
+crypto_provider = { workspace = true, features = ["raw_private_key_permit", "test_vectors", "alloc"] }
rand_ext.workspace = true
test_helper.workspace = true
@@ -14,4 +14,4 @@ rand.workspace = true
rstest.workspace = true
rstest_reuse.workspace = true
wycheproof.workspace = true
-hex.workspace = true \ No newline at end of file
+hex.workspace = true
diff --git a/nearby/crypto/crypto_provider_test/fuzz/Cargo.lock b/nearby/crypto/crypto_provider_test/fuzz/Cargo.lock
index 9e1d140..49c9513 100644
--- a/nearby/crypto/crypto_provider_test/fuzz/Cargo.lock
+++ b/nearby/crypto/crypto_provider_test/fuzz/Cargo.lock
@@ -25,6 +25,20 @@ dependencies = [
]
[[package]]
+name = "aes-gcm"
+version = "0.10.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "831010a0f742e1209b3bcea8fab6a8e149051ba6099432c8cb2cc117dec3ead1"
+dependencies = [
+ "aead",
+ "aes",
+ "cipher",
+ "ctr",
+ "ghash",
+ "subtle",
+]
+
+[[package]]
name = "aes-gcm-siv"
version = "0.11.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -47,9 +61,9 @@ checksum = "250f629c0161ad8107cf89319e990051fae62832fd343083bea452d93e2205fd"
[[package]]
name = "arbitrary"
-version = "1.3.0"
+version = "1.3.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "e2d098ff73c1ca148721f37baad5ea6a465a13f9573aba8641fbbbae8164a54e"
+checksum = "7d5a26814d8dcb93b0e5a0ff3c6d80a8843bafb21b39e8e18a6f05471870e110"
dependencies = [
"derive_arbitrary",
]
@@ -62,9 +76,9 @@ checksum = "4c7f02d4ea65f2c1853089ffd8d2787bdbc63de2f0d29dedbcf8ccdfa0ccd4cf"
[[package]]
name = "bitflags"
-version = "1.3.2"
+version = "2.4.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a"
+checksum = "327762f6e5a765692301e5bb513e0d9fef63be86bbc14528052b1cd3e6f03e07"
[[package]]
name = "block-buffer"
@@ -86,9 +100,9 @@ dependencies = [
[[package]]
name = "bytes"
-version = "1.4.0"
+version = "1.5.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "89b2fd2a0dcf38d7971e2194b6b6eebab45ae01067456a7fd93d5547a61b70be"
+checksum = "a2bd12c1caf447e69cd4528f47f94d203fd2582878ecb9e9465484c4148a8223"
[[package]]
name = "cbc"
@@ -101,11 +115,12 @@ dependencies = [
[[package]]
name = "cc"
-version = "1.0.79"
+version = "1.0.83"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "50d30906286121d95be3d479533b458f87493b30a4b5f79a607db8f5d11aa91f"
+checksum = "f1174fb0b6ec23863f8b971027804a42614e347eafb0a95bf0b12cdae21fc4d0"
dependencies = [
"jobserver",
+ "libc",
]
[[package]]
@@ -126,24 +141,24 @@ dependencies = [
[[package]]
name = "const-oid"
-version = "0.9.4"
+version = "0.9.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "795bc6e66a8e340f075fcf6227e417a2dc976b92b91f3cdc778bb858778b6747"
+checksum = "c2459377285ad874054d797f3ccebf984978aa39129f6eafde5cdc8315b612f8"
[[package]]
name = "cpufeatures"
-version = "0.2.9"
+version = "0.2.12"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "a17b76ff3a4162b0b27f354a0c87015ddad39d35f9c0c36607a3bdd175dde1f1"
+checksum = "53fe5e26ff1b7aef8bca9c6080520cfb8d9333c7568e1829cef191a9723e5504"
dependencies = [
"libc",
]
[[package]]
name = "crypto-bigint"
-version = "0.5.2"
+version = "0.5.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "cf4c2f4e1afd912bc40bfd6fed5d9dc1f288e0ba01bfcc835cc5bc3eb13efe15"
+checksum = "0dc92fb57ca44df6db8059111ab3af99a63d5d0f8375d9972e319a379c6bab76"
dependencies = [
"generic-array",
"rand_core",
@@ -165,6 +180,9 @@ dependencies = [
[[package]]
name = "crypto_provider"
version = "0.1.0"
+dependencies = [
+ "tinyvec",
+]
[[package]]
name = "crypto_provider_default"
@@ -193,6 +211,7 @@ version = "0.1.0"
dependencies = [
"aead",
"aes",
+ "aes-gcm",
"aes-gcm-siv",
"cbc",
"cfg-if",
@@ -239,9 +258,9 @@ dependencies = [
[[package]]
name = "curve25519-dalek"
-version = "4.0.0-rc.3"
+version = "4.1.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "436ace70fc06e06f7f689d2624dc4e2f0ea666efb5aa704215f7249ae6e047a7"
+checksum = "e89b8c6a2e4b1f45971ad09761aafb85514a84744b67a95e32c3cc1352d1f65c"
dependencies = [
"cfg-if",
"cpufeatures",
@@ -255,20 +274,20 @@ dependencies = [
[[package]]
name = "curve25519-dalek-derive"
-version = "0.1.0"
+version = "0.1.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "83fdaf97f4804dcebfa5862639bc9ce4121e82140bec2a987ac5140294865b5b"
+checksum = "f46882e17999c6cc590af592290432be3bce0428cb0d5f8b6715e4dc7b383eb3"
dependencies = [
"proc-macro2",
"quote",
- "syn 2.0.26",
+ "syn 2.0.48",
]
[[package]]
name = "der"
-version = "0.7.7"
+version = "0.7.8"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "0c7ed52955ce76b1554f509074bb357d3fb8ac9b51288a65a3fd480d1dfba946"
+checksum = "fffa369a668c8af7dbf8b5e56c9f744fbd399949ed171606040001947de40b1c"
dependencies = [
"const-oid",
"zeroize",
@@ -276,13 +295,13 @@ dependencies = [
[[package]]
name = "derive_arbitrary"
-version = "1.3.1"
+version = "1.3.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "53e0efad4403bfc52dc201159c4b842a246a14b98c64b55dfd0f2d89729dfeb8"
+checksum = "67e77553c4162a157adbf834ebae5b415acbecbeafc7a74b0e886657506a7611"
dependencies = [
"proc-macro2",
"quote",
- "syn 2.0.26",
+ "syn 2.0.48",
]
[[package]]
@@ -298,30 +317,31 @@ dependencies = [
[[package]]
name = "ed25519"
-version = "2.2.1"
+version = "2.2.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "5fb04eee5d9d907f29e80ee6b0e78f7e2c82342c63e3580d8c4f69d9d5aad963"
+checksum = "115531babc129696a58c64a4fef0a8bf9e9698629fb97e9e40767d235cfbcd53"
dependencies = [
"signature",
]
[[package]]
name = "ed25519-dalek"
-version = "2.0.0-rc.3"
+version = "2.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "faa8e9049d5d72bfc12acbc05914731b5322f79b5e2f195e9f2d705fca22ab4c"
+checksum = "1f628eaec48bfd21b865dc2950cfa014450c01d2fa2b69a86c2fd5844ec523c0"
dependencies = [
"curve25519-dalek",
"ed25519",
"rand_core",
"sha2",
+ "subtle",
]
[[package]]
name = "elliptic-curve"
-version = "0.13.5"
+version = "0.13.8"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "968405c8fdc9b3bf4df0a6638858cc0b52462836ab6b1c87377785dd09cf1c0b"
+checksum = "b5e6043086bf7973472e0c7dff2142ea0b680d30e18d9cc40f267efbf222bd47"
dependencies = [
"base16ct",
"crypto-bigint",
@@ -348,9 +368,9 @@ dependencies = [
[[package]]
name = "fiat-crypto"
-version = "0.1.20"
+version = "0.2.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "e825f6987101665dea6ec934c09ec6d721de7bc1bf92248e1d5810c8cd636b77"
+checksum = "27573eac26f4dd11e2b1916c3fe1baa56407c83c71a773a8ba17ec0bca03b6b7"
[[package]]
name = "foreign-types"
@@ -380,9 +400,9 @@ dependencies = [
[[package]]
name = "getrandom"
-version = "0.2.10"
+version = "0.2.12"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "be4136b2a15dd319360be1c07d9933517ccf0be8f16bf62a3bee4f0d618df427"
+checksum = "190092ea657667030ac6a35e305e62fc4dd69fd98ac98631e5d3a2b1575a12b5"
dependencies = [
"cfg-if",
"libc",
@@ -390,6 +410,16 @@ dependencies = [
]
[[package]]
+name = "ghash"
+version = "0.5.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d930750de5717d2dd0b8c0d42c076c0e884c81a73e6cab859bbd2339c71e3e40"
+dependencies = [
+ "opaque-debug",
+ "polyval",
+]
+
+[[package]]
name = "group"
version = "0.13.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -408,9 +438,9 @@ checksum = "95505c38b4572b2d910cecb0281560f54b440a19336cbbcb27bf6ce6adc6f5a8"
[[package]]
name = "hkdf"
-version = "0.12.3"
+version = "0.12.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "791a029f6b9fc27657f6f188ec6e5e43f6911f6f878e0dc5501396e09809d437"
+checksum = "7b5f8eb2ad728638ea2c7d47a21db23b7b58a72ed6a38256b8a1849f15fbbdf7"
dependencies = [
"hmac",
]
@@ -436,24 +466,24 @@ dependencies = [
[[package]]
name = "jobserver"
-version = "0.1.26"
+version = "0.1.27"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "936cfd212a0155903bcbc060e316fb6cc7cbf2e1907329391ebadc1fe0ce77c2"
+checksum = "8c37f63953c4c63420ed5fd3d6d398c719489b9f872b9fa683262f8edd363c7d"
dependencies = [
"libc",
]
[[package]]
name = "libc"
-version = "0.2.147"
+version = "0.2.152"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "b4668fb0ea861c1df094127ac5f1da3409a82116a4ba74fca2e58ef927159bb3"
+checksum = "13e3bf6590cbc649f4d1a3eefc9d5d6eb746f5200ffb04e5e142700b8faa56e7"
[[package]]
name = "libfuzzer-sys"
-version = "0.4.6"
+version = "0.4.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "beb09950ae85a0a94b27676cccf37da5ff13f27076aa1adbc6545dd0d0e1bd4e"
+checksum = "a96cfd5557eb82f2b83fed4955246c988d331975a002961b07c81584d107e7f7"
dependencies = [
"arbitrary",
"cc",
@@ -462,9 +492,9 @@ dependencies = [
[[package]]
name = "once_cell"
-version = "1.18.0"
+version = "1.19.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "dd8b5dd2ae5ed71462c540258bedcb51965123ad7e7ccf4b9a8cafaa4a63576d"
+checksum = "3fdb12b2476b595f9358c5161aa467c2438859caa136dec86c26fdd2efe17b92"
[[package]]
name = "opaque-debug"
@@ -474,9 +504,9 @@ checksum = "624a8340c38c1b80fd549087862da4ba43e08858af025b236e509b6649fc13d5"
[[package]]
name = "openssl"
-version = "0.10.55"
+version = "0.10.62"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "345df152bc43501c5eb9e4654ff05f794effb78d4efe3d53abc158baddc0703d"
+checksum = "8cde4d2d9200ad5909f8dac647e29482e07c3a35de8a13fce7c9c7747ad9f671"
dependencies = [
"bitflags",
"cfg-if",
@@ -495,14 +525,14 @@ checksum = "a948666b637a0f465e8564c73e89d4dde00d72d4d473cc972f390fc3dcee7d9c"
dependencies = [
"proc-macro2",
"quote",
- "syn 2.0.26",
+ "syn 2.0.48",
]
[[package]]
name = "openssl-sys"
-version = "0.9.90"
+version = "0.9.98"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "374533b0e45f3a7ced10fcaeccca020e66656bc03dac384f852e4e5a7a8104a6"
+checksum = "c1665caf8ab2dc9aef43d1c0023bd904633a6a05cb30b0ad59bec2ae986e57a7"
dependencies = [
"cc",
"libc",
@@ -512,9 +542,9 @@ dependencies = [
[[package]]
name = "ouroboros"
-version = "0.17.1"
+version = "0.17.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "b4361ddfdd108bddc19367ba805a3a43773d8bc3e407ac30e6c364cd264dd52e"
+checksum = "e2ba07320d39dfea882faa70554b4bd342a5f273ed59ba7c1c6b4c840492c954"
dependencies = [
"aliasable",
"ouroboros_macro",
@@ -523,15 +553,15 @@ dependencies = [
[[package]]
name = "ouroboros_macro"
-version = "0.17.1"
+version = "0.17.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "2b81e113ed913910f05ef45c9344f67588fe6395f92d906eedf9ee5d54279922"
+checksum = "ec4c6225c69b4ca778c0aea097321a64c421cf4577b331c61b229267edabb6f8"
dependencies = [
"heck",
"proc-macro-error",
"proc-macro2",
"quote",
- "syn 2.0.26",
+ "syn 2.0.48",
]
[[package]]
@@ -546,15 +576,15 @@ dependencies = [
[[package]]
name = "pkg-config"
-version = "0.3.27"
+version = "0.3.28"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "26072860ba924cbfa98ea39c8c19b4dd6a4a25423dbdf219c1eca91aa0cf6964"
+checksum = "69d3587f8a9e599cc7ec2c00e331f71c4e69a5f9a4b8a6efd5b07466b9736f9a"
[[package]]
name = "platforms"
-version = "3.0.2"
+version = "3.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "e3d7ddaed09e0eb771a79ab0fd64609ba0afb0a8366421957936ad14cbd13630"
+checksum = "626dec3cac7cc0e1577a2ec3fc496277ec2baa084bebad95bb6fdbfae235f84c"
[[package]]
name = "polyval"
@@ -576,9 +606,9 @@ checksum = "5b40af805b3121feab8a3c29f04d8ad262fa8e0561883e7653e024ae4479e6de"
[[package]]
name = "primeorder"
-version = "0.13.2"
+version = "0.13.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "3c2fcef82c0ec6eefcc179b978446c399b3cdf73c392c35604e399eee6df1ee3"
+checksum = "353e1ca18966c16d9deb1c69278edbc5f194139612772bd9537af60ac231e1e6"
dependencies = [
"elliptic-curve",
]
@@ -609,18 +639,18 @@ dependencies = [
[[package]]
name = "proc-macro2"
-version = "1.0.66"
+version = "1.0.76"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "18fb31db3f9bddb2ea821cde30a9f70117e3f119938b5ee630b7403aa6e2ead9"
+checksum = "95fc56cda0b5c3325f5fbbd7ff9fda9e02bb00bb3dac51252d2f1bfa1cb8cc8c"
dependencies = [
"unicode-ident",
]
[[package]]
name = "quote"
-version = "1.0.31"
+version = "1.0.35"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "5fe8a65d69dd0808184ebb5f836ab526bb259db23c657efa38711b1072ee47f0"
+checksum = "291ec9ab5efd934aaf503a6466c5d5251535d108ee747472c3977cc5acc868ef"
dependencies = [
"proc-macro2",
]
@@ -677,15 +707,15 @@ dependencies = [
[[package]]
name = "semver"
-version = "1.0.18"
+version = "1.0.21"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "b0293b4b29daaf487284529cc2f5675b8e57c61f70167ba415a463651fd6a918"
+checksum = "b97ed7a9823b74f99c7742f5336af7be5ecd3eeafcb1507d1fa93347b1d589b0"
[[package]]
name = "sha2"
-version = "0.10.7"
+version = "0.10.8"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "479fb9d862239e610720565ca91403019f2f00410f1864c5aa7479b950a76ed8"
+checksum = "793db75ad2bcafc3ffa7c68b215fee268f537982cd901d132f89c6343f3a3dc8"
dependencies = [
"cfg-if",
"cpufeatures",
@@ -694,9 +724,9 @@ dependencies = [
[[package]]
name = "signature"
-version = "2.1.0"
+version = "2.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "5e1788eed21689f9cf370582dfc467ef36ed9c707f073528ddafa8d83e3b8500"
+checksum = "77549399552de45a898a580c1b41d445bf730df867cc44e6c0233bbc4b8329de"
[[package]]
name = "static_assertions"
@@ -722,9 +752,9 @@ dependencies = [
[[package]]
name = "syn"
-version = "2.0.26"
+version = "2.0.48"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "45c3457aacde3c65315de5031ec191ce46604304d2446e803d71ade03308d970"
+checksum = "0f3531638e407dfc0814761abb7c00a5b54992b849452a0646b7f65c9f770f3f"
dependencies = [
"proc-macro2",
"quote",
@@ -732,16 +762,22 @@ dependencies = [
]
[[package]]
+name = "tinyvec"
+version = "1.6.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "87cc5ceb3875bb20c2890005a4e226a4651264a5c75edb2421b52861a0a0cb50"
+
+[[package]]
name = "typenum"
-version = "1.16.0"
+version = "1.17.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "497961ef93d974e23eb6f433eb5fe1b7930b659f06d12dec6fc44a8f554c0bba"
+checksum = "42ff0bf0c66b8238c6f3b578df37d0b7848e55df8577b3f74f92a69acceeb825"
[[package]]
name = "unicode-ident"
-version = "1.0.11"
+version = "1.0.12"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "301abaae475aa91687eb82514b328ab47a211a533026cb25fc3e519b86adfc3c"
+checksum = "3354b9ac3fae1ff6755cb6db53683adb661634f67557942dea4facebec0fee4b"
[[package]]
name = "universal-hash"
@@ -773,9 +809,9 @@ checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423"
[[package]]
name = "x25519-dalek"
-version = "2.0.0-rc.3"
+version = "2.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "ec7fae07da688e17059d5886712c933bb0520f15eff2e09cfa18e30968f4e63a"
+checksum = "fb66477291e7e8d2b0ff1bcb900bf29489a9692816d79874bea351e7a8b6de96"
dependencies = [
"curve25519-dalek",
"rand_core",
@@ -783,6 +819,6 @@ dependencies = [
[[package]]
name = "zeroize"
-version = "1.6.0"
+version = "1.7.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "2a0956f1ba7c7909bfb66c2e9e4124ab6f6482560f6628b5aaeba39207c9aad9"
+checksum = "525b4ec142c6b68a2d10f01f7bbf6755599ca3f81ea53b8431b7dd348f5fdb2d"
diff --git a/nearby/crypto/crypto_provider_test/src/aead/aes_gcm.rs b/nearby/crypto/crypto_provider_test/src/aead/aes_gcm.rs
new file mode 100644
index 0000000..88eaf32
--- /dev/null
+++ b/nearby/crypto/crypto_provider_test/src/aead/aes_gcm.rs
@@ -0,0 +1,316 @@
+// 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 alloc::vec::Vec;
+use core::marker;
+
+use hex_literal::hex;
+use rstest_reuse::template;
+
+pub use crate::prelude;
+use crypto_provider::aead::{AeadInit, AesGcm};
+
+/// Test AES-GCM-128 encryption
+pub fn aes_128_gcm_test_encrypt<A>(_marker: marker::PhantomData<A>)
+where
+ A: AesGcm<Tag = [u8; 16]> + AeadInit<crypto_provider::aes::Aes128Key>,
+{
+ // https://github.com/google/wycheproof/blob/master/testvectors/aes_gcm_test.json
+ // TC4
+ let test_key = hex!("bedcfb5a011ebc84600fcb296c15af0d");
+ let nonce = hex!("438a547a94ea88dce46c6c85");
+ let aes = A::new(&test_key.into());
+ let msg = hex!("");
+ let tag = hex!("960247ba5cde02e41a313c4c0136edc3");
+ let result = aes.encrypt(&msg, b"", &nonce).expect("Should succeed");
+ assert_eq!(&result[..], &tag);
+}
+
+pub fn aes_128_gcm_test_encrypt_detached<A>(_marker: marker::PhantomData<A>)
+where
+ A: AesGcm<Tag = [u8; 16]> + AeadInit<crypto_provider::aes::Aes128Key>,
+{
+ // https://github.com/google/wycheproof/blob/master/testvectors/aes_gcm_test.json
+ // TC4
+ let test_key = hex!("bedcfb5a011ebc84600fcb296c15af0d");
+ let nonce = hex!("438a547a94ea88dce46c6c85");
+ let aes = A::new(&test_key.into());
+ let msg = hex!("");
+ let tag = hex!("960247ba5cde02e41a313c4c0136edc3");
+ let mut buf = Vec::from(msg.as_slice());
+ let actual_tag: [u8; 16] = aes.encrypt_detached(&mut buf, b"", &nonce).unwrap();
+ assert_eq!(&buf, &[0_u8; 0]);
+ assert_eq!(&actual_tag, &tag);
+}
+
+/// Test AES-GCM-128 decryption
+pub fn aes_128_gcm_test_decrypt<A>(_marker: marker::PhantomData<A>)
+where
+ A: AesGcm<Tag = [u8; 16]> + AeadInit<crypto_provider::aes::Aes128Key>,
+{
+ // https://github.com/google/wycheproof/blob/master/testvectors/aes_gcm_test.json
+ // TC2
+ let test_key = hex!("5b9604fe14eadba931b0ccf34843dab9");
+ let nonce = hex!("921d2507fa8007b7bd067d34");
+ let aes = A::new(&test_key.into());
+ let msg = hex!("001d0c231287c1182784554ca3a21908");
+ let ct = hex!("49d8b9783e911913d87094d1f63cc765");
+ let ad = hex!("00112233445566778899aabbccddeeff");
+ let tag = hex!("1e348ba07cca2cf04c618cb4d43a5b92");
+ let result = aes.encrypt(&msg, &ad, &nonce).expect("should succeed");
+ assert_eq!(&result[..16], &ct);
+ assert_eq!(&result[16..], &tag);
+ assert_eq!(A::TAG_SIZE, result[16..].len());
+ let result = aes.decrypt(&result[..], &ad, &nonce).expect("should succeed");
+ assert_eq!(&result[..], &msg);
+}
+
+/// Test AES-GCM-128 decryption
+pub fn aes_128_gcm_test_decrypt_detached<A>(_marker: marker::PhantomData<A>)
+where
+ A: AesGcm<Tag = [u8; 16]> + AeadInit<crypto_provider::aes::Aes128Key>,
+{
+ // https://github.com/google/wycheproof/blob/master/testvectors/aes_gcm_test.json
+ // TC2
+ let test_key = hex!("5b9604fe14eadba931b0ccf34843dab9");
+ let nonce = hex!("921d2507fa8007b7bd067d34");
+ let aes = A::new(&test_key.into());
+ let msg = hex!("001d0c231287c1182784554ca3a21908");
+ let ct = hex!("49d8b9783e911913d87094d1f63cc765");
+ let ad = hex!("00112233445566778899aabbccddeeff");
+ let tag = hex!("1e348ba07cca2cf04c618cb4d43a5b92");
+ let mut buf = Vec::from(msg.as_slice());
+ let actual_tag = aes.encrypt_detached(&mut buf, &ad, &nonce).unwrap();
+ assert_eq!(&buf, &ct);
+ assert_eq!(actual_tag, tag);
+ assert!(aes.decrypt_detached(&mut buf, &ad, &nonce, &tag).is_ok());
+ assert_eq!(&buf[..], &msg);
+}
+
+/// Test AES-GCM-128 decryption
+pub fn aes_128_gcm_test_decrypt_detached_bad_tag<A>(_marker: marker::PhantomData<A>)
+where
+ A: AesGcm<Tag = [u8; 16]> + AeadInit<crypto_provider::aes::Aes128Key>,
+{
+ // https://github.com/google/wycheproof/blob/master/testvectors/aes_gcm_test.json
+ // TC23
+ let test_key = hex!("000102030405060708090a0b0c0d0e0f");
+ let nonce = hex!("505152535455565758595a5b");
+ let aes = A::new(&test_key.into());
+ let ct = hex!("eb156d081ed6b6b55f4612f021d87b39");
+ let mut buf = Vec::from(ct.as_slice());
+ let bad_tag = hex!("d9847dbc326a06e988c77ad3863e6083");
+ aes.decrypt_detached(&mut buf, b"", &nonce, &bad_tag)
+ .expect_err("decryption tag verification should fail");
+ // assert that the buffer does not change if tag verification fails
+ assert_eq!(buf, ct);
+}
+
+/// Test AES-256-GCM encryption/decryption
+pub fn aes_256_gcm_test_tc74<A>(_marker: marker::PhantomData<A>)
+where
+ A: AesGcm<Tag = [u8; 16]> + AeadInit<crypto_provider::aes::Aes256Key>,
+{
+ // https://github.com/google/wycheproof/blob/master/testvectors/aes_gcm_test.json
+ // TC74
+ let test_key = hex!("29d3a44f8723dc640239100c365423a312934ac80239212ac3df3421a2098123");
+ let nonce = hex!("00112233445566778899aabb");
+ let aes = A::new(&test_key.into());
+ let msg = hex!("");
+ let ad = hex!("aabbccddeeff");
+ let tag = hex!("2a7d77fa526b8250cb296078926b5020");
+ let result = aes.encrypt(&msg, &ad, &nonce).expect("should succeed");
+ assert_eq!(&result[..], &tag);
+ assert_eq!(A::TAG_SIZE, result.len());
+ let result = aes.decrypt(&result, &ad, &nonce).expect("should succeed");
+ assert_eq!(&result[..], &msg);
+}
+
+/// Test AES-256-GCM encryption/decryption
+pub fn aes_256_gcm_test_tc74_detached<A>(_marker: marker::PhantomData<A>)
+where
+ A: AesGcm<Tag = [u8; 16]> + AeadInit<crypto_provider::aes::Aes256Key>,
+{
+ // https://github.com/google/wycheproof/blob/master/testvectors/aes_gcm_test.json
+ // TC74
+ let test_key = hex!("29d3a44f8723dc640239100c365423a312934ac80239212ac3df3421a2098123");
+ let nonce = hex!("00112233445566778899aabb");
+ let aes = A::new(&test_key.into());
+ let msg = hex!("");
+ let ad = hex!("aabbccddeeff");
+ let ct = hex!("");
+ let tag = hex!("2a7d77fa526b8250cb296078926b5020");
+ let mut buf = Vec::new();
+ buf.extend_from_slice(&msg);
+ let actual_tag = aes.encrypt_detached(&mut buf, &ad, &nonce).unwrap();
+ assert_eq!(&buf, &ct);
+ assert_eq!(&actual_tag, &tag);
+ assert_eq!(A::TAG_SIZE, tag.len());
+ assert!(aes.decrypt_detached(&mut buf, &ad, &nonce, &actual_tag).is_ok());
+ assert_eq!(&buf, &msg);
+}
+
+/// Test AES-256-GCM encryption/decryption
+pub fn aes_256_gcm_test_tc79<A>(_marker: marker::PhantomData<A>)
+where
+ A: AesGcm<Tag = [u8; 16]> + AeadInit<crypto_provider::aes::Aes256Key>,
+{
+ // https://github.com/google/wycheproof/blob/master/testvectors/aes_gcm_test.json
+ // TC79
+ let test_key = hex!("59d4eafb4de0cfc7d3db99a8f54b15d7b39f0acc8da69763b019c1699f87674a");
+ let nonce = hex!("2fcb1b38a99e71b84740ad9b");
+ let aes = A::new(&test_key.into());
+ let msg = hex!("549b365af913f3b081131ccb6b825588");
+ let ct = hex!("f58c16690122d75356907fd96b570fca");
+ let tag = hex!("28752c20153092818faba2a334640d6e");
+ let result = aes.encrypt(&msg, b"", &nonce).expect("should succeed");
+ assert_eq!(&result[..16], &ct);
+ assert_eq!(&result[16..], &tag);
+ let result = aes.decrypt(&result[..], b"", &nonce).expect("should succeed");
+ assert_eq!(&result[..], &msg);
+}
+
+/// Test AES-256-GCM encryption/decryption
+pub fn aes_256_gcm_test_tc79_detached<A>(_marker: marker::PhantomData<A>)
+where
+ A: AesGcm<Tag = [u8; 16]> + AeadInit<crypto_provider::aes::Aes256Key>,
+{
+ // https://github.com/google/wycheproof/blob/master/testvectors/aes_gcm_test.json
+ // TC79
+ let test_key = hex!("59d4eafb4de0cfc7d3db99a8f54b15d7b39f0acc8da69763b019c1699f87674a");
+ let nonce = hex!("2fcb1b38a99e71b84740ad9b");
+ let aes = A::new(&test_key.into());
+ let msg = hex!("549b365af913f3b081131ccb6b825588");
+ let ct = hex!("f58c16690122d75356907fd96b570fca");
+ let tag = hex!("28752c20153092818faba2a334640d6e");
+ let mut buf = Vec::from(msg.as_slice());
+ let actual_tag = aes.encrypt_detached(&mut buf, b"", &nonce).unwrap();
+ assert_eq!(&buf, &ct);
+ assert_eq!(&actual_tag, &tag);
+ assert!(aes.decrypt_detached(&mut buf, b"", &nonce, &tag).is_ok());
+ assert_eq!(&buf, &msg);
+}
+
+/// Test AES-256-GCM encryption/decryption where the tag given to decryption doesn't match.
+pub fn aes_256_gcm_test_decrypt_detached_bad_tag<A>(_marker: marker::PhantomData<A>)
+where
+ A: AesGcm<Tag = [u8; 16]> + AeadInit<crypto_provider::aes::Aes256Key>,
+{
+ // https://github.com/google/wycheproof/blob/master/testvectors/aes_gcm_test.json
+ // TC94
+ let test_key = hex!("000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f");
+ let nonce = hex!("505152535455565758595a5b");
+ let aes = A::new(&test_key.into());
+ let aad = hex!("");
+ let ct = hex!("b2061457c0759fc1749f174ee1ccadfa");
+ let bad_tag = hex!("9de8fef6d8ab1bf1bf887232eab590dd");
+ let mut buf = Vec::from(ct.as_slice());
+ aes.decrypt_detached(&mut buf, &aad, &nonce, &bad_tag)
+ .expect_err("Decrypting with bad tag should fail");
+ // assert that the buffer does not change if tag verification fails
+ assert_eq!(buf, ct);
+}
+
+/// Generates the test cases to validate the AES-128-GCM implementation.
+/// For example, to test `MyAesGcm128Impl`:
+///
+/// ```
+/// use crypto_provider::aes::aes_gcm::testing::*;
+///
+/// mod tests {
+/// #[apply(aes_128_gcm_test_cases)]
+/// fn aes_128_gcm_tests(testcase: CryptoProviderTestCase<MyAesGcmImpl>) {
+/// testcase(MyAesGcm128Impl);
+/// }
+/// }
+/// ```
+#[template]
+#[export]
+#[rstest]
+#[case::encrypt(aes_128_gcm_test_encrypt)]
+#[case::decrypt(aes_128_gcm_test_decrypt)]
+fn aes_128_gcm_test_cases<F: AesGcmFactory<Key = Aes128Key>>(
+ #[case] testcase: CryptoProviderTestCase<F>,
+) {
+}
+
+/// Generates the test cases to validate the AES-128-GCM implementation.
+/// For example, to test `MyAesGcm128Impl`:
+///
+/// ```
+/// use crypto_provider::aes::aes_gcm::testing::*;
+///
+/// mod tests {
+/// #[apply(aes_128_gcm_test_cases_detached)]
+/// fn aes_128_gcm_tests(testcase: CryptoProviderTestCase<MyAesGcmImpl>) {
+/// testcase(MyAesGcm128Impl);
+/// }
+/// }
+/// ```
+#[template]
+#[export]
+#[rstest]
+#[case::encrypt_detached(aes_128_gcm_test_encrypt_detached)]
+#[case::decrypt_detached(aes_128_gcm_test_decrypt_detached)]
+#[case::decrypt_detached_bad_tag(aes_128_gcm_test_decrypt_detached_bad_tag)]
+fn aes_128_gcm_test_cases_detached<F: AesGcmFactory<Key = Aes128Key>>(
+ #[case] testcase: CryptoProviderTestCase<F>,
+) {
+}
+
+/// Generates the test cases to validate the AES-256-GCM implementation.
+/// For example, to test `MyAesGcm256Impl`:
+///
+/// ```
+/// use crypto_provider::aes::aes_gcm::testing::*;
+///
+/// mod tests {
+/// #[apply(aes_256_gcm_test_cases)]
+/// fn aes_256_gcm_tests(testcase: CryptoProviderTestCase<MyAesGcm256Impl>) {
+/// testcase(MyAesGcm256Impl);
+/// }
+/// }
+/// ```
+#[template]
+#[export]
+#[rstest]
+#[case::tc74(aes_256_gcm_test_tc74)]
+#[case::tc79(aes_256_gcm_test_tc79)]
+fn aes_256_gcm_test_cases<F: AesGcmFactory<Key = Aes256Key>>(
+ #[case] testcase: CryptoProviderTestCase<F>,
+) {
+}
+
+/// Generates the test cases to validate the AES-256-GCM implementation.
+/// For example, to test `MyAesGcm256Impl`:
+///
+/// ```
+/// use crypto_provider::aes::aes_gcm::testing::*;
+///
+/// mod tests {
+/// #[apply(aes_256_gcm_test_cases_detached)]
+/// fn aes_256_gcm_tests(testcase: CryptoProviderTestCase<MyAesGcm256Impl>) {
+/// testcase(MyAesGcm256Impl);
+/// }
+/// }
+/// ```
+#[template]
+#[export]
+#[rstest]
+#[case::tc74_detached(aes_256_gcm_test_tc74_detached)]
+#[case::tc79_detached(aes_256_gcm_test_tc79_detached)]
+#[case::decrypt_detached_bad_tag(aes_256_gcm_test_decrypt_detached_bad_tag)]
+fn aes_256_gcm_test_cases_detached<F: AesGcmFactory<Key = Aes256Key>>(
+ #[case] testcase: CryptoProviderTestCase<F>,
+) {
+}
diff --git a/nearby/crypto/crypto_provider_test/src/aead/aes_gcm_siv.rs b/nearby/crypto/crypto_provider_test/src/aead/aes_gcm_siv.rs
index 893fba2..56d2215 100644
--- a/nearby/crypto/crypto_provider_test/src/aead/aes_gcm_siv.rs
+++ b/nearby/crypto/crypto_provider_test/src/aead/aes_gcm_siv.rs
@@ -18,12 +18,28 @@ use hex_literal::hex;
use rstest_reuse::template;
pub use crate::prelude;
-use crypto_provider::aes::{Aes128Key, Aes256Key};
+use crypto_provider::aead::{AeadInit, AesGcmSiv};
-use crypto_provider::aead::aes_gcm_siv::AesGcmSiv;
+/// Test AES-GCM-SIV-128 encryption
+pub fn aes_128_gcm_siv_test_encrypt<A>(_marker: marker::PhantomData<A>)
+where
+ A: AesGcmSiv<Tag = [u8; 16]> + AeadInit<crypto_provider::aes::Aes128Key>,
+{
+ // https://github.com/google/wycheproof/blob/master/testvectors/aes_gcm_siv_test.json
+ // TC1
+ let test_key = hex!("01000000000000000000000000000000");
+ let nonce = hex!("030000000000000000000000");
+ let aes = A::new(&test_key.into());
+ let msg = hex!("");
+ let tag = hex!("dc20e2d83f25705bb49e439eca56de25");
+ let result = aes.encrypt(&msg, b"", &nonce).expect("Should succeed");
+ assert_eq!(&result[..], &tag);
+}
-/// Test AES-GCM-SIV-128 encryption/decryption
-pub fn aes_128_gcm_siv_test<A: AesGcmSiv<Key = Aes128Key>>(_marker: marker::PhantomData<A>) {
+pub fn aes_128_gcm_siv_test_encrypt_detached<A>(_marker: marker::PhantomData<A>)
+where
+ A: AesGcmSiv<Tag = [u8; 16]> + AeadInit<crypto_provider::aes::Aes128Key>,
+{
// https://github.com/google/wycheproof/blob/master/testvectors/aes_gcm_siv_test.json
// TC1
let test_key = hex!("01000000000000000000000000000000");
@@ -32,23 +48,102 @@ pub fn aes_128_gcm_siv_test<A: AesGcmSiv<Key = Aes128Key>>(_marker: marker::Phan
let msg = hex!("");
let mut buf = Vec::from(msg.as_slice());
let tag = hex!("dc20e2d83f25705bb49e439eca56de25");
- assert!(aes.encrypt(&mut buf, b"", &nonce).is_ok());
- assert_eq!(&buf[..], &tag);
+ let actual_tag: [u8; 16] = aes.encrypt_detached(&mut buf, b"", &nonce).unwrap();
+ assert_eq!(&buf, &[0_u8; 0]);
+ assert_eq!(&actual_tag, &tag);
+}
+
+/// Test AES-GCM-SIV-128 decryption
+pub fn aes_128_gcm_siv_test_decrypt<A>(_marker: marker::PhantomData<A>)
+where
+ A: AesGcmSiv<Tag = [u8; 16]> + AeadInit<crypto_provider::aes::Aes128Key>,
+{
+ // https://github.com/google/wycheproof/blob/master/testvectors/aes_gcm_siv_test.json
+ // TC2
+ let test_key = hex!("01000000000000000000000000000000");
+ let nonce = hex!("030000000000000000000000");
+ let aes = A::new(&test_key.into());
+ let msg = hex!("0100000000000000");
+ let ct = hex!("b5d839330ac7b786");
+ let tag = hex!("578782fff6013b815b287c22493a364c");
+ let result = aes.encrypt(&msg, b"", &nonce).expect("should succeed");
+ assert_eq!(&result[..8], &ct);
+ assert_eq!(&result[8..], &tag);
+ assert_eq!(A::TAG_SIZE, result[8..].len());
+ let result = aes.decrypt(&result[..], b"", &nonce).expect("should succeed");
+ assert_eq!(&result[..], &msg);
+}
+
+/// Test AES-GCM-SIV-128 decryption
+pub fn aes_128_gcm_siv_test_decrypt_detached<A>(_marker: marker::PhantomData<A>)
+where
+ A: AesGcmSiv<Tag = [u8; 16]> + AeadInit<crypto_provider::aes::Aes128Key>,
+{
+ // https://github.com/google/wycheproof/blob/master/testvectors/aes_gcm_siv_test.json
// TC2
+ let test_key = hex!("01000000000000000000000000000000");
+ let nonce = hex!("030000000000000000000000");
+ let aes = A::new(&test_key.into());
let msg = hex!("0100000000000000");
let ct = hex!("b5d839330ac7b786");
let tag = hex!("578782fff6013b815b287c22493a364c");
let mut buf = Vec::from(msg.as_slice());
- assert!(aes.encrypt(&mut buf, b"", &nonce).is_ok());
- assert_eq!(&buf[..8], &ct);
- assert_eq!(&buf[8..], &tag);
- assert_eq!(A::TAG_SIZE, buf[8..].len());
- assert!(aes.decrypt(&mut buf, b"", &nonce).is_ok());
+ let actual_tag = aes.encrypt_detached(&mut buf, b"", &nonce).unwrap();
+ assert_eq!(&buf, &ct);
+ assert_eq!(actual_tag, tag);
+ assert!(aes.decrypt_detached(&mut buf, b"", &nonce, &tag).is_ok());
assert_eq!(&buf[..], &msg);
}
+/// Test AES-GCM-SIV-128 decryption where the tag given to decryption doesn't match
+pub fn aes_128_gcm_siv_test_decrypt_detached_bad_tag<A>(_marker: marker::PhantomData<A>)
+where
+ A: AesGcmSiv<Tag = [u8; 16]> + AeadInit<crypto_provider::aes::Aes128Key>,
+{
+ // https://github.com/google/wycheproof/blob/master/testvectors/aes_gcm_siv_test.json
+ // TC45
+ let test_key = hex!("00112233445566778899aabbccddeeff");
+ let nonce = hex!("000000000000000000000000");
+ let aad = hex!("9ea3371e258288d5a01b15384e2c99ee");
+ let aes = A::new(&test_key.into());
+ // Use a longer ciphertext as the test case to make sure it's not unchanged only for the most
+ // recent block.
+ let ct = hex!(
+ "00112233445566778899aabbccddeeff00112233445566778899aabbccddeeff"
+ "00112233445566778899aabbccddeeff00112233445566778899aabbccddeeff"
+ );
+ let bad_tag = hex!("13a1883272188b4c8d2727178198fe95");
+ let mut buf = Vec::from(ct.as_slice());
+ aes.decrypt_detached(&mut buf, &aad, &nonce, &bad_tag).expect_err("Decryption should fail");
+ assert_eq!(&buf, &ct); // Buffer should be unchanged if decryption failed
+}
+
+/// Test AES-256-GCM-SIV encryption/decryption
+pub fn aes_256_gcm_siv_test_tc77<A>(_marker: marker::PhantomData<A>)
+where
+ A: AesGcmSiv<Tag = [u8; 16]> + AeadInit<crypto_provider::aes::Aes256Key>,
+{
+ // https://github.com/google/wycheproof/blob/master/testvectors/aes_gcm_siv_test.json
+ // TC77
+ let test_key = hex!("0100000000000000000000000000000000000000000000000000000000000000");
+ let nonce = hex!("030000000000000000000000");
+ let aes = A::new(&test_key.into());
+ let msg = hex!("0100000000000000");
+ let ct = hex!("c2ef328e5c71c83b");
+ let tag = hex!("843122130f7364b761e0b97427e3df28");
+ let result = aes.encrypt(&msg, b"", &nonce).expect("should succeed");
+ assert_eq!(&result[..8], &ct);
+ assert_eq!(&result[8..], &tag);
+ assert_eq!(A::TAG_SIZE, result[8..].len());
+ let result = aes.decrypt(&result[..], b"", &nonce).expect("should succeed");
+ assert_eq!(&result[..], &msg);
+}
+
/// Test AES-256-GCM-SIV encryption/decryption
-pub fn aes_256_gcm_siv_test<A: AesGcmSiv<Key = Aes256Key>>(_marker: marker::PhantomData<A>) {
+pub fn aes_256_gcm_siv_test_tc77_detached<A>(_marker: marker::PhantomData<A>)
+where
+ A: AesGcmSiv<Tag = [u8; 16]> + AeadInit<crypto_provider::aes::Aes256Key>,
+{
// https://github.com/google/wycheproof/blob/master/testvectors/aes_gcm_siv_test.json
// TC77
let test_key = hex!("0100000000000000000000000000000000000000000000000000000000000000");
@@ -59,22 +154,72 @@ pub fn aes_256_gcm_siv_test<A: AesGcmSiv<Key = Aes256Key>>(_marker: marker::Phan
buf.extend_from_slice(&msg);
let ct = hex!("c2ef328e5c71c83b");
let tag = hex!("843122130f7364b761e0b97427e3df28");
- assert!(aes.encrypt(&mut buf, b"", &nonce).is_ok());
- assert_eq!(&buf[..8], &ct);
- assert_eq!(&buf[8..], &tag);
- assert_eq!(A::TAG_SIZE, buf[8..].len());
- assert!(aes.decrypt(&mut buf, b"", &nonce).is_ok());
- assert_eq!(&buf[..], &msg);
+ let actual_tag = aes.encrypt_detached(&mut buf, b"", &nonce).unwrap();
+ assert_eq!(&buf, &ct);
+ assert_eq!(&actual_tag, &tag);
+ assert_eq!(A::TAG_SIZE, tag.len());
+ assert!(aes.decrypt_detached(&mut buf, b"", &nonce, &actual_tag).is_ok());
+ assert_eq!(&buf, &msg);
+}
+
+/// Test AES-256-GCM-SIV encryption/decryption
+pub fn aes_256_gcm_siv_test_tc78<A>(_marker: marker::PhantomData<A>)
+where
+ A: AesGcmSiv<Tag = [u8; 16]> + AeadInit<crypto_provider::aes::Aes256Key>,
+{
+ // https://github.com/google/wycheproof/blob/master/testvectors/aes_gcm_siv_test.json
// TC78
+ let test_key = hex!("0100000000000000000000000000000000000000000000000000000000000000");
+ let nonce = hex!("030000000000000000000000");
+ let aes = A::new(&test_key.into());
+ let msg = hex!("010000000000000000000000");
+ let ct = hex!("9aab2aeb3faa0a34aea8e2b1");
+ let tag = hex!("8ca50da9ae6559e48fd10f6e5c9ca17e");
+ let result = aes.encrypt(&msg, b"", &nonce).expect("should succeed");
+ assert_eq!(&result[..12], &ct);
+ assert_eq!(&result[12..], &tag);
+ let result = aes.decrypt(&result[..], b"", &nonce).expect("should succeed");
+ assert_eq!(&result[..], &msg);
+}
+
+/// Test AES-256-GCM-SIV encryption/decryption
+pub fn aes_256_gcm_siv_test_tc78_detached<A>(_marker: marker::PhantomData<A>)
+where
+ A: AesGcmSiv<Tag = [u8; 16]> + AeadInit<crypto_provider::aes::Aes256Key>,
+{
+ // https://github.com/google/wycheproof/blob/master/testvectors/aes_gcm_siv_test.json
+ // TC78
+ let test_key = hex!("0100000000000000000000000000000000000000000000000000000000000000");
+ let nonce = hex!("030000000000000000000000");
+ let aes = A::new(&test_key.into());
let msg = hex!("010000000000000000000000");
let ct = hex!("9aab2aeb3faa0a34aea8e2b1");
let tag = hex!("8ca50da9ae6559e48fd10f6e5c9ca17e");
let mut buf = Vec::from(msg.as_slice());
- assert!(aes.encrypt(&mut buf, b"", &nonce).is_ok());
- assert_eq!(&buf[..12], &ct);
- assert_eq!(&buf[12..], &tag);
- assert!(aes.decrypt(&mut buf, b"", &nonce).is_ok());
- assert_eq!(&buf[..], &msg);
+ let actual_tag = aes.encrypt_detached(&mut buf, b"", &nonce).unwrap();
+ assert_eq!(&buf, &ct);
+ assert_eq!(&actual_tag, &tag);
+ assert!(aes.decrypt_detached(&mut buf, b"", &nonce, &tag).is_ok());
+ assert_eq!(&buf, &msg);
+}
+
+/// Test AES-256-GCM-SIV encryption/decryption where the tag given to decryption doesn't match.
+pub fn aes_256_gcm_siv_test_decrypt_detached_bad_tag<A>(_marker: marker::PhantomData<A>)
+where
+ A: AesGcmSiv<Tag = [u8; 16]> + AeadInit<crypto_provider::aes::Aes256Key>,
+{
+ // https://github.com/google/wycheproof/blob/master/testvectors/aes_gcm_siv_test.json
+ // TC122
+ let test_key = hex!("00112233445566778899aabbccddeeff00112233445566778899aabbccddeeff");
+ let nonce = hex!("000000000000000000000000");
+ let aes = A::new(&test_key.into());
+ let aad = hex!("0289eaa93eb084107d2088435ef2a0cd");
+ let ct = hex!("ffffffffffffffff");
+ let bad_tag = hex!("ffffffffffffffffffffffffffffffff");
+ let mut buf = Vec::from(ct.as_slice());
+ aes.decrypt_detached(&mut buf, &aad, &nonce, &bad_tag)
+ .expect_err("Decrypting with bad tag should fail");
+ assert_eq!(&buf, &ct); // The buffer should be unchanged if the decryption failed
}
/// Generates the test cases to validate the AES-128-GCM-SIV implementation.
@@ -93,13 +238,37 @@ pub fn aes_256_gcm_siv_test<A: AesGcmSiv<Key = Aes256Key>>(_marker: marker::Phan
#[template]
#[export]
#[rstest]
-#[case::encrypt(aes_128_gcm_siv_test)]
-#[case::decrypt(aes_128_gcm_siv_test)]
+#[case::encrypt(aes_128_gcm_siv_test_encrypt)]
+#[case::decrypt(aes_128_gcm_siv_test_decrypt)]
fn aes_128_gcm_siv_test_cases<F: AesGcmSivFactory<Key = Aes128Key>>(
#[case] testcase: CryptoProviderTestCase<F>,
) {
}
+/// Generates the test cases to validate the AES-128-GCM-SIV implementation.
+/// For example, to test `MyAesGcmSiv128Impl`:
+///
+/// ```
+/// use crypto_provider::aes::aes_gcm_siv::testing::*;
+///
+/// mod tests {
+/// #[apply(aes_128_gcm_siv_test_cases_detached)]
+/// fn aes_128_gcm_siv_tests(testcase: CryptoProviderTestCase<MyAesGcmSivImpl>) {
+/// testcase(MyAesGcmSiv128Impl);
+/// }
+/// }
+/// ```
+#[template]
+#[export]
+#[rstest]
+#[case::encrypt_detached(aes_128_gcm_siv_test_encrypt_detached)]
+#[case::decrypt_detached(aes_128_gcm_siv_test_decrypt_detached)]
+#[case::decrypt_detached_bad_tag(aes_128_gcm_siv_test_decrypt_detached_bad_tag)]
+fn aes_128_gcm_siv_test_cases_detached<F: AesGcmSivFactory<Key = Aes128Key>>(
+ #[case] testcase: CryptoProviderTestCase<F>,
+) {
+}
+
/// Generates the test cases to validate the AES-256-GCM-SIV implementation.
/// For example, to test `MyAesGcmSiv256Impl`:
///
@@ -116,9 +285,33 @@ fn aes_128_gcm_siv_test_cases<F: AesGcmSivFactory<Key = Aes128Key>>(
#[template]
#[export]
#[rstest]
-#[case::encrypt(aes_256_gcm_siv_test)]
-#[case::decrypt(aes_256_gcm_siv_test)]
+#[case::tc77(aes_256_gcm_siv_test_tc77)]
+#[case::tc78(aes_256_gcm_siv_test_tc78)]
fn aes_256_gcm_siv_test_cases<F: AesGcmSivFactory<Key = Aes256Key>>(
#[case] testcase: CryptoProviderTestCase<F>,
) {
}
+
+/// Generates the test cases to validate the AES-256-GCM-SIV implementation.
+/// For example, to test `MyAesGcmSiv256Impl`:
+///
+/// ```
+/// use crypto_provider::aes::aes_gcm_siv::testing::*;
+///
+/// mod tests {
+/// #[apply(aes_256_gcm_siv_test_cases_detached)]
+/// fn aes_256_gcm_siv_tests(testcase: CryptoProviderTestCase<MyAesGcmSiv256Impl>) {
+/// testcase(MyAesGcmSiv256Impl);
+/// }
+/// }
+/// ```
+#[template]
+#[export]
+#[rstest]
+#[case::tc77_detached(aes_256_gcm_siv_test_tc77_detached)]
+#[case::tc78_detached(aes_256_gcm_siv_test_tc78_detached)]
+#[case::decrypt_detached_bad_tag(aes_256_gcm_siv_test_decrypt_detached_bad_tag)]
+fn aes_256_gcm_siv_test_cases_detached<F: AesGcmSivFactory<Key = Aes256Key>>(
+ #[case] testcase: CryptoProviderTestCase<F>,
+) {
+}
diff --git a/nearby/crypto/crypto_provider_test/src/aead/mod.rs b/nearby/crypto/crypto_provider_test/src/aead/mod.rs
index 962aa49..3fe857c 100644
--- a/nearby/crypto/crypto_provider_test/src/aead/mod.rs
+++ b/nearby/crypto/crypto_provider_test/src/aead/mod.rs
@@ -14,3 +14,6 @@
/// Contains test cases for aes_gcm_siv implementations.
pub mod aes_gcm_siv;
+
+/// Contains test cases for aes_gcm implementations.
+pub mod aes_gcm;
diff --git a/nearby/crypto/crypto_provider_test/src/aes/cbc.rs b/nearby/crypto/crypto_provider_test/src/aes/cbc.rs
index b22c828..46cbebe 100644
--- a/nearby/crypto/crypto_provider_test/src/aes/cbc.rs
+++ b/nearby/crypto/crypto_provider_test/src/aes/cbc.rs
@@ -15,13 +15,17 @@
use crate::aes::Aes256Key;
pub use crate::prelude::*;
use core::marker::PhantomData;
-use crypto_provider::aes::cbc::{AesCbcIv, AesCbcPkcs7Padded};
+use crypto_provider::{
+ aes::cbc::{AesCbcIv, AesCbcPkcs7Padded},
+ tinyvec::SliceVec,
+};
use hex_literal::hex;
use rstest_reuse::template;
/// Tests for AES-256-CBC encryption
pub fn aes_256_cbc_test_encrypt<A: AesCbcPkcs7Padded>(_marker: PhantomData<A>) {
- // http://google3/third_party/wycheproof/testvectors/aes_cbc_pkcs5_test.json;l=1492;rcl=264817632
+ // https://github.com/google/wycheproof/blob/b063b4a/testvectors/aes_cbc_pkcs5_test.json#L1492
+ // tcId: 132
let key: Aes256Key =
hex!("665a02bc265a66d01775091da56726b6668bfd903cb7af66fb1b78a8a062e43c").into();
let iv: AesCbcIv = hex!("3fb0d5ecd06c71150748b599595833cb");
@@ -30,9 +34,47 @@ pub fn aes_256_cbc_test_encrypt<A: AesCbcPkcs7Padded>(_marker: PhantomData<A>) {
assert_eq!(A::encrypt(&key, &iv, &msg), expected_ciphertext);
}
+/// Tests for AES-256-CBC in-place encryption
+pub fn aes_256_cbc_test_encrypt_in_place<A: AesCbcPkcs7Padded>(_marker: PhantomData<A>) {
+ // https://github.com/google/wycheproof/blob/b063b4a/testvectors/aes_cbc_pkcs5_test.json#L1492
+ // tcId: 132
+ let key: Aes256Key =
+ hex!("665a02bc265a66d01775091da56726b6668bfd903cb7af66fb1b78a8a062e43c").into();
+ let iv: AesCbcIv = hex!("3fb0d5ecd06c71150748b599595833cb");
+ let msg = hex!("3f56935def3f");
+ let expected_ciphertext = hex!("3f3f39697bd7e88d85a14132be1cbc48");
+ let mut msg_buffer_backing = [0_u8; 16];
+ let mut msg_buffer = SliceVec::from_slice_len(&mut msg_buffer_backing, 0);
+ msg_buffer.extend_from_slice(&msg);
+ A::encrypt_in_place(&key, &iv, &mut msg_buffer).unwrap();
+ assert_eq!(msg_buffer.as_slice(), &expected_ciphertext);
+}
+
+/// Tests for AES-256-CBC encryption, where the given buffer `SliceVec` is too short to contain the
+/// output.
+pub fn aes_256_cbc_test_encrypt_in_place_too_short<A: AesCbcPkcs7Padded>(_marker: PhantomData<A>) {
+ // https://github.com/google/wycheproof/blob/b063b4a/testvectors/aes_cbc_pkcs5_test.json#L1612
+ // tcId: 144
+ let key: Aes256Key =
+ hex!("4f097858a1aec62cf18f0966b2b120783aa4ae9149d3213109740506ae47adfe").into();
+ let iv: AesCbcIv = hex!("400aab92803bcbb44a96ef789655b34e");
+ let msg = hex!("ee53d8e5039e82d9fcca114e375a014febfea117a7e709d9008d43858e3660");
+ let mut msg_buffer_backing = [0_u8; 31];
+ let mut msg_buffer = SliceVec::from_slice_len(&mut msg_buffer_backing, 0);
+ msg_buffer.extend_from_slice(&msg);
+ A::encrypt_in_place(&key, &iv, &mut msg_buffer)
+ .expect_err("Encrypting AES with 15-byte buffer should fail");
+ // Buffer content is undefined, but test to make sure it doesn't contain half-decrypted data
+ assert!(
+ msg_buffer.as_slice() == [0_u8; 32] || msg_buffer.as_slice() == msg,
+ "Unrecognized content in buffer after decryption failure"
+ )
+}
+
/// Tests for AES-256-CBC decryption
pub fn aes_256_cbc_test_decrypt<A: AesCbcPkcs7Padded>(_marker: PhantomData<A>) {
- // http://google3/third_party/wycheproof/testvectors/aes_cbc_pkcs5_test.json;l=1492;rcl=264817632
+ // https://github.com/google/wycheproof/blob/b063b4a/testvectors/aes_cbc_pkcs5_test.json#L1492
+ // tcId: 132
let key: Aes256Key =
hex!("665a02bc265a66d01775091da56726b6668bfd903cb7af66fb1b78a8a062e43c").into();
let iv: AesCbcIv = hex!("3fb0d5ecd06c71150748b599595833cb");
@@ -41,6 +83,52 @@ pub fn aes_256_cbc_test_decrypt<A: AesCbcPkcs7Padded>(_marker: PhantomData<A>) {
assert_eq!(A::decrypt(&key, &iv, &ciphertext).unwrap(), expected_msg);
}
+/// Tests for AES-256-CBC decryption with bad padding
+pub fn aes_256_cbc_test_decrypt_bad_padding<A: AesCbcPkcs7Padded>(_marker: PhantomData<A>) {
+ // https://github.com/google/wycheproof/blob/b063b4a/testvectors/aes_cbc_pkcs5_test.json#L1690
+ // tcId: 151
+ let key: Aes256Key =
+ hex!("7c78f34dbce8f0557d43630266f59babd1cb92ba624bd1a8f45a2a91c84a804a").into();
+ let iv: AesCbcIv = hex!("f010f61c31c9aa8fa0d5be5f6b0f2f70");
+ let ciphertext = hex!("8881e9e02fa9e3037b397957ba1fb7ce64679a46621b792f643542a735f0bbbf");
+ A::decrypt(&key, &iv, &ciphertext).expect_err("Decryption with bad padding should fail");
+}
+
+/// Tests for AES-256-CBC in-place decryption
+pub fn aes_256_cbc_test_decrypt_in_place<A: AesCbcPkcs7Padded>(_marker: PhantomData<A>) {
+ // https://github.com/google/wycheproof/blob/b063b4a/testvectors/aes_cbc_pkcs5_test.json#L1492
+ // tcId: 132
+ let key: Aes256Key =
+ hex!("665a02bc265a66d01775091da56726b6668bfd903cb7af66fb1b78a8a062e43c").into();
+ let iv: AesCbcIv = hex!("3fb0d5ecd06c71150748b599595833cb");
+ let mut ciphertext = hex!("3f3f39697bd7e88d85a14132be1cbc48");
+ let expected_msg = hex!("3f56935def3f");
+ let mut msg_buffer = SliceVec::from(&mut ciphertext);
+ A::decrypt_in_place(&key, &iv, &mut msg_buffer).unwrap();
+ assert_eq!(msg_buffer.as_slice(), expected_msg);
+}
+
+/// Tests for AES-256-CBC in-place decryption with bad padding
+pub fn aes_256_cbc_test_decrypt_in_place_bad_padding<A: AesCbcPkcs7Padded>(
+ _marker: PhantomData<A>,
+) {
+ // https://github.com/google/wycheproof/blob/b063b4a/testvectors/aes_cbc_pkcs5_test.json#L1690
+ // tcId: 151
+ let key: Aes256Key =
+ hex!("7c78f34dbce8f0557d43630266f59babd1cb92ba624bd1a8f45a2a91c84a804a").into();
+ let iv: AesCbcIv = hex!("f010f61c31c9aa8fa0d5be5f6b0f2f70");
+ let ciphertext = hex!("8881e9e02fa9e3037b397957ba1fb7ce64679a46621b792f643542a735f0bbbf");
+ let mut msg_buffer_backing = ciphertext;
+ let mut msg_buffer = SliceVec::from(&mut msg_buffer_backing);
+ A::decrypt_in_place(&key, &iv, &mut msg_buffer)
+ .expect_err("Decryption with bad padding should fail");
+ // Buffer content is undefined, but test to make sure it doesn't contain half-decrypted data
+ assert!(
+ msg_buffer.as_slice() == [0_u8; 32] || msg_buffer.as_slice() == ciphertext,
+ "Unrecognized content in buffer after decryption failure"
+ )
+}
+
/// Generates the test cases to validate the AES-256-CBC implementation.
/// For example, to test `MyAesCbc256Impl`:
///
@@ -59,5 +147,9 @@ pub fn aes_256_cbc_test_decrypt<A: AesCbcPkcs7Padded>(_marker: PhantomData<A>) {
#[export]
#[rstest]
#[case::encrypt(aes_256_cbc_test_encrypt)]
+#[case::encrypt_in_place(aes_256_cbc_test_encrypt_in_place)]
+#[case::encrypt_in_place_too_short(aes_256_cbc_test_encrypt_in_place_too_short)]
#[case::decrypt(aes_256_cbc_test_decrypt)]
+#[case::decrypt_bad_padding(aes_256_cbc_test_decrypt_bad_padding)]
+#[case::decrypt_in_place_bad_padding(aes_256_cbc_test_decrypt_in_place_bad_padding)]
fn aes_256_cbc_test_cases<A: AesCbcPkcs7Padded>(#[case] testcase: CryptoProviderTestCases<F>) {}
diff --git a/nearby/crypto/crypto_provider_test/src/aes/ctr.rs b/nearby/crypto/crypto_provider_test/src/aes/ctr.rs
index b1d6b8b..de71740 100644
--- a/nearby/crypto/crypto_provider_test/src/aes/ctr.rs
+++ b/nearby/crypto/crypto_provider_test/src/aes/ctr.rs
@@ -27,22 +27,22 @@ pub fn aes_128_ctr_test_encrypt<A: AesCtr<Key = Aes128Key>>(_marker: marker::Pha
let mut cipher = A::new(&key, NonceAndCounter::from_block(iv));
block = hex!("6bc1bee22e409f96e93d7e117393172a");
- cipher.encrypt(&mut block);
+ cipher.apply_keystream(&mut block);
let expected_ciphertext_1 = hex!("874d6191b620e3261bef6864990db6ce");
assert_eq!(expected_ciphertext_1, block);
block = hex!("ae2d8a571e03ac9c9eb76fac45af8e51");
- cipher.encrypt(&mut block);
+ cipher.apply_keystream(&mut block);
let expected_ciphertext_2 = hex!("9806f66b7970fdff8617187bb9fffdff");
assert_eq!(expected_ciphertext_2, block);
block = hex!("30c81c46a35ce411e5fbc1191a0a52ef");
- cipher.encrypt(&mut block);
+ cipher.apply_keystream(&mut block);
let expected_ciphertext_3 = hex!("5ae4df3edbd5d35e5b4f09020db03eab");
assert_eq!(expected_ciphertext_3, block);
block = hex!("f69f2445df4f9b17ad2b417be66c3710");
- cipher.encrypt(&mut block);
+ cipher.apply_keystream(&mut block);
let expected_ciphertext_3 = hex!("1e031dda2fbe03d1792170a0f3009cee");
assert_eq!(expected_ciphertext_3, block);
}
@@ -56,22 +56,22 @@ pub fn aes_128_ctr_test_decrypt<A: AesCtr<Key = Aes128Key>>(_marker: marker::Pha
let mut cipher = A::new(&key, NonceAndCounter::from_block(iv));
block = hex!("874d6191b620e3261bef6864990db6ce");
- cipher.decrypt(&mut block);
+ cipher.apply_keystream(&mut block);
let expected_plaintext_1 = hex!("6bc1bee22e409f96e93d7e117393172a");
assert_eq!(expected_plaintext_1, block);
block = hex!("9806f66b7970fdff8617187bb9fffdff");
- cipher.decrypt(&mut block);
+ cipher.apply_keystream(&mut block);
let expected_plaintext_2 = hex!("ae2d8a571e03ac9c9eb76fac45af8e51");
assert_eq!(expected_plaintext_2, block);
block = hex!("5ae4df3edbd5d35e5b4f09020db03eab");
- cipher.decrypt(&mut block);
+ cipher.apply_keystream(&mut block);
let expected_plaintext_3 = hex!("30c81c46a35ce411e5fbc1191a0a52ef");
assert_eq!(expected_plaintext_3, block);
block = hex!("1e031dda2fbe03d1792170a0f3009cee");
- cipher.decrypt(&mut block);
+ cipher.apply_keystream(&mut block);
let expected_plaintext_3 = hex!("f69f2445df4f9b17ad2b417be66c3710");
assert_eq!(expected_plaintext_3, block);
}
@@ -86,22 +86,22 @@ pub fn aes_256_ctr_test_encrypt<A: AesCtr<Key = Aes256Key>>(_marker: marker::Pha
let mut cipher = A::new(&key, NonceAndCounter::from_block(iv));
block = hex!("6bc1bee22e409f96e93d7e117393172a");
- cipher.encrypt(&mut block);
+ cipher.apply_keystream(&mut block);
let expected_ciphertext_1 = hex!("601ec313775789a5b7a7f504bbf3d228");
assert_eq!(expected_ciphertext_1, block);
block = hex!("ae2d8a571e03ac9c9eb76fac45af8e51");
- cipher.encrypt(&mut block);
+ cipher.apply_keystream(&mut block);
let expected_ciphertext_2 = hex!("f443e3ca4d62b59aca84e990cacaf5c5");
assert_eq!(expected_ciphertext_2, block);
block = hex!("30c81c46a35ce411e5fbc1191a0a52ef");
- cipher.encrypt(&mut block);
+ cipher.apply_keystream(&mut block);
let expected_ciphertext_3 = hex!("2b0930daa23de94ce87017ba2d84988d");
assert_eq!(expected_ciphertext_3, block);
block = hex!("f69f2445df4f9b17ad2b417be66c3710");
- cipher.encrypt(&mut block);
+ cipher.apply_keystream(&mut block);
let expected_ciphertext_3 = hex!("dfc9c58db67aada613c2dd08457941a6");
assert_eq!(expected_ciphertext_3, block);
}
@@ -116,22 +116,22 @@ pub fn aes_256_ctr_test_decrypt<A: AesCtr<Key = Aes256Key>>(_marker: marker::Pha
let mut cipher = A::new(&key, NonceAndCounter::from_block(iv));
block = hex!("601ec313775789a5b7a7f504bbf3d228");
- cipher.decrypt(&mut block);
+ cipher.apply_keystream(&mut block);
let expected_plaintext_1 = hex!("6bc1bee22e409f96e93d7e117393172a");
assert_eq!(expected_plaintext_1, block);
block = hex!("f443e3ca4d62b59aca84e990cacaf5c5");
- cipher.decrypt(&mut block);
+ cipher.apply_keystream(&mut block);
let expected_plaintext_2 = hex!("ae2d8a571e03ac9c9eb76fac45af8e51");
assert_eq!(expected_plaintext_2, block);
block = hex!("2b0930daa23de94ce87017ba2d84988d");
- cipher.decrypt(&mut block);
+ cipher.apply_keystream(&mut block);
let expected_plaintext_3 = hex!("30c81c46a35ce411e5fbc1191a0a52ef");
assert_eq!(expected_plaintext_3, block);
block = hex!("dfc9c58db67aada613c2dd08457941a6");
- cipher.decrypt(&mut block);
+ cipher.apply_keystream(&mut block);
let expected_plaintext_3 = hex!("f69f2445df4f9b17ad2b417be66c3710");
assert_eq!(expected_plaintext_3, block);
}
diff --git a/nearby/crypto/crypto_provider_test/src/ed25519.rs b/nearby/crypto/crypto_provider_test/src/ed25519.rs
index d99605c..0fdb484 100644
--- a/nearby/crypto/crypto_provider_test/src/ed25519.rs
+++ b/nearby/crypto/crypto_provider_test/src/ed25519.rs
@@ -18,7 +18,9 @@ extern crate std;
use alloc::borrow::ToOwned;
use alloc::string::String;
use alloc::vec::Vec;
-use crypto_provider::ed25519::{Ed25519Provider, KeyPair, PublicKey, Signature};
+use crypto_provider::ed25519::{
+ Ed25519Provider, KeyPair, PublicKey, RawPrivateKeyPermit, RawSignature, Signature,
+};
use wycheproof::TestResult;
// These are test vectors from the creators of Ed25519: https://ed25519.cr.yp.to/ which are referenced
@@ -27,7 +29,7 @@ use wycheproof::TestResult;
// also used for test cases in the above RFC:
// https://dev.gnupg.org/source/libgcrypt/browse/master/tests/t-ed25519.inp
const PATH_TO_RFC_VECTORS_FILE: &str =
- "crypto/crypto_provider_test/src/testdata/ecdsa/rfc_test_vectors.txt";
+ "crypto/crypto_provider_test/src/testdata/EdDSA/rfc_test_vectors.txt";
/// Runs set of Ed25519 wycheproof test vectors against a provided ed25519 implementation
/// Tests vectors from Project Wycheproof: <https://github.com/google/wycheproof>
@@ -39,10 +41,7 @@ where
.expect("should be able to load test set");
for test_group in test_set.test_groups {
- let key_pair = test_group.key;
- let public_key = key_pair.pk;
- let secret_key = key_pair.sk;
-
+ let public_key = test_group.key.pk.to_vec();
for test in test_group.tests {
let tc_id = test.tc_id;
let comment = test.comment;
@@ -53,8 +52,7 @@ where
TestResult::Invalid => false,
TestResult::Valid | TestResult::Acceptable => true,
};
- let result =
- run_test::<E>(public_key.clone(), secret_key.clone(), sig.clone(), msg.clone());
+ let result = run_wycheproof_test::<E>(public_key.to_vec(), sig.to_vec(), msg.to_vec());
if valid {
if let Err(desc) = result {
panic!(
@@ -73,6 +71,20 @@ where
}
}
+fn run_wycheproof_test<E>(pub_key: Vec<u8>, sig: Vec<u8>, msg: Vec<u8>) -> Result<(), &'static str>
+where
+ E: Ed25519Provider,
+{
+ let pub_key = E::PublicKey::from_bytes(pub_key.as_slice().try_into().unwrap())
+ .map_err(|_| "Invalid public key bytes")?;
+
+ let raw_sig: RawSignature =
+ sig.as_slice().try_into().map_err(|_| "Invalid length signature")?;
+ let signature = E::Signature::from_bytes(&raw_sig);
+
+ pub_key.verify_strict(msg.as_slice(), &signature).map_err(|_| "Signature verification failed")
+}
+
/// Runs the RFC specified test vectors against an Ed25519 implementation
pub fn run_rfc_test_vectors<E>()
where
@@ -126,7 +138,10 @@ where
{
let private_key_bytes: [u8; 32] =
private_key.as_slice().try_into().expect("Secret key is the wrong length");
- let kp = E::KeyPair::from_private_key(&private_key_bytes);
+
+ // Permits: Test-only code, not a production leak of the private key
+ let permit = RawPrivateKeyPermit::default();
+ let kp = E::KeyPair::from_raw_private_key(&private_key_bytes, &permit);
let sig_result = kp.sign(msg.as_slice());
(sig.as_slice() == sig_result.to_bytes()).then_some(()).ok_or("sig not matching expected")?;
diff --git a/nearby/crypto/crypto_provider_test/src/lib.rs b/nearby/crypto/crypto_provider_test/src/lib.rs
index 8e63f44..2209a3e 100644
--- a/nearby/crypto/crypto_provider_test/src/lib.rs
+++ b/nearby/crypto/crypto_provider_test/src/lib.rs
@@ -36,9 +36,11 @@ pub mod x25519;
/// Common items that needs to be imported to use these test cases
pub mod prelude {
pub use super::CryptoProviderTestCase;
+ pub use ::rstest;
pub use rstest::rstest;
pub use rstest_reuse;
pub use rstest_reuse::apply;
+ pub extern crate std;
}
/// A test case for Crypto Provider. A test case is a function that panics if the test fails.
diff --git a/nearby/crypto/crypto_provider_test/src/p256.rs b/nearby/crypto/crypto_provider_test/src/p256.rs
index 9869c9e..5ebbd50 100644
--- a/nearby/crypto/crypto_provider_test/src/p256.rs
+++ b/nearby/crypto/crypto_provider_test/src/p256.rs
@@ -12,12 +12,12 @@
// See the License for the specific language governing permissions and
// limitations under the License.
-extern crate std;
use crate::elliptic_curve::EphemeralSecretForTesting;
pub use crate::prelude::*;
use crate::TestError;
use core::marker::PhantomData;
-use crypto_provider::p256::{P256PublicKey, P256};
+use core::ops::Deref;
+use crypto_provider::p256::{P256PublicKey, PointCompression, P256};
use crypto_provider::{
elliptic_curve::{EcdhProvider, EphemeralSecret, PublicKey},
CryptoRng,
@@ -62,9 +62,35 @@ pub fn to_bytes_test<E: EcdhProviderForP256Test>(_: PhantomData<E>) {
8bbe76c6dc1643088107636deff8aa79e8002a157b92"
);
let key = E::PublicKey::from_sec1_bytes(&sec1_bytes).unwrap();
+ // Not part of the API contract, but `to_bytes()` should prefer to use uncompressed
+ // representation since support for compressed point is optional.
+ let key_bytes = key.to_bytes();
+ assert_eq!(sec1_bytes.to_vec(), key_bytes.deref());
+}
+
+/// Test for P256PublicKey::to_sec1_bytes(Compressed). Support for compressed representation is
+/// optional.
+pub fn to_bytes_compressed_test<E: EcdhProviderForP256Test>(_: PhantomData<E>) {
+ let sec1_bytes = hex!(
+ "04756c07ba5b596fa96c9099e6619dc62deac4297a8fc1d803d74dc5caa9197c09f0b6da270d2a58a06022
+ 8bbe76c6dc1643088107636deff8aa79e8002a157b92"
+ );
+ let key = E::PublicKey::from_sec1_bytes(&sec1_bytes).unwrap();
+ let key_bytes = key.to_sec1_bytes(PointCompression::Compressed);
let sec1_bytes_compressed =
hex!("02756c07ba5b596fa96c9099e6619dc62deac4297a8fc1d803d74dc5caa9197c09");
- assert_eq!(sec1_bytes_compressed.to_vec(), key.to_bytes());
+ assert_eq!(sec1_bytes_compressed.to_vec(), key_bytes.deref());
+}
+
+/// Test for P256PublicKey::to_sec1_bytes(Uncompressed)
+pub fn to_bytes_uncompressed_test<E: EcdhProviderForP256Test>(_: PhantomData<E>) {
+ let sec1_bytes = hex!(
+ "04756c07ba5b596fa96c9099e6619dc62deac4297a8fc1d803d74dc5caa9197c09f0b6da270d2a58a06022
+ 8bbe76c6dc1643088107636deff8aa79e8002a157b92"
+ );
+ let key = E::PublicKey::from_sec1_bytes(&sec1_bytes).unwrap();
+ let key_bytes = key.to_sec1_bytes(PointCompression::Uncompressed);
+ assert_eq!(sec1_bytes.to_vec(), key_bytes.deref());
}
/// Random test for P256PublicKey::to_bytes
@@ -75,7 +101,7 @@ pub fn to_bytes_random_test<E: EcdhProviderForP256Test>(_: PhantomData<E>) {
P256,
>>::Rng::new())
.public_key_bytes();
- let public_key = E::PublicKey::from_bytes(&public_key_bytes).unwrap();
+ let public_key = E::PublicKey::from_bytes(public_key_bytes.as_ref()).unwrap();
assert_eq!(
E::PublicKey::from_bytes(&public_key.to_bytes()).unwrap(),
public_key,
@@ -192,7 +218,7 @@ pub fn public_key_to_affine_coordinates_zero_top_byte_test<E: EcdhProviderForP25
/// Test for P256 Diffie-Hellman key exchange.
pub fn p256_ecdh_test<E: EcdhProviderForP256Test>(_: PhantomData<E>) {
// From wycheproof ecdh_secp256r1_ecpoint_test.json, tcId 1
- // http://google3/third_party/wycheproof/testvectors/ecdh_secp256r1_ecpoint_test.json;l=22;rcl=375894991
+ // https://github.com/google/wycheproof/blob/b063b4a/testvectors/ecdh_secp256r1_ecpoint_test.json#L22
// sec1 public key manually extracted from the ASN encoded test data
let public_key_sec1 = hex!(
"0462d5bd3372af75fe85a040715d0f502428e07046868b0bfdfa61d731afe44f
@@ -230,20 +256,24 @@ pub fn wycheproof_p256_test<E: EcdhProviderForP256Test>(_: PhantomData<E>) {
};
let result = p256_ecdh_test_impl::<E>(
&test.public_key,
- &test.private_key.try_into().expect("Private key should be 32 bytes long"),
+ &test
+ .private_key
+ .as_slice()
+ .try_into()
+ .expect("Private key should be 32 bytes long"),
);
match test.result {
wycheproof::TestResult::Valid => {
let shared_secret =
result.unwrap_or_else(|_| panic!("Test {} should succeed", test.tc_id));
- assert_eq!(test.shared_secret, shared_secret.into());
+ assert_eq!(test.shared_secret.as_slice(), shared_secret.into());
}
wycheproof::TestResult::Invalid => {
result.err().unwrap_or_else(|| panic!("Test {} should fail", test.tc_id));
}
wycheproof::TestResult::Acceptable => {
if let Ok(shared_secret) = result {
- assert_eq!(test.shared_secret, shared_secret.into());
+ assert_eq!(test.shared_secret.as_slice(), shared_secret.into());
}
// Test passes if `result` is an error because this test is "acceptable"
}
@@ -268,22 +298,43 @@ pub fn wycheproof_p256_test<E: EcdhProviderForP256Test>(_: PhantomData<E>) {
#[template]
#[export]
#[rstest]
-#[case::to_bytes(to_bytes_test)]
-#[case::to_bytes_random(to_bytes_random_test)]
-#[case::from_sec1_bytes_not_on_curve(from_sec1_bytes_not_on_curve_test)]
-#[case::from_sec1_bytes_not_on_infinity(from_sec1_bytes_at_infinity_test)]
-#[case::from_affine_coordinates(from_affine_coordinates_test)]
-#[case::from_affine_coordinates_not_on_curve(from_affine_coordinates_not_on_curve_test)]
-#[case::public_key_to_affine_coordinates(public_key_to_affine_coordinates_test)]
+#[case::to_bytes(to_bytes_test, "to_bytes")]
+#[case::to_bytes_compressed(to_bytes_compressed_test, "to_bytes_compressed")]
+#[case::to_bytes_uncompressed(to_bytes_uncompressed_test, "to_bytes_uncompressed")]
+#[case::to_bytes_random(to_bytes_random_test, "to_bytes_random")]
+#[case::from_sec1_bytes_not_on_curve(
+ from_sec1_bytes_not_on_curve_test,
+ "from_sec1_bytes_not_on_curve"
+)]
+#[case::from_sec1_bytes_not_on_infinity(
+ from_sec1_bytes_at_infinity_test,
+ "from_sec1_bytes_not_on_infinity"
+)]
+#[case::from_affine_coordinates(from_affine_coordinates_test, "from_affine_coordinates")]
+#[case::from_affine_coordinates_not_on_curve(
+ from_affine_coordinates_not_on_curve_test,
+ "from_affine_coordinates_not_on_curve"
+)]
+#[case::public_key_to_affine_coordinates(
+ public_key_to_affine_coordinates_test,
+ "public_key_to_affine_coordinates"
+)]
#[case::public_key_to_affine_coordinates_compressed02(
- public_key_to_affine_coordinates_compressed02_test
+ public_key_to_affine_coordinates_compressed02_test,
+ "public_key_to_affine_coordinates_compressed02"
)]
#[case::public_key_to_affine_coordinates_compressed03(
- public_key_to_affine_coordinates_compressed03_test
+ public_key_to_affine_coordinates_compressed03_test,
+ "public_key_to_affine_coordinates_compressed03"
)]
#[case::public_key_to_affine_coordinates_zero_top_byte(
- public_key_to_affine_coordinates_zero_top_byte_test
+ public_key_to_affine_coordinates_zero_top_byte_test,
+ "public_key_to_affine_coordinates_zero_top_byte"
)]
-#[case::p256_ecdh(p256_ecdh_test)]
-#[case::wycheproof_p256(wycheproof_p256_test)]
-fn p256_test_cases<C: CryptoProvider>(#[case] testcase: CryptoProviderTestCase<C>) {}
+#[case::p256_ecdh(p256_ecdh_test, "p256_ecdh")]
+#[case::wycheproof_p256(wycheproof_p256_test, "wycheproof_p256")]
+fn p256_test_cases<C: CryptoProvider>(
+ #[case] testcase: CryptoProviderTestCase<C>,
+ #[case] name: &str,
+) {
+}
diff --git a/nearby/crypto/crypto_provider_test/src/sha2.rs b/nearby/crypto/crypto_provider_test/src/sha2.rs
index 330f284..66a3025 100644
--- a/nearby/crypto/crypto_provider_test/src/sha2.rs
+++ b/nearby/crypto/crypto_provider_test/src/sha2.rs
@@ -14,7 +14,7 @@
extern crate alloc;
extern crate std;
-pub use crate::prelude::*;
+use crate::prelude::*;
use crate::CryptoProvider;
use alloc::vec::Vec;
use core::{marker::PhantomData, str::FromStr};
diff --git a/nearby/crypto/crypto_provider_test/src/testdata/ecdsa/rfc_test_vectors.txt b/nearby/crypto/crypto_provider_test/src/testdata/EdDSA/rfc_test_vectors.txt
index 0dcd164..0dcd164 100644
--- a/nearby/crypto/crypto_provider_test/src/testdata/ecdsa/rfc_test_vectors.txt
+++ b/nearby/crypto/crypto_provider_test/src/testdata/EdDSA/rfc_test_vectors.txt
diff --git a/nearby/crypto/crypto_provider_test/src/x25519.rs b/nearby/crypto/crypto_provider_test/src/x25519.rs
index 0fcaa12..1fe2019 100644
--- a/nearby/crypto/crypto_provider_test/src/x25519.rs
+++ b/nearby/crypto/crypto_provider_test/src/x25519.rs
@@ -58,7 +58,7 @@ where
pub fn x25519_to_bytes_test<E: EcdhProviderForX25519Test>(_: PhantomData<E>) {
let public_key_bytes = hex!("504a36999f489cd2fdbc08baff3d88fa00569ba986cba22548ffde80f9806829");
let public_key = E::PublicKey::from_bytes(&public_key_bytes).unwrap();
- assert_eq!(public_key_bytes.to_vec(), public_key.to_bytes());
+ assert_eq!(public_key_bytes.to_vec(), public_key.to_bytes().as_ref());
}
/// Random test for `PublicKey<X25519>::to_bytes`
@@ -69,9 +69,9 @@ pub fn x25519_to_bytes_random_test<E: EcdhProviderForX25519Test>(_: PhantomData<
X25519,
>>::Rng::new())
.public_key_bytes();
- let public_key = E::PublicKey::from_bytes(&public_key_bytes).unwrap();
+ let public_key = E::PublicKey::from_bytes(public_key_bytes.as_ref()).unwrap();
assert_eq!(
- E::PublicKey::from_bytes(&public_key.to_bytes()).unwrap(),
+ E::PublicKey::from_bytes(public_key.to_bytes().as_ref()).unwrap(),
public_key,
"from_bytes should return the same key for `{public_key_bytes:?}`",
);
@@ -81,7 +81,7 @@ pub fn x25519_to_bytes_random_test<E: EcdhProviderForX25519Test>(_: PhantomData<
/// Test for X25519 Diffie-Hellman key exchange.
pub fn x25519_ecdh_test<E: EcdhProviderForX25519Test>(_: PhantomData<E>) {
// From wycheproof ecdh_secx25519r1_ecpoint_test.json, tcId 1
- // http://google3/third_party/wycheproof/testvectors/ecdh_secx25519r1_ecpoint_test.json;l=22;rcl=375894991
+ // https://github.com/google/wycheproof/blob/b063b4a/testvectors/x25519_test.json#L23
// sec1 public key manually extracted from the ASN encoded test data
let public_key = hex!("504a36999f489cd2fdbc08baff3d88fa00569ba986cba22548ffde80f9806829");
let private = hex!("c8a9d5a91091ad851c668b0736c1c9a02936c0d3ad62670858088047ba057475");
@@ -109,20 +109,24 @@ pub fn wycheproof_x25519_test<E: EcdhProviderForX25519Test>(_: PhantomData<E>) {
for test in test_group.tests {
let result = x25519_ecdh_test_impl::<E>(
&test.public_key,
- &test.private_key.try_into().expect("Private keys should be 32 bytes long"),
+ &test
+ .private_key
+ .as_slice()
+ .try_into()
+ .expect("Private keys should be 32 bytes long"),
);
match test.result {
wycheproof::TestResult::Valid => {
let shared_secret =
result.unwrap_or_else(|_| panic!("Test {} should succeed", test.tc_id));
- assert_eq!(&test.shared_secret, &shared_secret.into());
+ assert_eq!(&test.shared_secret.as_slice(), &shared_secret.into());
}
wycheproof::TestResult::Invalid => {
result.err().unwrap_or_else(|| panic!("Test {} should fail", test.tc_id));
}
wycheproof::TestResult::Acceptable => {
if let Ok(shared_secret) = result {
- assert_eq!(test.shared_secret, shared_secret.into());
+ assert_eq!(test.shared_secret.as_slice(), shared_secret.into());
}
// Test passes if `result` is an error because this test is "acceptable"
}
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 @@ version.workspace = true
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/CMakeLists.txt b/nearby/presence/CMakeLists.txt
index 15143a1..9670c34 100644
--- a/nearby/presence/CMakeLists.txt
+++ b/nearby/presence/CMakeLists.txt
@@ -17,7 +17,7 @@ cmake_minimum_required(VERSION 3.14)
project(NearbyProtocol)
# required for designated initializers on MSVC
-set(CMAKE_CXX_STANDARD 17)
+set(CMAKE_CXX_STANDARD 20)
# root directory of repo
set(BETO_CORE_ROOT ${CMAKE_SOURCE_DIR}/../..)
@@ -32,15 +32,6 @@ if (UNIX)
set(CMAKE_CXX_FLAGS_DEBUG "-g ${CMAKE_C_FLAGS_DEBUG}")
endif ()
-if (UNIX)
- add_compile_options(-Wall -Wextra -Wimplicit-fallthrough -Wextra-semi
- -Wno-missing-field-initializers -Wno-unused-parameter -Wno-psabi
- -Wshadow
- -Wsign-compare)
-elseif (MSVC)
- add_compile_options(-W4 -O1 -MD)
-endif ()
-
find_package(OpenSSL REQUIRED)
if (OPENSSL_FOUND)
message(STATUS "OpenSSL Found: ${OPENSSL_VERSION}")
@@ -48,6 +39,10 @@ if (OPENSSL_FOUND)
message(STATUS "OpenSSL Libraries: ${OPENSSL_LIBRARIES}")
endif ()
+if (MSVC)
+ add_compile_options(-W4 -O1 -MD)
+endif ()
+
if (ENABLE_TESTS)
message(STATUS "Enabling workspace wide tests")
@@ -62,8 +57,8 @@ if (ENABLE_TESTS)
enable_testing()
include(GoogleTest)
- # Find GoogleBenchmark
- find_package(benchmark REQUIRED)
+ # Include google benchmark
+ add_subdirectory(${THIRD_PARTY_DIR}/benchmark ${THIRD_PARTY_DIR}/benchmark/build)
# Setup jsoncpp
set(JSONCPP_DIR ${THIRD_PARTY_DIR}/jsoncpp)
@@ -74,6 +69,15 @@ if (ENABLE_TESTS)
)
endif ()
+# Add these compile options for our own code, not needed for the above deps
+if (UNIX)
+ add_compile_options(-Wall -Wextra -Wimplicit-fallthrough -Wextra-semi
+ -Wno-missing-field-initializers -Wno-unused-parameter -Wno-psabi
+ -Wshadow
+ -Wsign-compare)
+endif()
+
+
add_subdirectory(np_cpp_ffi)
add_subdirectory(ldt_np_c_sample)
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 @@ version.workspace = true
edition.workspace = true
publish.workspace = true
+[lints]
+workspace = true
+
[features]
default = []
std = []
-[dependencies]
diff --git a/nearby/presence/array_ref/README.md b/nearby/presence/array_ref/README.md
new file mode 100644
index 0000000..2ff0748
--- /dev/null
+++ b/nearby/presence/array_ref/README.md
@@ -0,0 +1,4 @@
+This crate was originally forked from crates.io: https://github.com/droundy/arrayref
+
+This fork removes unsafe code which is no longer needed in stable rust while
+keeping the behavior the same. The owner of the upstream repo has been unresponsive with regards to landing these changes upstream ie: https://github.com/droundy/arrayref/pull/24 \ No newline at end of file
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 @@ version.workspace = true
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/handle_map/src/lib.rs b/nearby/presence/handle_map/src/lib.rs
deleted file mode 100644
index 745aec4..0000000
--- a/nearby/presence/handle_map/src/lib.rs
+++ /dev/null
@@ -1,608 +0,0 @@
-// Copyright 2023 Google LLC
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-// http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-//! A thread-safe implementation of a map for managing object handles,
-//! a safer alternative to raw pointers for FFI interop.
-#![cfg_attr(not(test), no_std)]
-#![forbid(unsafe_code)]
-#![deny(missing_docs)]
-extern crate alloc;
-extern crate core;
-
-use alloc::boxed::Box;
-use alloc::vec::Vec;
-use core::ops::{Deref, DerefMut};
-use core::sync::atomic::Ordering;
-use hashbrown::hash_map::EntryRef;
-use portable_atomic::{AtomicU32, AtomicU64};
-use spin::lock_api::*;
-
-#[cfg(test)]
-mod tests;
-
-/// A RAII read lock guard for an object in a [`HandleMap`]
-/// pointed-to by a given [`Handle`]. When this struct is
-/// dropped, the underlying read lock on the associated
-/// shard will be dropped.
-pub struct ObjectReadGuard<'a, T> {
- guard: lock_api::MappedRwLockReadGuard<'a, spin::RwLock<()>, T>,
-}
-
-impl<'a, T> Deref for ObjectReadGuard<'a, T> {
- type Target = T;
-
- fn deref(&self) -> &Self::Target {
- self.guard.deref()
- }
-}
-
-/// A RAII read-write lock guard for an object in a [`HandleMap`]
-/// pointed-to by a given [`Handle`]. When this struct is
-/// dropped, the underlying read-write lock on the associated
-/// shard will be dropped.
-pub struct ObjectReadWriteGuard<'a, T> {
- guard: lock_api::MappedRwLockWriteGuard<'a, spin::RwLock<()>, T>,
-}
-
-impl<'a, T> Deref for ObjectReadWriteGuard<'a, T> {
- type Target = T;
-
- fn deref(&self) -> &Self::Target {
- self.guard.deref()
- }
-}
-
-impl<'a, T> DerefMut for ObjectReadWriteGuard<'a, T> {
- fn deref_mut(&mut self) -> &mut Self::Target {
- self.guard.deref_mut()
- }
-}
-
-/// FFI-transmissible structure expressing the dimensions
-/// (max # of allocatable slots, number of shards) of a handle-map
-/// to be used upon initialization.
-#[repr(C)]
-#[derive(Clone, Copy)]
-pub struct HandleMapDimensions {
- /// The number of shards which are employed
- /// by the associated handle-map.
- pub num_shards: u8,
- /// The maximum number of active handles which may be
- /// stored within the associated handle-map.
- pub max_active_handles: u32,
-}
-
-/// Trait for marker structs containing information about a
-/// given [`SingletonHandleMap`], including the underlying
-/// object type and the dimensions of the map.
-/// Used primarily to enforce type-level constraints
-/// on `SingletoneHandleMap`s, including
-/// ensuring that they are truly singletons.
-pub trait SingletonHandleMapInfo {
- /// The underlying kind of object pointed-to by handles
- /// derived from the associated handle-map.
- type Object: Send + Sync;
-
- /// Gets the dimensions of the corresponding handle-map.
- fn dimensions(&self) -> HandleMapDimensions;
-
- /// Gets a static reference to the global handle-map
- fn get_handle_map() -> &'static HandleMap<Self::Object>;
-}
-
-/// An individual handle to be given out by a [`HandleMap`].
-/// This representation is untyped, and just a wrapper
-/// around a handle-id, in contrast to implementors of `HandleLike`.
-#[derive(Clone, Copy, PartialEq, Eq, Hash)]
-pub struct Handle {
- handle_id: u64,
-}
-
-impl From<&Handle> for Handle {
- fn from(handle: &Handle) -> Self {
- *handle
- }
-}
-
-impl Handle {
- /// Constructs a handle wrapping the given ID.
- ///
- /// No validity checks are done on the wrapped ID
- /// to ensure that the given ID is active in
- /// any specific handle-map, and the type
- /// of the handle is not represented.
- ///
- /// As a result, this method is only useful for
- /// allowing access to handles from an FFI layer.
- pub fn from_id(handle_id: u64) -> Self {
- Self { handle_id }
- }
- /// Gets the ID for this handle.
- ///
- /// Since the underlying handle is un-typed,`
- /// this method is only suitable for
- /// transmitting handles across an FFI layer.
- pub fn get_id(&self) -> u64 {
- self.handle_id
- }
- /// Derives the shard index from the handle id
- fn get_shard_index(&self, num_shards: u8) -> usize {
- (self.handle_id % (num_shards as u64)) as usize
- }
-}
-
-/// Error raised when attempting to allocate into a full handle-map.
-#[derive(Debug)]
-pub struct HandleMapFullError;
-
-/// Internal error enum for failed allocations into a given shard.
-enum ShardAllocationError<T, F: FnOnce() -> T> {
- /// 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 raised when the entry for a given [`Handle`] doesn't exist.
-#[derive(Debug)]
-pub struct HandleNotPresentError;
-
-/// Wrapper around a `HandleMap` which is specifically tailored for maintaining
-/// a global singleton `'static` reference to a handle-map.
-/// The wrapper handles initialization of the singleton in a const-context
-/// and subsequent accesses to ensure that the underlying map is always
-/// initialized upon attempts to access it.
-pub struct SingletonHandleMap<I: SingletonHandleMapInfo> {
- info: I,
- wrapped: spin::once::Once<HandleMap<I::Object>, spin::relax::Spin>,
-}
-
-impl<I: SingletonHandleMapInfo> SingletonHandleMap<I> {
- /// Constructs a new global handle-map using the given
- /// singleton handle-map info. This method is callable
- /// from a `const`-context to allow it to be used to initialize
- /// `static` variables.
- pub const fn with_info(info: I) -> Self {
- Self { info, wrapped: spin::once::Once::new() }
- }
- /// Initialize the handle-map if it's not already initialized,
- /// and return a reference to the result.
- pub fn get(&self) -> &HandleMap<I::Object> {
- self.wrapped.call_once(|| {
- let dimensions = self.info.dimensions();
- HandleMap::with_dimensions(dimensions)
- })
- }
-}
-
-/// A thread-safe mapping from "handle"s [like pointers, but safer]
-/// to underlying structures, supporting allocations, reads, writes,
-/// and deallocations of objects behind handles.
-pub struct HandleMap<T: Send + Sync> {
- /// The dimensions of this handle-map
- dimensions: HandleMapDimensions,
-
- /// The individually-lockable "shards" of the handle-map,
- /// among which the keys will be roughly uniformly-distributed.
- handle_map_shards: Box<[HandleMapShard<T>]>,
-
- /// An atomically-incrementing counter which tracks the
- /// next handle ID which allocations will attempt to use.
- new_handle_id_counter: AtomicU64,
-
- /// An atomic integer roughly tracking the number of
- /// currently-outstanding allocated entries in this
- /// handle-map among all [`HandleMapShard`]s.
- outstanding_allocations_counter: AtomicU32,
-}
-
-impl<T: Send + Sync> HandleMap<T> {
- /// Creates a new handle-map with the given `HandleMapDimensions`.
- pub fn with_dimensions(dimensions: HandleMapDimensions) -> Self {
- let mut handle_map_shards = Vec::with_capacity(dimensions.num_shards as usize);
- for _ in 0..dimensions.num_shards {
- handle_map_shards.push(HandleMapShard::default());
- }
- let handle_map_shards = handle_map_shards.into_boxed_slice();
- Self {
- dimensions,
- handle_map_shards,
- new_handle_id_counter: AtomicU64::new(0),
- outstanding_allocations_counter: AtomicU32::new(0),
- }
- }
-}
-
-impl<T: Send + Sync> HandleMap<T> {
- /// Allocates 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 `HandleMapFullError`.
- pub fn allocate(
- &self,
- initial_value_provider: impl FnOnce() -> T,
- ) -> Result<Handle, HandleMapFullError> {
- let mut initial_value_provider = initial_value_provider;
- loop {
- // Increment the new-handle-ID counter using relaxed memory ordering,
- // since the only invariant that we want to enforce is that concurrently-running
- // threads always get distinct new handle-ids.
- let new_handle_id = self.new_handle_id_counter.fetch_add(1, Ordering::Relaxed);
- let new_handle = Handle::from_id(new_handle_id);
- 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,
- );
- match shard_allocate_result {
- Ok(_) => {
- return Ok(new_handle);
- }
- Err(ShardAllocationError::ExceedsAllocationLimit) => {
- return Err(HandleMapFullError);
- }
- Err(ShardAllocationError::EntryOccupied(thrown_back_provider)) => {
- // We need to do the whole thing again with a new ID
- initial_value_provider = thrown_back_provider;
- }
- }
- }
- }
-
- /// Gets a read-only reference to an object within the given handle-map,
- /// if the given handle is present. Otherwise, returns [`HandleNotPresentError`].
- pub fn get(&self, handle: Handle) -> Result<ObjectReadGuard<T>, HandleNotPresentError> {
- let shard_index = handle.get_shard_index(self.dimensions.num_shards);
- self.handle_map_shards[shard_index].get(handle)
- }
-
- /// Gets a read+write reference to an object within the given handle-map,
- /// if the given handle is present. Otherwise, returns [`HandleNotPresentError`].
- pub fn get_mut(
- &self,
- handle: Handle,
- ) -> Result<ObjectReadWriteGuard<T>, HandleNotPresentError> {
- let shard_index = handle.get_shard_index(self.dimensions.num_shards);
- self.handle_map_shards[shard_index].get_mut(handle)
- }
-
- /// Removes the object pointed to by the given handle in
- /// the handle-map, returning the removed object if it
- /// 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]
- .deallocate(handle, &self.outstanding_allocations_counter)
- }
-
- /// Gets the actual number of elements stored in the entire map.
- /// Only suitable for single-threaded sections of tests.
- #[cfg(test)]
- pub(crate) fn len(&self) -> usize {
- self.handle_map_shards.iter().map(|s| s.len()).sum()
- }
-
- /// Sets the new-handle-id counter to the given value.
- /// Only suitable for tests.
- #[cfg(test)]
- pub(crate) fn set_new_handle_id_counter(&mut self, value: u64) {
- self.new_handle_id_counter = AtomicU64::new(value);
- }
-}
-
-// Bunch o' type aliases to make talking about them much easier in the shard code.
-type ShardMapType<T> = hashbrown::HashMap<Handle, T>;
-type ShardReadWriteLock<T> = RwLock<ShardMapType<T>>;
-type ShardReadGuard<'a, T> = RwLockReadGuard<'a, ShardMapType<T>>;
-type ShardUpgradableReadGuard<'a, T> = RwLockUpgradableReadGuard<'a, ShardMapType<T>>;
-type ShardReadWriteGuard<'a, T> = RwLockWriteGuard<'a, ShardMapType<T>>;
-
-/// An individual handle-map shard, which is ultimately
-/// just a hash-map behind a lock.
-pub(crate) struct HandleMapShard<T: Send + Sync> {
- data: RwLock<ShardMapType<T>>,
-}
-
-impl<T: Send + Sync> Default for HandleMapShard<T> {
- fn default() -> Self {
- Self { data: RwLock::new(hashbrown::HashMap::new()) }
- }
-}
-
-impl<T: Send + Sync> HandleMapShard<T> {
- fn get(&self, handle: Handle) -> Result<ObjectReadGuard<T>, HandleNotPresentError> {
- let map_read_guard = ShardReadWriteLock::<T>::read(&self.data);
- let read_only_map_ref = map_read_guard.deref();
- if read_only_map_ref.contains_key(&handle) {
- let object_read_guard = ShardReadGuard::<T>::map(map_read_guard, move |map_ref| {
- // 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.
- map_ref.get(&handle).unwrap()
- });
- Ok(ObjectReadGuard { guard: object_read_guard })
- } else {
- // Auto-drop the read guard, and return an error
- Err(HandleNotPresentError)
- }
- }
- /// Gets a read-write guard on the entire shard map if an entry for the given
- /// handle exists, but if not, yield [`HandleNotPresentError`].
- fn get_read_write_guard_if_entry_exists(
- &self,
- handle: Handle,
- ) -> Result<ShardReadWriteGuard<T>, HandleNotPresentError> {
- // Start with an upgradable read lock and then upgrade to a write lock.
- // By doing this, we prevent new readers from entering (see `spin` documentation)
- let map_upgradable_read_guard = ShardReadWriteLock::<T>::upgradable_read(&self.data);
- let read_only_map_ref = map_upgradable_read_guard.deref();
- if read_only_map_ref.contains_key(&handle) {
- // If we know that the entry exists, and we're currently
- // holding a read-lock, we know that we're safe to request
- // an upgrade to a write lock, since only one write or
- // upgradable read lock can be outstanding at any one time.
- let map_read_write_guard =
- ShardUpgradableReadGuard::<T>::upgrade(map_upgradable_read_guard);
- Ok(map_read_write_guard)
- } else {
- // Auto-drop the read guard, we don't need to allow a write.
- Err(HandleNotPresentError)
- }
- }
-
- fn get_mut(&self, handle: Handle) -> Result<ObjectReadWriteGuard<T>, HandleNotPresentError> {
- let map_read_write_guard = self.get_read_write_guard_if_entry_exists(handle)?;
- // Expose only the pointed-to object with a mapped read-write guard
- let object_read_write_guard =
- ShardReadWriteGuard::<T>::map(map_read_write_guard, move |map_ref| {
- // Already checked that the entry exists while holding the lock
- map_ref.get_mut(&handle).unwrap()
- });
- Ok(ObjectReadWriteGuard { guard: object_read_write_guard })
- }
- fn deallocate(
- &self,
- handle: Handle,
- outstanding_allocations_counter: &AtomicU32,
- ) -> Result<T, HandleNotPresentError> {
- let mut map_read_write_guard = self.get_read_write_guard_if_entry_exists(handle)?;
- // We don't need to worry about double-decrements, since the above call
- // got us an upgradable read guard for our read, which means it's the only
- // 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();
- // 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.
- outstanding_allocations_counter.sub(1, Ordering::Release);
- Ok(removed_object)
- }
-
- fn try_allocate<F>(
- &self,
- handle: Handle,
- object_provider: F,
- outstanding_allocations_counter: &AtomicU32,
- max_active_handles: u32,
- ) -> Result<(), ShardAllocationError<T, F>>
- where
- F: FnOnce() -> T,
- {
- // Use an upgradeable read guard -> write guard to provide greater fairness
- // toward writers (see `spin` documentation)
- let map_upgradable_read_guard = ShardReadWriteLock::<T>::upgradable_read(&self.data);
- let mut map_read_write_guard =
- ShardUpgradableReadGuard::<T>::upgrade(map_upgradable_read_guard);
- match map_read_write_guard.entry_ref(&handle) {
- EntryRef::Occupied(_) => {
- // We've already allocated for that handle-id, so yield
- // the object provider back to the caller.
- Err(ShardAllocationError::EntryOccupied(object_provider))
- }
- EntryRef::Vacant(vacant_entry) => {
- // An entry is open, but we haven't yet checked the allocations count.
- // Try to increment the total allocations count atomically.
- // Use acquire ordering on a successful bump, because we don't want
- // to invoke the allocation closure before we have a guaranteed slot.
- // On the other hand, upon failure, we don't care about ordering
- // of surrounding operations, and so we use a relaxed ordering there.
- let allocation_count_bump_result = outstanding_allocations_counter.fetch_update(
- Ordering::Acquire,
- Ordering::Relaxed,
- |old_total_allocations| {
- if old_total_allocations >= max_active_handles {
- None
- } else {
- Some(old_total_allocations + 1)
- }
- },
- );
- match allocation_count_bump_result {
- Ok(_) => {
- // We're good to actually allocate
- let object = object_provider();
- vacant_entry.insert(object);
- Ok(())
- }
- Err(_) => {
- // The allocation would cause us to exceed the allowed allocations,
- // so release all locks and error.
- Err(ShardAllocationError::ExceedsAllocationLimit)
- }
- }
- }
- }
- }
- /// Gets the actual number of elements stored in this shard.
- /// Only suitable for single-threaded sections of tests.
- #[cfg(test)]
- fn len(&self) -> usize {
- let guard = ShardReadWriteLock::<T>::read(&self.data);
- guard.deref().len()
- }
-}
-
-/// Externally-facing trait for things which behave like handle-map handles
-/// with a globally-defined handle-map for the type.
-pub trait HandleLike: Sized {
- /// 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.
- fn allocate(
- initial_value_provider: impl FnOnce() -> Self::Object,
- ) -> Result<Self, HandleMapFullError>;
-
- /// Gets a RAII read-guard on the contents behind this handle.
- fn get(&self) -> Result<ObjectReadGuard<Self::Object>, HandleNotPresentError>;
-
- /// Gets a RAII read-write guard on the contents behind this handle.
- fn get_mut(&self) -> Result<ObjectReadWriteGuard<Self::Object>, HandleNotPresentError>;
-
- /// Deallocates the contents behind this handle.
- fn deallocate(self) -> Result<Self::Object, HandleNotPresentError>;
-}
-
-#[macro_export]
-/// `declare_handle_map! { handle_module_name, handle_type_name, wrapped_type,
-/// map_dimension_provider }`
-///
-/// Declares a new public module with name `handle_module_name` which includes a new type
-/// `handle_type_name` which is `#[repr(C)]` and represents FFI-accessible handles
-/// to values of type `wrapped_type`.
-///
-/// Internal to the generated module, a new static `SingletonHandleMap` is created, where the
-/// maximum number of active handles and the number of shards are given by
-/// the dimensions returned by evaluation of the `map_dimension_provider` expression.
-///
-/// Note: `map_dimension_provider` will be evaluated within the defined module's scope,
-/// so you will likely need to use `super` to refer to definitions in the enclosing scope.
-///
-/// # Example
-/// The following code defines an FFI-safe type `StringHandle` which references
-/// the `String` data-type, and uses it to define a (contrived)
-/// function `sample` which will print "Hello World".
-///
-/// ```
-/// mod sample {
-/// use core::ops::Deref;
-/// use handle_map::{declare_handle_map, HandleMapDimensions, HandleLike};
-///
-/// fn get_string_handle_map_dimensions() -> HandleMapDimensions {
-/// HandleMapDimensions {
-/// num_shards: 8,
-/// max_active_handles: 100,
-/// }
-/// }
-///
-/// declare_handle_map! { string_handle, StringHandle, String,
-/// super::get_string_handle_map_dimensions() }
-///
-/// use string_handle::StringHandle;
-///
-/// fn sample() {
-/// // Note: this method could panic if there are
-/// // more than 99 outstanding handles.
-///
-/// // Allocate a new string-handle pointing to the string "Hello"
-/// let handle = StringHandle::allocate(|| { "Hello".to_string() }).unwrap();
-/// {
-/// // Obtain a write-guard on the contents of our handle
-/// let mut handle_write_guard = handle.get_mut().unwrap();
-/// handle_write_guard.push_str(" World");
-/// // Write guard is auto-dropped at the end of this block.
-/// }
-/// {
-/// // Obtain a read-guard on the contents of our handle.
-/// // Note that we had to ensure that the write-guard was
-/// // dropped prior to doing this, or else execution
-/// // could potentially hang.
-/// let handle_read_guard = handle.get().unwrap();
-/// println!("{}", handle_read_guard.deref());
-/// }
-/// // Clean up the data behind the created handle
-/// handle.deallocate().unwrap();
-/// }
-/// }
-/// ```
-macro_rules! declare_handle_map {
- ($handle_module_name:ident, $handle_type_name:ident, $wrapped_type:ty,
- $map_dimension_provider:expr) => {
- #[doc = concat!("Macro-generated (via `handle_map::declare_handle_map!`) module",
- " which defines the `", stringify!($handle_module_name), "::",
- stringify!($handle_type_name), "` FFI-transmissible handle type ",
- " which references values of type `", stringify!($wrapped_type), "`.")]
- pub mod $handle_module_name {
- use $crate::SingletonHandleMapInfo;
-
- struct SingletonInfo;
-
- static GLOBAL_HANDLE_MAP: $crate::SingletonHandleMap<SingletonInfo> =
- $crate::SingletonHandleMap::with_info(SingletonInfo);
-
- impl $crate::SingletonHandleMapInfo for SingletonInfo {
- type Object = $wrapped_type;
-
- fn dimensions(&self) -> $crate::HandleMapDimensions {
- $map_dimension_provider
- }
- fn get_handle_map() -> &'static $crate::HandleMap<$wrapped_type> {
- GLOBAL_HANDLE_MAP.get()
- }
- }
-
- #[doc = concat!("A `#[repr(C)]` handle to a value of type `",
- stringify!($wrapped_type), "`.")]
- #[repr(C)]
- #[derive(Clone, Copy, PartialEq, Eq)]
- pub struct $handle_type_name {
- handle_id: u64,
- }
-
- impl $handle_type_name {
- fn get_as_handle(&self) -> $crate::Handle {
- $crate::Handle::from_id(self.handle_id)
- }
- }
- impl $crate::HandleLike for $handle_type_name {
- type Object = $wrapped_type;
- fn allocate(initial_value_provider: impl FnOnce() -> $wrapped_type,
- ) -> Result<Self, $crate::HandleMapFullError> {
- SingletonInfo::get_handle_map().allocate(initial_value_provider)
- .map(|derived_handle| Self {
- handle_id: derived_handle.get_id(),
- })
- }
- fn get(&self) -> Result<$crate::ObjectReadGuard<$wrapped_type>, $crate::HandleNotPresentError> {
- SingletonInfo::get_handle_map().get(self.get_as_handle())
- }
- fn get_mut(&self) -> Result<$crate::ObjectReadWriteGuard<$wrapped_type>, $crate::HandleNotPresentError> {
- SingletonInfo::get_handle_map().get_mut(self.get_as_handle())
- }
- fn deallocate(self) -> Result<$wrapped_type, $crate::HandleNotPresentError> {
- SingletonInfo::get_handle_map().deallocate(self.get_as_handle())
- }
- }
- }
- }
-}
diff --git a/nearby/presence/ldt/Cargo.toml b/nearby/presence/ldt/Cargo.toml
index c4456cc..337dd0d 100644
--- a/nearby/presence/ldt/Cargo.toml
+++ b/nearby/presence/ldt/Cargo.toml
@@ -4,6 +4,9 @@ version.workspace = true
edition.workspace = true
publish.workspace = true
+[lints]
+workspace = true
+
[features]
default = []
std = []
@@ -13,7 +16,7 @@ crypto_provider.workspace = true
ldt_tbc.workspace = true
[dev-dependencies]
-crypto_provider_default = {workspace = true}
+crypto_provider_default = { workspace = true, features = ["rustcrypto"] }
rand_ext.workspace = true
test_helper.workspace = true
xts_aes.workspace = true
@@ -21,7 +24,7 @@ xts_aes.workspace = true
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/fuzz/Cargo.lock b/nearby/presence/ldt/fuzz/Cargo.lock
index 539197a..7262f74 100644
--- a/nearby/presence/ldt/fuzz/Cargo.lock
+++ b/nearby/presence/ldt/fuzz/Cargo.lock
@@ -25,6 +25,20 @@ dependencies = [
]
[[package]]
+name = "aes-gcm"
+version = "0.10.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "831010a0f742e1209b3bcea8fab6a8e149051ba6099432c8cb2cc117dec3ead1"
+dependencies = [
+ "aead",
+ "aes",
+ "cipher",
+ "ctr",
+ "ghash",
+ "subtle",
+]
+
+[[package]]
name = "aes-gcm-siv"
version = "0.11.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -158,7 +172,7 @@ dependencies = [
name = "crypto_provider"
version = "0.1.0"
dependencies = [
- "bytes",
+ "tinyvec",
]
[[package]]
@@ -167,6 +181,7 @@ version = "0.1.0"
dependencies = [
"aead",
"aes",
+ "aes-gcm",
"aes-gcm-siv",
"cbc",
"cfg-if",
@@ -196,9 +211,9 @@ dependencies = [
[[package]]
name = "curve25519-dalek"
-version = "4.0.0-rc.3"
+version = "4.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "436ace70fc06e06f7f689d2624dc4e2f0ea666efb5aa704215f7249ae6e047a7"
+checksum = "f711ade317dd348950a9910f81c5947e3d8907ebd2b83f76203ff1807e6a2bc2"
dependencies = [
"cfg-if",
"cpufeatures",
@@ -264,14 +279,15 @@ dependencies = [
[[package]]
name = "ed25519-dalek"
-version = "2.0.0-rc.3"
+version = "2.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "faa8e9049d5d72bfc12acbc05914731b5322f79b5e2f195e9f2d705fca22ab4c"
+checksum = "1f628eaec48bfd21b865dc2950cfa014450c01d2fa2b69a86c2fd5844ec523c0"
dependencies = [
"curve25519-dalek",
"ed25519",
"rand_core",
"sha2",
+ "subtle",
]
[[package]]
@@ -332,6 +348,16 @@ dependencies = [
]
[[package]]
+name = "ghash"
+version = "0.5.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d930750de5717d2dd0b8c0d42c076c0e884c81a73e6cab859bbd2339c71e3e40"
+dependencies = [
+ "opaque-debug",
+ "polyval",
+]
+
+[[package]]
name = "group"
version = "0.13.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -553,9 +579,9 @@ checksum = "b0293b4b29daaf487284529cc2f5675b8e57c61f70167ba415a463651fd6a918"
[[package]]
name = "sha2"
-version = "0.10.7"
+version = "0.10.8"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "479fb9d862239e610720565ca91403019f2f00410f1864c5aa7479b950a76ed8"
+checksum = "793db75ad2bcafc3ffa7c68b215fee268f537982cd901d132f89c6343f3a3dc8"
dependencies = [
"cfg-if",
"cpufeatures",
@@ -586,6 +612,12 @@ dependencies = [
]
[[package]]
+name = "tinyvec"
+version = "1.6.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "87cc5ceb3875bb20c2890005a4e226a4651264a5c75edb2421b52861a0a0cb50"
+
+[[package]]
name = "typenum"
version = "1.16.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -621,9 +653,9 @@ checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423"
[[package]]
name = "x25519-dalek"
-version = "2.0.0-rc.3"
+version = "2.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "ec7fae07da688e17059d5886712c933bb0520f15eff2e09cfa18e30968f4e63a"
+checksum = "fb66477291e7e8d2b0ff1bcb900bf29489a9692816d79874bea351e7a8b6de96"
dependencies = [
"curve25519-dalek",
"rand_core",
diff --git a/nearby/presence/ldt/src/lib.rs b/nearby/presence/ldt/src/lib.rs
index f05c14b..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;
@@ -31,7 +29,6 @@ use ldt_tbc::{TweakableBlockCipherDecrypter, TweakableBlockCipherEncrypter};
/// `B` is the block size.
/// `T` is the provided implementation of a Tweakable Block Cipher
/// `M` is the implementation of a [pure mix function](https://eprint.iacr.org/2017/841.pdf)
-#[repr(C)]
pub struct LdtEncryptCipher<const B: usize, T: TweakableBlockCipher<B>, M: Mix> {
cipher_1: T::EncryptionCipher,
cipher_2: T::EncryptionCipher,
@@ -124,7 +121,7 @@ where
// Encrypt or decrypt in place with a tweak
O: Fn(&C, T::Tweak, &mut [u8; B]),
// Mix a/b into block-sized chunks
- X: Fn(&[u8], &[u8]) -> ([u8; B], [u8; B]),
+ X: for<'a, 'b> Fn(&'a [u8], &'b [u8]) -> (&'b [u8], &'a [u8]),
P: Padder<B, T>,
{
if data.len() < B || data.len() >= B * 2 {
@@ -147,20 +144,23 @@ where
// |z| = B - s, |m3| = s
let (z, m3) = m1_ciphertext.split_at(B - s);
debug_assert_eq!(s, m3.len());
+
// c3 and c2 are the last s bytes of their size-B arrays, respectively
- let (mut c3, c2) = mix(m3, m2);
+ let (c3, c2) = mix(m3, m2);
+
let c1 = {
- // constructing z || c3 is easy since c3 is already the last s bytes
- c3[0..(B - s)].copy_from_slice(z);
- let mut z_c3 = c3;
- let tweak = padder.pad_tweak(&c2[B - s..]);
+ let mut z_c3 = [0; B];
+ z_c3[(B - s)..].copy_from_slice(c3);
+ z_c3[0..(B - s)].copy_from_slice(z);
+
+ let tweak = padder.pad_tweak(c2);
cipher_op(second_cipher, tweak, &mut z_c3);
z_c3
};
- let len = data.len();
- data.get_mut(0..B).ok_or(LdtError::InvalidLength(len))?.copy_from_slice(&c1);
- data.get_mut(B..).ok_or(LdtError::InvalidLength(len))?.copy_from_slice(&c2[B - s..]);
+ let (left, right) = data.split_at_mut(B);
+ left.copy_from_slice(&c1);
+ right.copy_from_slice(c2);
Ok(())
}
@@ -219,31 +219,23 @@ pub trait Mix {
/// Mix `a` and `b`, writing into the last `s` bytes of the output arrays.
/// `a` and `b` must be the same length `s`, and no longer than the block size `B`.
/// Must be the inverse of [Mix::backwards].
- fn forwards<const B: usize>(a: &[u8], b: &[u8]) -> ([u8; B], [u8; B]);
+ fn forwards<'a, 'b>(a: &'a [u8], b: &'b [u8]) -> (&'b [u8], &'a [u8]);
/// Mix `a` and `b`, writing into the last `s` bytes of the output arrays.
/// `a` and `b` must be the same length, and no longer than the block size `B`.
/// Must be the inverse of [Mix::forwards].
- fn backwards<const B: usize>(a: &[u8], b: &[u8]) -> ([u8; B], [u8; B]);
+ fn backwards<'a, 'b>(a: &'a [u8], b: &'b [u8]) -> (&'b [u8], &'a [u8]);
}
/// Per section 2.4, swapping `a` and `b` is a valid mix function
pub struct Swap {}
impl Mix for Swap {
- fn forwards<const B: usize>(a: &[u8], b: &[u8]) -> ([u8; B], [u8; B]) {
+ fn forwards<'a, 'b>(a: &'a [u8], b: &'b [u8]) -> (&'b [u8], &'a [u8]) {
debug_assert_eq!(a.len(), b.len());
- // implies b length as well
- debug_assert!(a.len() <= B);
- let mut out1 = [0; B];
- let mut out2 = [0; B];
-
- let start = B - a.len();
- out1[start..].copy_from_slice(b);
- out2[start..].copy_from_slice(a);
- (out1, out2)
+ (b, a)
}
- fn backwards<const B: usize>(a: &[u8], b: &[u8]) -> ([u8; B], [u8; B]) {
+ fn backwards<'a, 'b>(a: &'a [u8], b: &'b [u8]) -> (&'b [u8], &'a [u8]) {
// backwards is the same as forwards.
Self::forwards(a, b)
}
@@ -262,7 +254,7 @@ pub trait Padder<const B: usize, T: TweakableBlockCipher<B>> {
fn pad_tweak(&self, data: &[u8]) -> T::Tweak;
}
-/// The default padding algorithm per section 2 of LDT paper.
+/// The default padding algorithm per section 2 of [LDT paper](https://eprint.iacr.org/2017/841.pdf)
#[derive(Default)]
pub struct DefaultPadder;
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 @@ fn aluykx_test_vectors() -> Result<(), anyhow::Error> {
);
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 @@ fn xor_pad_test_vectors() -> Result<(), anyhow::Error> {
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_max_len() {
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 @@ fn xor_pad_too_big_panics() {
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 02592ce..3f19bdd 100644
--- a/nearby/presence/ldt_np_adv/Cargo.toml
+++ b/nearby/presence/ldt_np_adv/Cargo.toml
@@ -4,6 +4,9 @@ version.workspace = true
edition.workspace = true
publish.workspace = true
+[lints]
+workspace = true
+
[features]
default = []
std = []
@@ -17,14 +20,14 @@ xts_aes.workspace = true
ldt_tbc.workspace = true
[dev-dependencies]
-crypto_provider_default.workspace = true
+crypto_provider_default = { workspace = true, features = ["rustcrypto", "std"] }
crypto_provider_openssl.workspace = true
rand_ext.workspace = true
test_helper.workspace = true
rand.workspace = true
base64.workspace = true
-serde_json = {workspace = true, features=["std"]}
+serde_json = { workspace = true, features = ["std"] }
hex.workspace = true
anyhow.workspace = true
criterion.workspace = true
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 @@ fn ldt_adv_scan<C: CryptoProvider>(c: &mut Criterion) {
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 @@ fn ldt_adv_scan<C: CryptoProvider>(c: &mut Criterion) {
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 @@ fn find_matching_item<C: CryptoProvider>(
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/fuzz/Cargo.lock b/nearby/presence/ldt_np_adv/fuzz/Cargo.lock
index 7b5ba15..3bfbfb9 100644
--- a/nearby/presence/ldt_np_adv/fuzz/Cargo.lock
+++ b/nearby/presence/ldt_np_adv/fuzz/Cargo.lock
@@ -25,6 +25,20 @@ dependencies = [
]
[[package]]
+name = "aes-gcm"
+version = "0.10.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "831010a0f742e1209b3bcea8fab6a8e149051ba6099432c8cb2cc117dec3ead1"
+dependencies = [
+ "aead",
+ "aes",
+ "cipher",
+ "ctr",
+ "ghash",
+ "subtle",
+]
+
+[[package]]
name = "aes-gcm-siv"
version = "0.11.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -162,7 +176,7 @@ dependencies = [
name = "crypto_provider"
version = "0.1.0"
dependencies = [
- "bytes",
+ "tinyvec",
]
[[package]]
@@ -171,6 +185,7 @@ version = "0.1.0"
dependencies = [
"aead",
"aes",
+ "aes-gcm",
"aes-gcm-siv",
"cbc",
"cfg-if",
@@ -200,9 +215,9 @@ dependencies = [
[[package]]
name = "curve25519-dalek"
-version = "4.0.0-rc.3"
+version = "4.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "436ace70fc06e06f7f689d2624dc4e2f0ea666efb5aa704215f7249ae6e047a7"
+checksum = "f711ade317dd348950a9910f81c5947e3d8907ebd2b83f76203ff1807e6a2bc2"
dependencies = [
"cfg-if",
"cpufeatures",
@@ -268,14 +283,15 @@ dependencies = [
[[package]]
name = "ed25519-dalek"
-version = "2.0.0-rc.3"
+version = "2.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "faa8e9049d5d72bfc12acbc05914731b5322f79b5e2f195e9f2d705fca22ab4c"
+checksum = "1f628eaec48bfd21b865dc2950cfa014450c01d2fa2b69a86c2fd5844ec523c0"
dependencies = [
"curve25519-dalek",
"ed25519",
"rand_core",
"sha2",
+ "subtle",
]
[[package]]
@@ -336,6 +352,16 @@ dependencies = [
]
[[package]]
+name = "ghash"
+version = "0.5.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d930750de5717d2dd0b8c0d42c076c0e884c81a73e6cab859bbd2339c71e3e40"
+dependencies = [
+ "opaque-debug",
+ "polyval",
+]
+
+[[package]]
name = "group"
version = "0.13.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -580,9 +606,9 @@ checksum = "b0293b4b29daaf487284529cc2f5675b8e57c61f70167ba415a463651fd6a918"
[[package]]
name = "sha2"
-version = "0.10.7"
+version = "0.10.8"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "479fb9d862239e610720565ca91403019f2f00410f1864c5aa7479b950a76ed8"
+checksum = "793db75ad2bcafc3ffa7c68b215fee268f537982cd901d132f89c6343f3a3dc8"
dependencies = [
"cfg-if",
"cpufeatures",
@@ -613,6 +639,12 @@ dependencies = [
]
[[package]]
+name = "tinyvec"
+version = "1.6.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "87cc5ceb3875bb20c2890005a4e226a4651264a5c75edb2421b52861a0a0cb50"
+
+[[package]]
name = "typenum"
version = "1.16.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -648,9 +680,9 @@ checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423"
[[package]]
name = "x25519-dalek"
-version = "2.0.0-rc.3"
+version = "2.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "ec7fae07da688e17059d5886712c933bb0520f15eff2e09cfa18e30968f4e63a"
+checksum = "fb66477291e7e8d2b0ff1bcb900bf29489a9692816d79874bea351e7a8b6de96"
dependencies = [
"curve25519-dalek",
"rand_core",
diff --git a/nearby/presence/ldt_np_adv/src/lib.rs b/nearby/presence/ldt_np_adv/src/lib.rs
index 3785797..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;
@@ -41,10 +33,13 @@ use xts_aes::XtsAes128;
/// Max LDT-XTS-AES data size: `(2 * AES block size) - 1`
pub const LDT_XTS_AES_MAX_LEN: usize = 31;
-/// Legacy (v0) format uses 14-byte metadata key
+
+/// Legacy (v0) format uses a 14-byte metadata key
pub const NP_LEGACY_METADATA_KEY_LEN: usize = 14;
-/// The salt included in an NP advertisement
+/// The salt included in an NP advertisement.
+/// LDT does not use an IV but can instead incorporate the 2 byte, regularly rotated,
+/// salt from the advertisement payload and XOR it with the padded tweak data.
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct LegacySalt {
/// Salt bytes extracted from the incoming NP advertisement
@@ -104,6 +99,20 @@ type LdtXtsAes128Decrypter<C> = LdtDecryptCipher<{ BLOCK_SIZE }, XtsAes128<C>, S
/// Decrypts and validates a NP legacy format advertisement encrypted with LDT.
///
+/// A NP legacy advertisement will always be in the format of:
+///
+/// Header (1 byte) | Identity DE header (1 byte) | Salt (2 bytes) | Identity (14 bytes) | repeated
+/// { DE header | DE payload }
+///
+/// Example:
+/// Header (1 byte) | Identity DE header (1 byte) | Salt (2 bytes) | Identity (14 bytes) |
+/// Tx power DE header (1 byte) | Tx power (1 byte) | Action DE header(1 byte) | action (1-3 bytes)
+///
+/// The ciphertext bytes will always start with the Identity through the end of the
+/// advertisement, for example in the above [ Identity (14 bytes) | Tx power DE header (1 byte) |
+/// Tx power (1 byte) | Action DE header(1 byte) | action (1-3 bytes) ] will be the ciphertext section
+/// passed as the input to `decrypt_and_verify`
+///
/// `B` is the underlying block cipher block size.
/// `O` is the max output size (must be 2 * B - 1).
/// `T` is the tweakable block cipher used by LDT.
@@ -130,6 +139,10 @@ where
///
/// If the plaintext's metadata key matches this item's MAC, return the plaintext, otherwise `None`.
///
+ /// NOTE: because LDT acts as a PRP over the entire message, tampering with any bit scrambles
+ /// the whole message, so we can leverage the MAC on just the metadata key to ensure integrity
+ /// for the whole message.
+ ///
/// # Errors
/// - If `payload` has a length outside of `[B, B * 2)`.
/// - If the decrypted plaintext fails its HMAC validation
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 @@ fn np_adv_test_vectors() -> Result<(), anyhow::Error> {
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_adv_ffi/.cargo/config-boringssl.toml b/nearby/presence/ldt_np_adv_ffi/.cargo/config-boringssl.toml
deleted file mode 100644
index 5a4a047..0000000
--- a/nearby/presence/ldt_np_adv_ffi/.cargo/config-boringssl.toml
+++ /dev/null
@@ -1,3 +0,0 @@
-paths = [
- "../../../boringssl-build/boringssl/rust/bssl-crypto",
-]
diff --git a/nearby/presence/ldt_np_adv_ffi/Cargo.lock b/nearby/presence/ldt_np_adv_ffi/Cargo.lock
index 1d13887..d8664ea 100644
--- a/nearby/presence/ldt_np_adv_ffi/Cargo.lock
+++ b/nearby/presence/ldt_np_adv_ffi/Cargo.lock
@@ -25,6 +25,20 @@ dependencies = [
]
[[package]]
+name = "aes-gcm"
+version = "0.10.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "831010a0f742e1209b3bcea8fab6a8e149051ba6099432c8cb2cc117dec3ead1"
+dependencies = [
+ "aead",
+ "aes",
+ "cipher",
+ "ctr",
+ "ghash",
+ "subtle",
+]
+
+[[package]]
name = "aes-gcm-siv"
version = "0.11.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -73,9 +87,9 @@ checksum = "8c3c1a368f70d6cf7302d78f8f7093da241fb8e8807c05cc9e51a125895a6d5b"
[[package]]
name = "bitflags"
-version = "1.3.2"
+version = "2.4.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a"
+checksum = "327762f6e5a765692301e5bb513e0d9fef63be86bbc14528052b1cd3e6f03e07"
[[package]]
name = "block-buffer"
@@ -98,6 +112,13 @@ dependencies = [
[[package]]
name = "bssl-crypto"
version = "0.1.0"
+dependencies = [
+ "bssl-sys",
+]
+
+[[package]]
+name = "bssl-sys"
+version = "0.1.0"
[[package]]
name = "bytes"
@@ -177,6 +198,9 @@ dependencies = [
[[package]]
name = "crypto_provider"
version = "0.1.0"
+dependencies = [
+ "tinyvec",
+]
[[package]]
name = "crypto_provider_boringssl"
@@ -184,7 +208,6 @@ version = "0.1.0"
dependencies = [
"bssl-crypto",
"crypto_provider",
- "crypto_provider_stubs",
]
[[package]]
@@ -204,6 +227,7 @@ version = "0.1.0"
dependencies = [
"aead",
"aes",
+ "aes-gcm",
"aes-gcm-siv",
"cbc",
"cfg-if",
@@ -240,9 +264,9 @@ dependencies = [
[[package]]
name = "curve25519-dalek"
-version = "4.0.0-rc.3"
+version = "4.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "436ace70fc06e06f7f689d2624dc4e2f0ea666efb5aa704215f7249ae6e047a7"
+checksum = "f711ade317dd348950a9910f81c5947e3d8907ebd2b83f76203ff1807e6a2bc2"
dependencies = [
"cfg-if",
"cpufeatures",
@@ -299,15 +323,16 @@ dependencies = [
[[package]]
name = "ed25519-dalek"
-version = "2.0.0-rc.3"
+version = "2.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "faa8e9049d5d72bfc12acbc05914731b5322f79b5e2f195e9f2d705fca22ab4c"
+checksum = "1f628eaec48bfd21b865dc2950cfa014450c01d2fa2b69a86c2fd5844ec523c0"
dependencies = [
"curve25519-dalek",
"ed25519",
"rand_core",
"serde",
"sha2",
+ "subtle",
"zeroize",
]
@@ -384,6 +409,16 @@ dependencies = [
]
[[package]]
+name = "ghash"
+version = "0.5.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d930750de5717d2dd0b8c0d42c076c0e884c81a73e6cab859bbd2339c71e3e40"
+dependencies = [
+ "opaque-debug",
+ "polyval",
+]
+
+[[package]]
name = "group"
version = "0.13.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -528,9 +563,9 @@ checksum = "624a8340c38c1b80fd549087862da4ba43e08858af025b236e509b6649fc13d5"
[[package]]
name = "openssl"
-version = "0.10.55"
+version = "0.10.62"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "345df152bc43501c5eb9e4654ff05f794effb78d4efe3d53abc158baddc0703d"
+checksum = "8cde4d2d9200ad5909f8dac647e29482e07c3a35de8a13fce7c9c7747ad9f671"
dependencies = [
"bitflags",
"cfg-if",
@@ -554,9 +589,9 @@ dependencies = [
[[package]]
name = "openssl-sys"
-version = "0.9.90"
+version = "0.9.98"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "374533b0e45f3a7ced10fcaeccca020e66656bc03dac384f852e4e5a7a8104a6"
+checksum = "c1665caf8ab2dc9aef43d1c0023bd904633a6a05cb30b0ad59bec2ae986e57a7"
dependencies = [
"cc",
"libc",
@@ -742,9 +777,9 @@ checksum = "d29ab0c6d3fc0ee92fe66e2d99f700eab17a8d57d1c1d3b748380fb20baa78cd"
[[package]]
name = "sec1"
-version = "0.7.2"
+version = "0.7.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "f0aec48e813d6b90b15f0b8948af3c63483992dee44c03e9930b3eebdabe046e"
+checksum = "d3e97a565f76233a6003f9f5c54be1d9c5bdfa3eccfb189469f11ec4901c47dc"
dependencies = [
"base16ct",
"der",
@@ -767,9 +802,9 @@ checksum = "9e8c8cf938e98f769bc164923b06dce91cea1751522f46f8466461af04c9027d"
[[package]]
name = "sha2"
-version = "0.10.7"
+version = "0.10.8"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "479fb9d862239e610720565ca91403019f2f00410f1864c5aa7479b950a76ed8"
+checksum = "793db75ad2bcafc3ffa7c68b215fee268f537982cd901d132f89c6343f3a3dc8"
dependencies = [
"cfg-if",
"cpufeatures",
@@ -841,6 +876,12 @@ dependencies = [
]
[[package]]
+name = "tinyvec"
+version = "1.6.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "87cc5ceb3875bb20c2890005a4e226a4651264a5c75edb2421b52861a0a0cb50"
+
+[[package]]
name = "typenum"
version = "1.16.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -882,9 +923,9 @@ checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423"
[[package]]
name = "x25519-dalek"
-version = "2.0.0-rc.3"
+version = "2.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "ec7fae07da688e17059d5886712c933bb0520f15eff2e09cfa18e30968f4e63a"
+checksum = "fb66477291e7e8d2b0ff1bcb900bf29489a9692816d79874bea351e7a8b6de96"
dependencies = [
"curve25519-dalek",
"rand_core",
diff --git a/nearby/presence/ldt_np_adv_ffi/src/lib.rs b/nearby/presence/ldt_np_adv_ffi/src/lib.rs
index b6f7a58..1534857 100644
--- a/nearby/presence/ldt_np_adv_ffi/src/lib.rs
+++ b/nearby/presence/ldt_np_adv_ffi/src/lib.rs
@@ -20,8 +20,8 @@
clippy::panic,
clippy::expect_used
)]
-// TODO: Remove usage of `lang_items` when ffi is no longer alloc
#![allow(internal_features)]
+// TODO: Remove usage of `lang_items` when ffi is no longer alloc
// These features are needed to support no_std + alloc
#![feature(lang_items)]
@@ -130,7 +130,7 @@ extern "C" fn NpLdtEncryptClose(handle: NpLdtEncryptHandle) -> i32 {
get_enc_handle_map()
.remove(&handle.handle)
.ok_or(CloseCipherError::InvalidHandle)
- .map(|_| 0)
+ .map(|_| SUCCESS)
})
}
@@ -140,14 +140,11 @@ extern "C" fn NpLdtDecryptClose(handle: NpLdtDecryptHandle) -> i32 {
get_dec_handle_map()
.remove(&handle.handle)
.ok_or(CloseCipherError::InvalidHandle)
- .map(|_| 0)
+ .map(|_| SUCCESS)
})
}
#[no_mangle]
-// continue to use LdtAdvDecrypter::encrypt() for now, but we should expose a higher level API
-// and get rid of this.
-#[allow(deprecated)]
extern "C" fn NpLdtEncrypt(
handle: NpLdtEncryptHandle,
buffer: *mut u8,
diff --git a/nearby/presence/ldt_np_c_sample/CMakeLists.txt b/nearby/presence/ldt_np_c_sample/CMakeLists.txt
index 588078a..aefc0ba 100644
--- a/nearby/presence/ldt_np_c_sample/CMakeLists.txt
+++ b/nearby/presence/ldt_np_c_sample/CMakeLists.txt
@@ -31,7 +31,6 @@ target_link_libraries(
ldt_c_sample
optimized "${CMAKE_SOURCE_DIR}/ldt_np_adv_ffi/target/release/${CMAKE_STATIC_LIBRARY_PREFIX}ldt_np_adv_ffi${CMAKE_STATIC_LIBRARY_SUFFIX}"
debug "${CMAKE_SOURCE_DIR}/ldt_np_adv_ffi/target/debug/${CMAKE_STATIC_LIBRARY_PREFIX}ldt_np_adv_ffi${CMAKE_STATIC_LIBRARY_SUFFIX}"
- OpenSSL::SSL
)
if(UNIX)
diff --git a/nearby/presence/ldt_np_c_sample/main.c b/nearby/presence/ldt_np_c_sample/main.c
index 1ae9244..9348a70 100644
--- a/nearby/presence/ldt_np_c_sample/main.c
+++ b/nearby/presence/ldt_np_c_sample/main.c
@@ -14,12 +14,12 @@
* limitations under the License.
*/
+#include "np_ldt.h"
+
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
-#include "np_ldt.h"
-
static const uint8_t KEY_SEED_BYTES[] = {204, 219, 36, 137, 233, 252, 172, 66, 179, 147, 72, 184, 148, 30, 209, 154, 29, 54, 14, 117, 224, 152, 200, 193, 94, 107, 28, 194, 182, 32, 205, 57};
static const uint8_t KNOWN_HMAC_BYTES[] = {223, 185, 10, 31, 155, 31, 226, 141, 24, 187, 204, 165, 34, 64, 181, 204, 44, 203, 95, 141, 82, 137, 163, 203, 100, 235, 53, 65, 202, 97, 75, 180};
static const uint8_t TEST_DATA_BYTES[] = {205, 104, 63, 225, 161, 209, 248, 70, 84, 61, 10, 19, 212, 174, 164, 0, 64, 200, 214, 123};
diff --git a/nearby/presence/ldt_np_c_sample/tests/CMakeLists.txt b/nearby/presence/ldt_np_c_sample/tests/CMakeLists.txt
index 6edfd63..141563d 100644
--- a/nearby/presence/ldt_np_c_sample/tests/CMakeLists.txt
+++ b/nearby/presence/ldt_np_c_sample/tests/CMakeLists.txt
@@ -25,7 +25,6 @@ target_link_libraries(
"${CMAKE_SOURCE_DIR}/ldt_np_adv_ffi/target/release/${CMAKE_STATIC_LIBRARY_PREFIX}ldt_np_adv_ffi${CMAKE_STATIC_LIBRARY_SUFFIX}"
jsoncpp
GTest::gtest_main
- OpenSSL::SSL
)
if(UNIX)
target_link_libraries(
@@ -51,7 +50,6 @@ target_link_libraries(
ldt_benchmarks
"${CMAKE_SOURCE_DIR}/ldt_np_adv_ffi/target/release/${CMAKE_STATIC_LIBRARY_PREFIX}ldt_np_adv_ffi${CMAKE_STATIC_LIBRARY_SUFFIX}"
benchmark::benchmark
- OpenSSL::SSL
)
if(UNIX)
diff --git a/nearby/presence/ldt_np_c_sample/tests/ldt_benchmarks.cc b/nearby/presence/ldt_np_c_sample/tests/ldt_benchmarks.cc
index d0cfb67..2559954 100644
--- a/nearby/presence/ldt_np_c_sample/tests/ldt_benchmarks.cc
+++ b/nearby/presence/ldt_np_c_sample/tests/ldt_benchmarks.cc
@@ -12,10 +12,11 @@
// See the License for the specific language governing permissions and
// limitations under the License.
-#include <benchmark/benchmark.h>
+#include "np_ldt.h"
+
#include <ctime>
-#include "np_ldt.h"
+#include "benchmark/benchmark.h"
using std::vector;
using std::tuple;
diff --git a/nearby/presence/ldt_np_c_sample/tests/ldt_ffi_tests.cc b/nearby/presence/ldt_np_c_sample/tests/ldt_ffi_tests.cc
index e66af65..7cd1fb3 100644
--- a/nearby/presence/ldt_np_c_sample/tests/ldt_ffi_tests.cc
+++ b/nearby/presence/ldt_np_c_sample/tests/ldt_ffi_tests.cc
@@ -12,9 +12,6 @@
// See the License for the specific language governing permissions and
// limitations under the License.
-#include <gtest/gtest.h>
-#include <json/json.h>
-
extern "C" {
#include "np_ldt.h"
}
@@ -27,6 +24,9 @@ extern "C" {
#include <pthread.h>
#endif
+#include "gtest/gtest.h"
+#include "json/json.h"
+
#ifdef LDT_TEST_VECTORS
static const char *PATH_TO_DATA_FILE = LDT_TEST_VECTORS;
#else
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 @@ version.workspace = true
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/java/LdtNpJni/AndroidManifest.xml b/nearby/presence/ldt_np_jni/java/LdtNpJni/AndroidManifest.xml
new file mode 100644
index 0000000..978460f
--- /dev/null
+++ b/nearby/presence/ldt_np_jni/java/LdtNpJni/AndroidManifest.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ Copyright 2022 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.
+-->
+
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+ package="com.google.nearby.LdtNpJni">
+ <uses-sdk android:minSdkVersion="19" />
+</manifest> \ No newline at end of file
diff --git a/nearby/presence/ldt_np_jni/java/LdtNpJni/build.gradle.kts b/nearby/presence/ldt_np_jni/java/LdtNpJni/build.gradle.kts
index 3cc0692..0f0991e 100644
--- a/nearby/presence/ldt_np_jni/java/LdtNpJni/build.gradle.kts
+++ b/nearby/presence/ldt_np_jni/java/LdtNpJni/build.gradle.kts
@@ -15,24 +15,35 @@
*/
plugins {
- id("java")
- kotlin("jvm") version "1.8.0"
+ id("java")
+ kotlin("jvm") version "1.9.0"
+}
+
+kotlin {
+ jvmToolchain {
+ languageVersion.set(JavaLanguageVersion.of("17"))
+ }
}
group = "com.google.android.gms.nearby.presence.hazmat"
version = "1.0-SNAPSHOT"
repositories {
- mavenCentral()
+ mavenCentral()
+ google()
}
dependencies {
- testImplementation("org.junit.jupiter:junit-jupiter-api:5.8.1")
- testRuntimeOnly("org.junit.jupiter:junit-jupiter-engine:5.8.1")
- implementation(kotlin("stdlib"))
+ testImplementation("org.junit.jupiter:junit-jupiter-api:5.8.1")
+ testRuntimeOnly("org.junit.jupiter:junit-jupiter-engine:5.8.1")
+ implementation(kotlin("stdlib"))
+ implementation("androidx.annotation:annotation:1.6.0")
}
tasks.getByName<Test>("test") {
- useJUnitPlatform()
- jvmArgs = mutableListOf("-Djava.library.path=../../../../target/debug")
+ useJUnitPlatform()
+ jvmArgs = mutableListOf("-Djava.library.path=../../../../target/debug")
+ testLogging {
+ events("passed", "skipped", "failed")
+ }
} \ No newline at end of file
diff --git a/nearby/presence/ldt_np_jni/java/LdtNpJni/src/main/java/com/google/android/gms/nearby/presence/hazmat/LdtNpCipher.java b/nearby/presence/ldt_np_jni/java/LdtNpJni/src/main/java/com/google/android/gms/nearby/presence/hazmat/LdtNpCipher.java
deleted file mode 100644
index ecd0a85..0000000
--- a/nearby/presence/ldt_np_jni/java/LdtNpJni/src/main/java/com/google/android/gms/nearby/presence/hazmat/LdtNpCipher.java
+++ /dev/null
@@ -1,162 +0,0 @@
-/*
- * Copyright 2022 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.
- */
-
-package com.google.android.gms.nearby.presence.hazmat;
-
-/**
- * LDT-XTS-AES128 implementation using the default "swap" mix function. It is suitable for
- * encryption or decryption of individual Nearby Presence BLE 4.2 legacy format encrypted payloads.
- *
- * <p>To avoid leaking resources, call close() once an instance is no longer needed.
- *
- * <p>This class is not thread safe.
- */
-public class LdtNpCipher implements AutoCloseable {
-
- /**
- * Size in bytes of key seed used to derive further keys used for in np ldt operations
- */
- private static final int KEY_SEED_SIZE = 32;
- /**
- * Size in bytes of the metadata keys calculated hmac tag
- */
- private static final int TAG_SIZE = 32;
- /** Block size of AES. */
- private static final int BLOCK_SIZE = 16;
-
- private final long ldtHandle;
- private boolean closed = false;
-
- private LdtNpCipher(long ldtHandle) {
- this.ldtHandle = ldtHandle;
- }
-
- /**
- * Create a new Ldt instance using LDT-XTS-AES128 with the "swap" mix function.
- *
- * @param key_seed 64-byte key material from the credential for the identity used to broadcast. The
- * supplied byte[] can be zeroed out once this method returns, as the contents are copied.
- * @return an instance configured with the supplied key
- * @throws LdtException if the key is the wrong size
- * @throws LdtException if the tag is the wrong size
- * @throws LdtException if creating the instance fails
- */
- public static LdtNpCipher fromKey(byte[] key_seed, byte[] metadata_key_tag) throws LdtException {
- if (key_seed.length != KEY_SEED_SIZE) {
- throw new LdtException("Key must be " + KEY_SEED_SIZE + " bytes");
- }
- if (metadata_key_tag.length != TAG_SIZE) {
- throw new LdtException("Tag must be " + TAG_SIZE + " bytes");
- }
-
- long handle = LdtNpJni.createLdtCipher(key_seed, metadata_key_tag);
- if (handle == 0) {
- throw new LdtException("Creating Ldt native resources failed");
- }
-
- return new LdtNpCipher(handle);
- }
-
- /**
- * Encode a 2 byte salt as a big-endian char.
- *
- * @return a char with b1 in the high bits and b2 in the low bits
- */
- public static char saltAsChar(byte b1, byte b2) {
- // byte widening conversion to int sign-extends
- int highBits = b1 << 8;
- int lowBits = b2 & 0xFF;
- // narrowing conversion truncates to low 16 bits
- return (char) (highBits | lowBits);
- }
-
- /**
- * Encrypt data in place, XORing bytes derived from the salt into the LDT tweaks.
- *
- * @param salt the salt that will be used in the advertisement with this encrypted payload. See
- * {@link LdtNpCipher#saltAsChar(byte, byte)} for constructing the char
- * representation.
- * @param data plaintext to encrypt in place: the metadata key followed by the data elements to be
- * encrypted. The length must be in [16, 31).
- * @throws IllegalStateException if this instance has already been closed
- * @throws IllegalArgumentException if data is the wrong length
- * @throws LdtException if encryption fails
- */
- public void encrypt(char salt, byte[] data) throws LdtException {
- checkPreconditions(data);
-
- int res = LdtNpJni.encrypt(ldtHandle, salt, data);
- if (res < 0) {
- // TODO is it possible for this to fail if the length is correct?
- throw new LdtException("Could not encrypt: error code " + res);
- }
- }
-
- /**
- * Decrypt the data in place, XORing the LDT tweak with the provided bytes.
- *
- * @param salt the salt extracted from the advertisement that contained this payload. See {@link
- * LdtNpCipher#saltAsChar(byte, byte)} for constructing the char representation.
- * @param data ciphertext to decrypt in place: the metadata key followed by the data elements to
- * be decrypted. The length must be in [16, 31).
- * @throws IllegalStateException if this instance has already been closed
- * @throws IllegalArgumentException if data is the wrong length
- * @throws LdtException if decryption fails
- */
- public void decrypt_and_verify(char salt, byte[] data) throws LdtException {
- checkPreconditions(data);
-
- int res = LdtNpJni.decrypt_and_verify(ldtHandle, salt, data);
- if (res < 0) {
- // TODO is it possible for this to fail if the length is correct?
- throw new LdtException("Could not decrypt: error code " + res);
- }
- }
-
- private void checkPreconditions(byte[] data) {
- if (closed) {
- throw new IllegalStateException("Instance has been closed");
- }
- if (data.length < BLOCK_SIZE || data.length >= BLOCK_SIZE * 2) {
- throw new IllegalArgumentException(
- "Data must be at least " + BLOCK_SIZE + " and less than " + BLOCK_SIZE * 2 + " bytes");
- }
- }
-
- /**
- * Releases native resources.
- *
- * <p>Once closed, an Ldt instance cannot be used further.
- */
- @Override
- public void close() {
- if (closed) {
- return;
- }
- closed = true;
-
- int res = LdtNpJni.closeLdtCipher(ldtHandle);
- if (res < 0) {
- throw new RuntimeException("Could not close Ldt: error code " + res);
- }
- }
-
- public static class LdtException extends Exception {
- LdtException(String message) {
- super(message);
- }
- }
-}
diff --git a/nearby/presence/ldt_np_jni/java/LdtNpJni/src/main/java/com/google/android/gms/nearby/presence/hazmat/LdtNpJni.java b/nearby/presence/ldt_np_jni/java/LdtNpJni/src/main/java/com/google/android/gms/nearby/presence/hazmat/LdtNpJni.java
index bbb0067..6af362a 100644
--- a/nearby/presence/ldt_np_jni/java/LdtNpJni/src/main/java/com/google/android/gms/nearby/presence/hazmat/LdtNpJni.java
+++ b/nearby/presence/ldt_np_jni/java/LdtNpJni/src/main/java/com/google/android/gms/nearby/presence/hazmat/LdtNpJni.java
@@ -15,48 +15,90 @@
*/
package com.google.android.gms.nearby.presence.hazmat;
-/** JNI for LDT-XTS-AES128 with the "swap" mix function. */
+import androidx.annotation.IntDef;
+
+
+/** JNI for a Nearby Presence LDT-XTS-AES128 cipher with the "swap" mix function. */
class LdtNpJni {
+ /** Error codes which map to return values on the native side. */
+ @IntDef({DecryptErrorCode.DATA_LEN_ERROR, DecryptErrorCode.JNI_OP_ERROR, DecryptErrorCode.MAC_MISMATCH} )
+ public @interface DecryptErrorCode {
+ int DATA_LEN_ERROR = -1;
+ int JNI_OP_ERROR = -2;
+ int MAC_MISMATCH = -3;
+ }
+
+ /** Error codes which map to return values on the native side. */
+ @IntDef({EncryptErrorCode.DATA_LEN_ERROR, EncryptErrorCode.JNI_OP_ERROR} )
+ public @interface EncryptErrorCode {
+ int DATA_LEN_ERROR = -1;
+ int JNI_OP_ERROR = -2;
+ }
+
static {
System.loadLibrary("ldt_np_jni");
}
/**
- * Create an instance of LDT-XTS-AES-128 using the Swap mix function.
+ * Create a new LDT-XTS-AES128 Encryption cipher using the "swap" mix function.
*
- * @param key key bytes, must be 4x AES key size = 64 bytes
- * @return 0 on error, and any other value for success
+ * @param keySeed is the 32-byte key material from the Nearby Presence credential from which
+ * the LDT key will be derived.
+ * @return 0 on error, or a non-zero handle on success.
+ */
+ static native long createEncryptionCipher(byte[] keySeed);
+
+ /**
+ * Create a new LDT-XTS-AES128 Decryption cipher using the "swap" mix function.
+ *
+ * @param keySeed is the 32-byte key material from the Nearby Presence credential from which
+ * the LDT key will be derived.
+ * @param hmacTag is the hmac auth tag calculated on the metadata key used to verify
+ * decryption was successful.
+ * @return 0 on error, or a non-zero handle on success.
+ */
+ static native long createDecryptionCipher(byte[] keySeed, byte[] hmacTag);
+
+ /**
+ * Close the native resources for an LdtEncryptCipher instance.
+ * @param ldtEncryptHandle a ldt handle returned from {@link LdtNpJni#createEncryptionCipher}.
*/
- static native long createLdtCipher(byte[] key_seed, byte[] metadata_key_hmac_tag);
+ static native void closeEncryptCipher(long ldtEncryptHandle);
/**
- * Close the native resources for an Ldt instance.
+ * Close the native resources for an LdtDecryptCipher instance.
*
- * @param ldtHandle An ldt handle returned from {@link LdtNpJni#createLdtCipher}.
- * @return 0 on success, <0 for any error
+ * @param ldtDecryptHandle a ldt handle returned from {@link LdtNpJni#createDecryptionCipher}.
*/
- static native int closeLdtCipher(long ldtHandle);
+ static native void closeDecryptCipher(long ldtDecryptHandle);
/**
- * Encrypt the data in place.
+ * Encrypt a 16-31 byte buffer in-place.
*
- * @param ldtHandle An ldt handle returned from {@link LdtNpJni#createLdtCipher}.
- * @param salt big-endian salt to be expanded into bytes XORd into the LDT tweaks
- * @param data size must be between 16 and 31 bytes
- * @return 0 on success, -1 if the data size is wrong, or another negative number for any other
- * error
+ * @param ldtEncryptHandle a ldt encryption handle returned from {@link LdtNpJni#createEncryptionCipher}.
+ * @param salt is the big-endian 2 byte salt that will be used in the Nearby
+ * Presence advertisement, which will be incorporated into the tweaks LDT uses
+ * while encrypting.
+ * @param data 16-31 bytes of plaintext data to be encrypted
+ * @return 0 on success, in which case `buffer` will now contain ciphertext or a non-zero
+ * an error code on failure
*/
- static native int encrypt(long ldtHandle, char salt, byte[] data);
+ @EncryptErrorCode
+ static native int encrypt(long ldtEncryptHandle, char salt, byte[] data);
/**
- * Decrypt the data in place using the default LDT tweak padding scheme.
+ * Decrypt a 16-31 byte buffer in-place and verify the plaintext metadata key matches
+ * this item's MAC, if not the buffer will not be decrypted.
*
- * @param ldtHandle An ldt address returned from {@link LdtNpJni#createLdtCipher}.
- * @param salt big-endian salt to be expanded into bytes XORd into the LDT tweaks
- * @param data size must be between 16 and 31 bytes
- * @return 0 on success, -1 if the data size is wrong, -2 if the calculated hmac
- * does not match the provided tag or another negative number for any other error
+ * @param ldtDecryptHandle a ldt encryption handle returned from {@link LdtNpJni#createDecryptionCipher}.
+ * @param salt is the big-endian 2 byte salt that will be used in the Nearby
+ * Presence advertisement, which will be incorporated into the tweaks LDT uses
+ * while encrypting.
+ * @param data 16-31 bytes of ciphertext data to be decrypted
+ * @return 0 on success, in which case `buffer` will now contain ciphertext or a non-zero
+ * an error code on failure, in which case data will remain unchanged.
*/
- static native int decrypt_and_verify(long ldtHandle, char salt, byte[] data);
+ @DecryptErrorCode
+ static native int decryptAndVerify(long ldtDecryptHandle, char salt, byte[] data);
}
diff --git a/nearby/presence/ldt_np_jni/java/LdtNpJni/src/main/java/com/google/android/gms/nearby/presence/hazmat/NpLdtCipher.kt b/nearby/presence/ldt_np_jni/java/LdtNpJni/src/main/java/com/google/android/gms/nearby/presence/hazmat/NpLdtCipher.kt
new file mode 100644
index 0000000..8f080e8
--- /dev/null
+++ b/nearby/presence/ldt_np_jni/java/LdtNpJni/src/main/java/com/google/android/gms/nearby/presence/hazmat/NpLdtCipher.kt
@@ -0,0 +1,210 @@
+/*
+ * 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.
+ */
+package com.google.android.gms.nearby.presence.hazmat
+
+import androidx.annotation.IntDef
+import com.google.android.gms.nearby.presence.hazmat.LdtNpJni.DecryptErrorCode
+import com.google.android.gms.nearby.presence.hazmat.LdtNpJni.EncryptErrorCode
+
+private const val KEY_SEED_SIZE = 32
+private const val TAG_SIZE = 32
+private const val BLOCK_SIZE = 16
+
+// Error return value for create operations
+private const val CREATE_HANDLE_ERROR = 0L
+
+// Status code returned on successful cipher operations
+private const val SUCCESS = 0
+
+/**
+ * A 2 byte salt that will be used in the advertisement with this encrypted payload.
+ */
+class Salt(b1: Byte, b2: Byte) {
+ private val saltBytes: Char
+
+ // Returns the bytes of the salt represented as a 2 byte Char type
+ internal fun getBytesAsChar(): Char {
+ return saltBytes
+ }
+
+ init {
+ // byte widening conversion to int sign-extends
+ val highBits = b1.toInt() shl 8
+ val lowBits = b2.toInt() and 0xFF
+ // narrowing conversion truncates to low 16 bits
+ saltBytes = (highBits or lowBits).toChar()
+ }
+}
+
+/** A runtime exception thrown if something fails during a Ldt jni call*/
+class LdtJniException internal constructor(message: String?) : RuntimeException(message)
+
+/**
+ * A checked exception which occurs if the calculated hmac tag does not match the expected hmac
+ * tag during decrypt operations
+ */
+class MacMismatchException internal constructor(message: String?) : Exception(message)
+
+/**
+ * Create a new LdtEncryptionCipher instance using LDT-XTS-AES128 with the "swap" mix function.
+ *
+ * @constructor Creates a new instance from the provided keySeed
+ * @param keySeed is the key material from the Nearby Presence credential from which
+ * the LDT key will be derived.
+ * @return an instance configured with the supplied key seed
+ * @throws IllegalArgumentException if the keySeed is the wrong size
+ * @throws LdtJniException if creating the instance fails
+ */
+class LdtEncryptionCipher @Throws(
+ LdtJniException::class,
+ IllegalArgumentException::class
+) constructor(keySeed: ByteArray) :
+ AutoCloseable {
+ @Volatile
+ private var closed = false
+ private val handle: Long
+
+ init {
+ require(keySeed.size == KEY_SEED_SIZE)
+ handle = LdtNpJni.createEncryptionCipher(keySeed)
+ if (handle == CREATE_HANDLE_ERROR) {
+ throw LdtJniException("Creating ldt encryption cipher native resources failed")
+ }
+ }
+
+ /**
+ * Encrypt a 16-31 byte buffer in-place.
+ *
+ * @param salt the salt that will be used in the advertisement with this encrypted payload.
+ * @param data plaintext to encrypt in place: the metadata key followed by the data elements to be
+ * encrypted. The length must be in [16, 31).
+ * @throws IllegalStateException if this instance has already been closed
+ * @throws IllegalArgumentException if data is the wrong length
+ * @throws LdtJniException if encryption fails
+ */
+ @Throws(LdtJniException::class, IllegalArgumentException::class, IllegalStateException::class)
+ fun encrypt(salt: Salt, data: ByteArray) {
+ check(!closed) { "Use after free! This instance has already been closed" }
+ requireValidSizeData(data.size)
+
+ when (val res = LdtNpJni.encrypt(handle, salt.getBytesAsChar(), data)) {
+ EncryptErrorCode.JNI_OP_ERROR -> throw LdtJniException("Error during jni encrypt operation: error code $res");
+ EncryptErrorCode.DATA_LEN_ERROR -> check(false) // this will never happen, lengths checked above
+ else -> check(res == SUCCESS)
+ }
+ }
+
+ /**
+ * Releases native resources.
+ *
+ * <p>Once closed, a Ldt instance cannot be used further.
+ */
+ override fun close() {
+ if (!closed) {
+ closed = true
+ LdtNpJni.closeEncryptCipher(handle)
+ }
+ }
+}
+
+/**
+ * A LdtDecryptionCipher instance which uses LDT-XTS-AES128 with the "swap" mix function.
+ *
+ * @constructor Creates a new instance from the provided keySeed and hmacTag
+ * @param keySeed is the key material from the Nearby Presence credential from which
+ * the LDT key will be derived.
+ * @param hmacTag is the hmac auth tag calculated on the metadata key used to verify
+ * decryption was successful.
+ * @return an instance configured with the supplied key seed
+ * @throws IllegalArgumentException if the keySeed is the wrong size
+ * @throws IllegalArgumentException if the hmacTag is the wrong size
+ * @throws LdtJniException if creating the instance fails
+ */
+class LdtDecryptionCipher @Throws(
+ LdtJniException::class,
+ IllegalArgumentException::class
+) constructor(keySeed: ByteArray, hmacTag: ByteArray) : AutoCloseable {
+ @Volatile
+ private var closed = false
+ private val handle: Long
+
+ init {
+ require(keySeed.size == KEY_SEED_SIZE)
+ require(hmacTag.size == TAG_SIZE)
+ handle = LdtNpJni.createDecryptionCipher(keySeed, hmacTag)
+ if (handle == CREATE_HANDLE_ERROR) {
+ throw LdtJniException("Creating ldt decryption cipher native resources failed")
+ }
+ }
+
+ /** Error codes which map to return values on the native side. */
+ @IntDef(DecryptAndVerifyResultCode.SUCCESS, DecryptAndVerifyResultCode.MAC_MISMATCH)
+ annotation class DecryptAndVerifyResultCode {
+ companion object {
+ const val SUCCESS = 0
+ const val MAC_MISMATCH = -1
+ }
+ }
+
+ /**
+ * Decrypt a 16-31 byte buffer in-place and verify the plaintext metadata key matches
+ * this item's MAC, if not the buffer will not be decrypted.
+ *
+ * @param salt the salt extracted from the advertisement that contained this payload.
+ * @param data ciphertext to decrypt in place: the metadata key followed by the data elements to
+ * be decrypted. The length must be in [16, 31).
+ * @return a [DecryptAndVerifyResultCode] indicating of the decrypt operation failed or succeeded.
+ * In the case of a failed decrypt, the provided plaintext will not change.
+ * @throws IllegalStateException if this instance has already been closed
+ * @throws IllegalArgumentException if data is the wrong length
+ * @throws LdtJniException if decryption fails
+ */
+ @Throws(
+ IllegalStateException::class,
+ IllegalArgumentException::class,
+ LdtJniException::class
+ )
+ @DecryptAndVerifyResultCode
+ fun decryptAndVerify(salt: Salt, data: ByteArray): Int {
+ check(!closed) { "Double free! Close should only ever be called once" }
+ requireValidSizeData(data.size)
+
+ when (val res = LdtNpJni.decryptAndVerify(handle, salt.getBytesAsChar(), data)) {
+ DecryptErrorCode.MAC_MISMATCH -> return DecryptAndVerifyResultCode.MAC_MISMATCH
+ DecryptErrorCode.DATA_LEN_ERROR -> check(false); // This condition is impossible, we validated data length above
+ DecryptErrorCode.JNI_OP_ERROR -> throw LdtJniException("Error occurred during jni encrypt operation")
+ else -> check(res == SUCCESS)
+ }
+ return DecryptAndVerifyResultCode.SUCCESS
+ }
+
+ /**
+ * Releases native resources.
+ *
+ * <p>Once closed, a Ldt instance cannot be used further.
+ */
+ override fun close() {
+ if (!closed) {
+ closed = true
+ LdtNpJni.closeEncryptCipher(handle)
+ }
+ }
+}
+
+private fun requireValidSizeData(size: Int) {
+ require(size >= BLOCK_SIZE && size < BLOCK_SIZE * 2) { "Invalid size data: $size" }
+}
+
diff --git a/nearby/presence/ldt_np_jni/java/LdtNpJni/src/test/java/com/google/android/gms/nearby/presence/hazmat/LdtNpJniTests.kt b/nearby/presence/ldt_np_jni/java/LdtNpJni/src/test/java/com/google/android/gms/nearby/presence/hazmat/LdtNpJniTests.kt
index dc861c6..07d29a3 100644
--- a/nearby/presence/ldt_np_jni/java/LdtNpJni/src/test/java/com/google/android/gms/nearby/presence/hazmat/LdtNpJniTests.kt
+++ b/nearby/presence/ldt_np_jni/java/LdtNpJni/src/test/java/com/google/android/gms/nearby/presence/hazmat/LdtNpJniTests.kt
@@ -16,27 +16,143 @@
package com.google.android.gms.nearby.presence.hazmat
-import org.junit.jupiter.api.Assertions
+import org.junit.jupiter.api.Assertions.assertArrayEquals
+import org.junit.jupiter.api.Assertions.assertEquals
import org.junit.jupiter.api.Test
+import org.junit.jupiter.api.assertThrows
+
+const val KEY_SEED = "CCDB2489E9FCAC42B39348B8941ED19A1D360E75E098C8C15E6B1CC2B620CD39"
+const val HMAC_TAG = "DFB90A1F9B1FE28D18BBCCA52240B5CC2CCB5F8D5289A3CB64EB3541CA614BB4"
+const val PLAINTEXT = "CD683FE1A1D1F846543D0A13D4AEA40040C8D67B"
+const val SALT_BYTES = "32EE"
+const val EXPECTED_CIPHER_TEXT = "04344411F1E57C841FE0F7150636BC782455059A"
class LdtNpJniTests {
@Test
- fun ldtRoundTripTest() {
+ fun roundTripTest() {
// Data taken from first test case in ldt_np_adv/resources/test/np_adv_test_vectors.json
- val key_seed = "CCDB2489E9FCAC42B39348B8941ED19A1D360E75E098C8C15E6B1CC2B620CD39".decodeHex()
- val hmac_tag = "DFB90A1F9B1FE28D18BBCCA52240B5CC2CCB5F8D5289A3CB64EB3541CA614BB4".decodeHex()
- val plaintext = "CD683FE1A1D1F846543D0A13D4AEA40040C8D67B".decodeHex()
- val salt_bytes = "32EE".decodeHex()
- val expected_ciphertext = "04344411F1E57C841FE0F7150636BC782455059A".decodeHex()
- val salt = LdtNpCipher.saltAsChar(salt_bytes[0], salt_bytes[1])
+ val keySeed = KEY_SEED.decodeHex()
+ val hmacTag = HMAC_TAG.decodeHex()
+ val plaintext = PLAINTEXT.decodeHex()
+ val saltBytes = SALT_BYTES.decodeHex()
+ val expectedCiphertext = EXPECTED_CIPHER_TEXT.decodeHex()
+ val salt = Salt(saltBytes[0], saltBytes[1])
val data = plaintext.copyOf()
- val LdtCipher = LdtNpCipher.fromKey(key_seed, hmac_tag)
- LdtCipher.encrypt(salt, data)
- Assertions.assertArrayEquals(expected_ciphertext, data)
+ val encryptionCipher = LdtEncryptionCipher(keySeed)
+ encryptionCipher.encrypt(salt, data)
+ assertArrayEquals(expectedCiphertext, data)
+ encryptionCipher.close()
+
+
+ val decryptionCipher = LdtDecryptionCipher(keySeed, hmacTag)
+ val result = decryptionCipher.decryptAndVerify(salt, data)
+ assertEquals(LdtDecryptionCipher.DecryptAndVerifyResultCode.SUCCESS, result)
+ assertArrayEquals(plaintext, data)
+ decryptionCipher.close()
+ }
+
+ @Test
+ fun createEncryptionCipherInvalidLength() {
+ assertThrows<IllegalArgumentException> {
+ val keySeed = ByteArray(31)
+ LdtEncryptionCipher(keySeed)
+ }
+
+ assertThrows<IllegalArgumentException> {
+ val keySeed = ByteArray(33)
+ LdtEncryptionCipher(keySeed)
+ }
+ }
+
+ @Test
+ fun encryptInvalidLengthData() {
+ val keySeed = KEY_SEED.decodeHex()
+ val cipher = LdtEncryptionCipher(keySeed)
+ assertThrows<IllegalArgumentException> {
+ var data = ByteArray(15)
+ cipher.encrypt(Salt(0x0, 0x0), data)
+ }
+ assertThrows<IllegalArgumentException> {
+ var data = ByteArray(32)
+ cipher.encrypt(Salt(0x0, 0x0), data)
+ }
+ }
+
+ @Test
+ fun encryptUseAfterClose() {
+ val keySeed = KEY_SEED.decodeHex()
+ val cipher = LdtEncryptionCipher(keySeed)
+ val data = ByteArray(20)
+ cipher.close()
+ assertThrows<IllegalStateException> { cipher.encrypt(Salt(0x0, 0x0), data) }
+ }
+
+ @Test
+ fun createDecryptionCipherInvalidLengths() {
+ assertThrows<IllegalArgumentException> {
+ val keySeed = ByteArray(31)
+ val hmacTag = ByteArray(31)
+ LdtDecryptionCipher(keySeed, hmacTag)
+ }
+ assertThrows<IllegalArgumentException> {
+ val keySeed = ByteArray(33)
+ val hmacTag = ByteArray(33)
+ LdtDecryptionCipher(keySeed, hmacTag)
+ }
+ assertThrows<IllegalArgumentException> {
+ val keySeed = ByteArray(32)
+ val hmacTag = ByteArray(33)
+ LdtDecryptionCipher(keySeed, hmacTag)
+ }
+ assertThrows<IllegalArgumentException> {
+ val keySeed = ByteArray(33)
+ val hmacTag = ByteArray(32)
+ LdtDecryptionCipher(keySeed, hmacTag)
+ }
+ }
+
+ @Test
+ fun decryptInvalidLengthData() {
+ val keySeed = KEY_SEED.decodeHex()
+ val hmacTag = HMAC_TAG.decodeHex()
+ val cipher = LdtDecryptionCipher(keySeed, hmacTag)
+ assertThrows<IllegalArgumentException> {
+ var data = ByteArray(15)
+ cipher.decryptAndVerify(Salt(0x0, 0x0), data)
+ }
+ assertThrows<IllegalArgumentException> {
+ var data = ByteArray(32)
+ cipher.decryptAndVerify(Salt(0x0, 0x0), data)
+ }
+ }
+
+ @Test
+ fun decryptMacMismatch() {
+ val keySeed = KEY_SEED.decodeHex()
+ val hmacTag = HMAC_TAG.decodeHex()
+
+ // alter first byte in the hmac tag
+ hmacTag[0] = 0x00
+ val cipher = LdtDecryptionCipher(keySeed, hmacTag)
+
+ val cipherText = EXPECTED_CIPHER_TEXT.decodeHex()
+ val saltBytes = SALT_BYTES.decodeHex()
+ val salt = Salt(saltBytes[0], saltBytes[1])
+
+ val result = cipher.decryptAndVerify(salt, cipherText);
+ assertEquals(LdtDecryptionCipher.DecryptAndVerifyResultCode.MAC_MISMATCH, result)
+ }
+
+ @Test
+ fun decryptUseAfterClose() {
+ val keySeed = KEY_SEED.decodeHex()
+ val hmacTag = HMAC_TAG.decodeHex()
+ val cipher = LdtDecryptionCipher(keySeed, hmacTag)
+ cipher.close()
- LdtCipher.decrypt_and_verify(salt, data)
- Assertions.assertArrayEquals(plaintext, data)
+ val data = ByteArray(20)
+ assertThrows<IllegalStateException> { cipher.decryptAndVerify(Salt(0x0, 0x0), data) }
}
}
diff --git a/nearby/presence/ldt_np_jni/src/lib.rs b/nearby/presence/ldt_np_jni/src/lib.rs
index 8f7958e..e232c1e 100644
--- a/nearby/presence/ldt_np_jni/src/lib.rs
+++ b/nearby/presence/ldt_np_jni/src/lib.rs
@@ -20,18 +20,19 @@
//! - <https://www.iitk.ac.in/esc101/05Aug/tutorial/native1.1/index.html>
//! - <https://docs.oracle.com/javase/8/docs/technotes/guides/jni/spec/jniTOC.html>
+// 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;
use alloc::boxed::Box;
-use jni::objects::JByteArray;
use jni::{
- objects::JClass,
- sys::{jbyte, jbyteArray, jchar, jint, jlong},
+ objects::{JByteArray, JClass},
+ sys::{jbyte, jchar, jint, jlong},
JNIEnv,
};
@@ -45,149 +46,202 @@ use crypto_provider_default::CryptoProviderImpl;
const MIN_DATA_LEN: usize = crypto_provider::aes::BLOCK_SIZE;
const MAX_DATA_LEN: usize = crypto_provider::aes::BLOCK_SIZE * 2 - 1;
-/// Error return value for creating handles
+/// Required size constraints of input parameters
+const KEY_SEED_SIZE: usize = 32;
+const TAG_SIZE: usize = 32;
+
+/// Error return value for create operations
const CREATE_ERROR: jlong = 0;
-// TODO: don't allow panics to cross FFI boundary
-// TODO: JNI null checks? (only if jni crate isn't doing them already).
+/// Status code returned on successful cipher operations
+const SUCCESS: jint = 0;
+
+type LdtAdvDecrypter = LdtNpAdvDecrypterXtsAes128<CryptoProviderImpl>;
+type LdtAdvEncrypter = LdtEncrypterXtsAes128<CryptoProviderImpl>;
+
+/// Marker trait to ensure above types are thread safe
+trait JniThreadSafe: Send + Sync {}
+
+impl JniThreadSafe for LdtAdvDecrypter {}
+
+impl JniThreadSafe for LdtAdvEncrypter {}
+
+/// Create a LDT Encryption cipher.
+///
+/// Returns 0 on failure, or the non-zero handle as a jlong/i64 on success.
+#[no_mangle]
+extern "system" fn Java_com_google_android_gms_nearby_presence_hazmat_LdtNpJni_createEncryptionCipher(
+ env: JNIEnv,
+ _class: JClass,
+ java_key_seed: JByteArray,
+) -> jlong {
+ create_map_to_error(|| {
+ let key_seed =
+ env.convert_byte_array(&java_key_seed).map_err(|_| CREATE_ERROR).and_then(|seed| {
+ if seed.len() != KEY_SEED_SIZE {
+ Err(CREATE_ERROR)
+ } else {
+ Ok(seed)
+ }
+ })?;
+
+ let hkdf_key_seed = NpKeySeedHkdf::<CryptoProviderImpl>::new(
+ #[allow(clippy::expect_used)]
+ key_seed.as_slice().try_into().expect("Length is checked above"),
+ );
-// TODO: split this into separate APIs for encrypt and decrypt
-struct Ldt {
- ldt_enc: LdtEncrypterXtsAes128<CryptoProviderImpl>,
- ldt_dec: LdtNpAdvDecrypterXtsAes128<CryptoProviderImpl>,
+ let cipher = LdtAdvEncrypter::new(&hkdf_key_seed.legacy_ldt_key());
+ box_to_handle(cipher).map_err(|_| CREATE_ERROR)
+ })
}
-/// Create an LDT cipher.
+/// Create a LDT Decryption cipher.
///
-/// Returns 0 for error, or the pointer as a jlong/i64.
-/// Safety: We know the key pointer is safe as it is coming directly from the JVM.
+/// Returns 0 on failure, or the non-zero handle as a jlong/i64 on success.
#[no_mangle]
-#[allow(clippy::not_unsafe_ptr_arg_deref)]
-pub extern "system" fn Java_com_google_android_gms_nearby_presence_hazmat_LdtNpJni_createLdtCipher(
+extern "system" fn Java_com_google_android_gms_nearby_presence_hazmat_LdtNpJni_createDecryptionCipher(
env: JNIEnv,
_class: JClass,
- key_seed: jbyteArray,
- metadata_key_hmac_tag: jbyteArray,
+ java_key_seed: JByteArray,
+ java_hmac_tag: JByteArray,
) -> jlong {
- env.get_array_length(unsafe { &JByteArray::from_raw(key_seed) })
- .map_err(|_| CREATE_ERROR)
- // check length
- .and_then(|len| if len as usize != 32 { Err(CREATE_ERROR) } else { Ok(len) })
- // extract u8 array
- .and_then(|len| {
- let mut jbyte_buf = [jbyte::default(); 32];
- env.get_byte_array_region(
- unsafe { &JByteArray::from_raw(key_seed) },
- 0,
- &mut jbyte_buf[..],
- )
- .map_err(|_| CREATE_ERROR)
- .map(|_| (len, jbyte_array_to_u8_array(jbyte_buf)))
- })
- // initialize ldt -- we already know the key is the right length
- .and_then(|(_len, key_seed_buf)| {
- let hkdf_key_seed = NpKeySeedHkdf::new(&key_seed_buf);
- let ldt_enc = ldt_np_adv::LdtEncrypterXtsAes128::<CryptoProviderImpl>::new(
- &hkdf_key_seed.legacy_ldt_key(),
- );
-
- let mut tag_buff = [jbyte::default(); 32];
- let tag = env
- .get_byte_array_region(
- unsafe { &JByteArray::from_raw(metadata_key_hmac_tag) },
- 0,
- &mut tag_buff[..],
- )
- .map_err(|_| CREATE_ERROR)
- .map(|_| jbyte_array_to_u8_array(tag_buff))
- .unwrap();
- // TODO: Error handling
-
- let ldt_dec = ldt_np_adv::build_np_adv_decrypter_from_key_seed::<CryptoProviderImpl>(
- &hkdf_key_seed,
- tag,
- );
- box_to_handle(Ldt { ldt_enc, ldt_dec }).map_err(|_| CREATE_ERROR)
- })
- .unwrap_or_else(|e| e)
+ create_map_to_error(|| {
+ let key_seed =
+ env.convert_byte_array(&java_key_seed).map_err(|_| CREATE_ERROR).and_then(|seed| {
+ if seed.len() != KEY_SEED_SIZE {
+ Err(CREATE_ERROR)
+ } else {
+ Ok(seed)
+ }
+ })?;
+ let hmac_tag =
+ env.convert_byte_array(&java_hmac_tag).map_err(|_| CREATE_ERROR).and_then(|tag| {
+ if tag.len() != TAG_SIZE {
+ Err(CREATE_ERROR)
+ } else {
+ Ok(tag)
+ }
+ })?;
+ 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"),
+ );
+ box_to_handle(cipher).map_err(|_| CREATE_ERROR)
+ })
}
-/// Close an LDT cipher.
+fn create_map_to_error<F: Fn() -> Result<jlong, jlong>>(f: F) -> jlong {
+ f().unwrap_or_else(|e| e)
+}
+
+/// Close an LDT Encryption Cipher
#[no_mangle]
-pub extern "system" fn Java_com_google_android_gms_nearby_presence_hazmat_LdtNpJni_closeLdtCipher(
+extern "system" fn Java_com_google_android_gms_nearby_presence_hazmat_LdtNpJni_closeEncryptCipher(
_env: JNIEnv,
_class: JClass,
- ldt_handle: jlong,
-) -> jint {
+ handle: jlong,
+) {
+ // create the box, let it be dropped
+ let _ = boxed_from_handle::<LdtAdvEncrypter>(handle);
+}
+
+/// Close an LDT Decryption Cipher
+#[no_mangle]
+extern "system" fn Java_com_google_android_gms_nearby_presence_hazmat_LdtNpJni_closeDecryptCipher(
+ _env: JNIEnv,
+ _class: JClass,
+ handle: jlong,
+) {
// create the box, let it be dropped
- let _ = boxed_from_handle::<Ldt>(ldt_handle);
- // success -- are there any meaningful error condtions we can even detect?
- 0
+ let _ = boxed_from_handle::<LdtAdvDecrypter>(handle);
}
/// Encrypt a buffer in place.
-/// Safety: We know the data jArray pointer is safe because it is coming directly from the JVM.
#[no_mangle]
-#[allow(clippy::not_unsafe_ptr_arg_deref)]
-pub extern "system" fn Java_com_google_android_gms_nearby_presence_hazmat_LdtNpJni_encrypt(
+extern "system" fn Java_com_google_android_gms_nearby_presence_hazmat_LdtNpJni_encrypt(
env: JNIEnv,
_class: JClass,
- ldt_handle: jlong,
+ handle: jlong,
salt: jchar,
- data: jbyteArray,
+ data: JByteArray,
) -> jint {
- jbyte_cipher_data_as_u8_array(&env, data)
- .and_then(|(len, mut data_u8)| {
- with_handle::<Ldt, _, _>(ldt_handle, |ldt| {
- ldt.ldt_enc.encrypt(&mut data_u8[..len], &expand_np_salt_to_padder(salt)).map_err(
- |err| match err {
- ldt::LdtError::InvalidLength(_) => CipherOpError::DataLen,
- },
- )?;
- env.set_byte_array_region(
- unsafe { &JByteArray::from_raw(data) },
- 0,
- &u8_slice_to_jbyte_array(data_u8)[..len],
- )
- .map_err(|_| CipherOpError::JniOp)
- .map(|_| 0) // success
- })
+ map_to_error_code(|| {
+ let mut buffer =
+ env.convert_byte_array(&data).map_err(|_| EncryptError::JniOp).and_then(|data| {
+ if !(MIN_DATA_LEN..=MAX_DATA_LEN).contains(&data.len()) {
+ Err(EncryptError::DataLen)
+ } else {
+ Ok(data)
+ }
+ })?;
+
+ with_handle::<LdtAdvEncrypter, _, _>(handle, |cipher| {
+ cipher.encrypt(buffer.as_mut_slice(), &expand_np_salt_to_padder(salt)).map_err(
+ |err| match err {
+ ldt::LdtError::InvalidLength(_) => EncryptError::DataLen,
+ },
+ )?;
+
+ // Avoid a copy since transmuting from a &[u8] to a &[i8] is safe
+ // Safety:
+ // - u8 and jbyte/i8 are the same size have the same alignment
+ let jbyte_buffer = bytes_to_jbytes(buffer.as_slice());
+
+ env.set_byte_array_region(&data, 0, jbyte_buffer)
+ .map_err(|_| EncryptError::JniOp)
+ .map(|_| SUCCESS)
})
- .unwrap_or_else(|e| e.to_jni_error_code())
+ })
}
/// Decrypt a buffer in place.
/// Safety: We know the data pointer is safe because it is coming directly from the JVM.
#[no_mangle]
-#[allow(clippy::not_unsafe_ptr_arg_deref)]
-pub extern "system" fn Java_com_google_android_gms_nearby_presence_hazmat_LdtNpJni_decrypt_1and_1verify(
+extern "system" fn Java_com_google_android_gms_nearby_presence_hazmat_LdtNpJni_decryptAndVerify(
env: JNIEnv,
_class: JClass,
- ldt_handle: jlong,
+ handle: jlong,
salt: jchar,
- data: jbyteArray,
+ data: JByteArray,
) -> jint {
- jbyte_cipher_data_as_u8_array(&env, data)
- .and_then(|(len, mut data_u8)| {
- with_handle::<Ldt, _, _>(ldt_handle, |ldt| {
- let result = ldt
- .ldt_dec
- .decrypt_and_verify(&data_u8[..len], &expand_np_salt_to_padder(salt))
- .map_err(|err| match err {
- LdtAdvDecryptError::InvalidLength(_) => CipherOpError::DataLen,
- LdtAdvDecryptError::MacMismatch => CipherOpError::MacMisMatch,
- })?;
- data_u8[..result.len()].copy_from_slice(result.as_slice());
- env.set_byte_array_region(
- unsafe { &JByteArray::from_raw(data) },
- 0,
- &u8_slice_to_jbyte_array(data_u8)[..len],
- )
- .map_err(|_| CipherOpError::JniOp)
- .map(|_| 0) // success
- })
+ map_to_error_code(|| {
+ let mut buffer =
+ env.convert_byte_array(&data).map_err(|_| DecryptError::JniOp).and_then(|data| {
+ if !(MIN_DATA_LEN..=MAX_DATA_LEN).contains(&data.len()) {
+ Err(DecryptError::DataLen)
+ } else {
+ Ok(data)
+ }
+ })?;
+
+ with_handle::<LdtAdvDecrypter, _, _>(handle, |cipher| {
+ let result = cipher
+ .decrypt_and_verify(buffer.as_mut_slice(), &expand_np_salt_to_padder(salt))
+ .map_err(|err| match err {
+ LdtAdvDecryptError::InvalidLength(_) => DecryptError::DataLen,
+ LdtAdvDecryptError::MacMismatch => DecryptError::MacMisMatch,
+ })?;
+
+ let jbyte_buffer = bytes_to_jbytes(result.as_slice());
+
+ env.set_byte_array_region(&data, 0, jbyte_buffer)
+ .map_err(|_| DecryptError::JniOp)
+ .map(|_| SUCCESS)
})
- .unwrap_or_else(|e| e.to_jni_error_code())
+ })
+}
+
+/// A zero-copy conversion from a u8 slice to a jbyte slice
+fn bytes_to_jbytes(bytes: &[u8]) -> &[jbyte] {
+ // Safety:
+ // - u8 and jbyte/i8 are the same size have the same alignment
+ unsafe { alloc::slice::from_raw_parts(bytes.as_ptr() as *const jbyte, bytes.len()) }
}
/// Reconstruct a `Box<T>` from `handle`, and invoke `f` with the resulting `&T`.
@@ -201,8 +255,7 @@ fn with_handle<T, U, F: FnMut(&T) -> U>(handle: jlong, mut f: F) -> U {
let ret = f(&boxed);
// don't consume the box -- need to keep the handle alive
- Box::leak(boxed);
-
+ let _ = Box::leak(boxed);
ret
}
@@ -243,77 +296,56 @@ fn box_to_handle<T>(thing: T) -> Result<jlong, ()> {
.map(|ptr_64: u64| ptr_64 as jlong)
}
-/// Extract data suitable for Ldt128 cipher ops from a JNI jbyteArray.
+/// Expand the NP salt to the size needed to be an LDT XorPadder.
///
-/// Returns `(data len in buffer, buffer)`, or `Err` if any JNI ops fail.
-fn jbyte_cipher_data_as_u8_array(
- env: &JNIEnv,
- cipher_data: jbyteArray,
-) -> Result<(usize, [u8; MAX_DATA_LEN]), CipherOpError> {
- let data_len = env
- .get_array_length(unsafe { &JByteArray::from_raw(cipher_data) })
- .map_err(|_| CipherOpError::JniOp)? as usize;
- if !(MIN_DATA_LEN..=MAX_DATA_LEN).contains(&data_len) {
- return Err(CipherOpError::DataLen);
- }
-
- let mut buf = [jbyte::default(); MAX_DATA_LEN];
- env.get_byte_array_region(
- unsafe { &JByteArray::from_raw(cipher_data) },
- 0,
- &mut buf[0..data_len],
- )
- .map_err(|_| CipherOpError::JniOp)?;
+/// Returns a XorPadder containing the HKDF of the salt.
+fn expand_np_salt_to_padder(np_salt: jchar) -> XorPadder<{ crypto_provider::aes::BLOCK_SIZE }> {
+ let salt_bytes = np_salt.to_be_bytes();
+ ldt_np_adv::salt_padder::<16, CryptoProviderImpl>(salt_bytes.into())
+}
- Ok((data_len, jbyte_array_to_u8_array(buf)))
+fn map_to_error_code<E: JniError, F: Fn() -> Result<jint, E>>(f: F) -> jint {
+ f().unwrap_or_else(|e| e.to_jni_error_code())
}
-/// Convert a jbyte array to a u8 array
-fn jbyte_array_to_u8_array<const N: usize>(src: [jbyte; N]) -> [u8; N] {
- let mut dest = [0_u8; N];
- for i in 0..N {
- // numeric cast doesn't alter bits, which is what we want
- // https://doc.rust-lang.org/reference/expressions/operator-expr.html#semantics
- dest[i] = src[i] as u8;
- }
- dest
+trait JniError {
+ fn to_jni_error_code(&self) -> jint;
}
-fn u8_slice_to_jbyte_array<const N: usize>(src: [u8; N]) -> [jbyte; N] {
- let mut dest = [0_i8; N];
- for i in 0..N {
- // numeric cast doesn't alter bits, which is what we want
- // https://doc.rust-lang.org/reference/expressions/operator-expr.html#semantics
- dest[i] = src[i] as jbyte;
- }
- dest
+#[derive(Debug)]
+enum EncryptError {
+ /// Data is the wrong length
+ DataLen,
+ /// JNI op failed
+ JniOp,
}
-/// Expand the NP salt to the size needed to be an LDT XorPadder.
-///
-/// Returns a XorPadder containing the HKDF of the salt.
-fn expand_np_salt_to_padder(np_salt: jchar) -> XorPadder<{ crypto_provider::aes::BLOCK_SIZE }> {
- let salt_bytes = np_salt.to_be_bytes();
- ldt_np_adv::salt_padder::<16, CryptoProviderImpl>(salt_bytes.into())
+impl JniError for EncryptError {
+ fn to_jni_error_code(&self) -> jint {
+ match self {
+ Self::DataLen => -1,
+ Self::JniOp => -2,
+ }
+ }
}
#[derive(Debug)]
-enum CipherOpError {
- /// The mac did not match the provided tag
- MacMisMatch,
+enum DecryptError {
/// Data is the wrong length
DataLen,
+ /// The mac did not match the provided tag
+ MacMisMatch,
/// JNI op failed
JniOp,
}
-impl CipherOpError {
+impl JniError for DecryptError {
/// Returns an error code suitable for returning from Ldt encrypt/decrypt JNI calls.
fn to_jni_error_code(&self) -> jint {
match self {
- CipherOpError::DataLen => -1,
- CipherOpError::MacMisMatch => -2,
- CipherOpError::JniOp => -3,
+ Self::DataLen => -1,
+ Self::JniOp => -2,
+ Self::MacMisMatch => -3,
}
}
}
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 @@ version.workspace = true
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 7f0d5d7..9b576a6 100644
--- a/nearby/presence/np_adv/Cargo.toml
+++ b/nearby/presence/np_adv/Cargo.toml
@@ -4,6 +4,9 @@ version.workspace = true
edition.workspace = true
publish.workspace = true
+[lints]
+workspace = true
+
[dependencies]
array_view = { path = "../array_view" }
ldt_np_adv.workspace = true
@@ -14,29 +17,41 @@ xts_aes.workspace = true
crypto_provider.workspace = true
strum.workspace = true
strum_macros.workspace = true
-nom = { version = "7.1.1", default-features = false, features = ["alloc"] }
+nom = { version = "7.1.3", default-features = false }
lazy_static.workspace = true
sink.workspace = true
tinyvec.workspace = true
-rand.workspace = true
[features]
default = []
devtools = []
testing = []
+alloc = ["crypto_provider/alloc"]
[dev-dependencies]
hex.workspace = true
+rand.workspace = true
rand_ext = { path = "../rand_ext" }
-init_with = "1.1.0"
-serde_json.workspace = true
+serde_json = { workspace = true, features = ["std"] }
+serde.workspace = true
anyhow.workspace = true
test_helper = { path = "../test_helper" }
criterion.workspace = true
-crypto_provider_default = {workspace = true, features = ["std", "rustcrypto"]}
-np_ed25519 = {workspace = true, features = ["std"]}
-sink = {workspace = true, features = ["std"]}
+crypto_provider_default = { workspace = true, features = ["std", "rustcrypto"] }
+np_ed25519 = { workspace = true, features = ["std"] }
+sink = { workspace = true, features = ["std"] }
[[bench]]
name = "deser_adv"
harness = false
+required-features = ["alloc", "devtools"]
+
+[[test]]
+name = "examples_v0"
+path = "tests/examples_v0.rs"
+required-features = ["alloc"]
+
+[[test]]
+name = "examples_v1"
+path = "tests/examples_v1.rs"
+required-features = ["alloc"]
diff --git a/nearby/presence/np_adv/benches/deser_adv.rs b/nearby/presence/np_adv/benches/deser_adv.rs
index bc2789d..b52f53a 100644
--- a/nearby/presence/np_adv/benches/deser_adv.rs
+++ b/nearby/presence/np_adv/benches/deser_adv.rs
@@ -12,17 +12,27 @@
// 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};
use crypto_provider_default::CryptoProviderImpl;
-use ldt_np_adv::{LdtEncrypterXtsAes128, LegacySalt};
+use ldt_np_adv::LegacySalt;
+use np_adv::deserialization_arena;
use np_adv::extended::serialize::AdvertisementType;
use np_adv::{
- credential::{simple::*, source::*, v0::*, v1::*},
+ credential::{book::*, v0::*, v1::*, *},
de_type::EncryptedIdentityDataElementType,
- deserialize_advertisement, deserialize_v0_advertisement, deserialize_v1_advertisement,
+ deserialize_advertisement,
extended::{
data_elements::{GenericDataElement, TxPowerDataElement},
deserialize::VerificationMode,
@@ -34,9 +44,10 @@ use np_adv::{
legacy::{
actions::{ActionBits, ActionsDataElement},
serialize::{AdvBuilder as LegacyAdvBuilder, LdtIdentity},
+ ShortMetadataKey,
},
shared_data::{ContextSyncSeqNum, TxPower},
- PublicIdentity,
+ MetadataKey, PublicIdentity,
};
use rand::{Rng as _, SeedableRng as _};
use strum::IntoEnumIterator;
@@ -63,17 +74,18 @@ pub fn deser_adv_v1_encrypted(c: &mut Criterion) {
// take the first n identities, one section per identity
for identity in identities.iter().take(num_sections) {
- let hkdf = np_hkdf::NpKeySeedHkdf::<CryptoProviderImpl>::new(
- &identity.key_seed,
+ let broadcast_cm = SimpleSignedBroadcastCryptoMaterial::new(
+ identity.key_seed,
+ identity.extended_metadata_key,
+ identity.key_pair.private_key(),
);
match identity_type {
VerificationMode::Mic => {
let mut sb = adv_builder
- .section_builder(MicEncryptedSectionEncoder::new_random_salt(
+ .section_builder(MicEncryptedSectionEncoder::<CryptoProviderImpl>::new_random_salt(
&mut crypto_rng,
EncryptedIdentityDataElementType::Private,
- &identity.extended_metadata_key,
- &hkdf,
+ &broadcast_cm,
))
.unwrap();
@@ -82,12 +94,10 @@ pub fn deser_adv_v1_encrypted(c: &mut Criterion) {
}
VerificationMode::Signature => {
let mut sb = adv_builder
- .section_builder(SignedEncryptedSectionEncoder::new_random_salt(
+ .section_builder(SignedEncryptedSectionEncoder::<CryptoProviderImpl>::new_random_salt(
&mut crypto_rng,
EncryptedIdentityDataElementType::Private,
- &identity.extended_metadata_key,
- &identity.key_pair,
- &hkdf,
+ &broadcast_cm,
))
.unwrap();
@@ -99,22 +109,11 @@ pub fn deser_adv_v1_encrypted(c: &mut Criterion) {
let adv = adv_builder.into_advertisement();
- match crypto_type {
- CryptoMaterialType::MinFootprint => run_with_v1_creds::<
- MinimumFootprintV1CryptoMaterial,
- CryptoProviderImpl,
- >(
- b, identities, adv.as_slice()
- ),
- CryptoMaterialType::Precalculated => {
- run_with_v1_creds::<
- PrecalculatedV1CryptoMaterial,
- CryptoProviderImpl,
- >(
- b, identities, adv.as_slice()
- )
- }
- }
+ run_with_v1_creds::<
+ CryptoProviderImpl
+ >(
+ b, crypto_type, identities, adv.as_slice()
+ )
},
);
}
@@ -124,31 +123,23 @@ pub fn deser_adv_v1_encrypted(c: &mut Criterion) {
}
pub fn deser_adv_v1_plaintext(c: &mut Criterion) {
- let _rng = rand::rngs::StdRng::from_entropy();
-
- for &num_sections in &[1, 2, 5, 8] {
- // measure worst-case performance -- the correct identities will be the last
- // num_sections of the identities to be tried
- c.bench_function(&format!("Deser V1 plaintext: sections={num_sections}"), |b| {
- let mut adv_builder = ExtendedAdvBuilder::new(AdvertisementType::Plaintext);
+ c.bench_function("Deser V1 plaintext: sections=1", |b| {
+ let mut adv_builder = ExtendedAdvBuilder::new(AdvertisementType::Plaintext);
- // take the first n identities, one section per identity
- for _ in 0..num_sections {
- let mut sb = adv_builder.section_builder(PublicSectionEncoder::default()).unwrap();
+ let mut sb = adv_builder.section_builder(PublicSectionEncoder::default()).unwrap();
- add_des(&mut sb);
- sb.add_to_advertisement();
- }
+ add_des(&mut sb);
+ sb.add_to_advertisement();
- let adv = adv_builder.into_advertisement();
+ let adv = adv_builder.into_advertisement();
- run_with_v1_creds::<MinimumFootprintV1CryptoMaterial, CryptoProviderImpl>(
- b,
- vec![],
- adv.as_slice(),
- )
- });
- }
+ run_with_v1_creds::<CryptoProviderImpl>(
+ b,
+ CryptoMaterialType::MinFootprint,
+ vec![],
+ adv.as_slice(),
+ )
+ });
}
pub fn deser_adv_v0_encrypted(c: &mut Criterion) {
@@ -165,17 +156,17 @@ pub fn deser_adv_v0_encrypted(c: &mut Criterion) {
.collect::<Vec<_>>();
let identity = &identities[0];
- let hkdf =
- np_hkdf::NpKeySeedHkdf::<CryptoProviderImpl>::new(&identity.key_seed);
+
+ let broadcast_cm = SimpleBroadcastCryptoMaterial::<V0>::new(
+ identity.key_seed,
+ identity.legacy_metadata_key,
+ );
let mut adv_builder =
LegacyAdvBuilder::new(LdtIdentity::<CryptoProviderImpl>::new(
EncryptedIdentityDataElementType::Private,
LegacySalt::from(rng.gen::<[u8; 2]>()),
- identity.legacy_metadata_key,
- LdtEncrypterXtsAes128::<CryptoProviderImpl>::new(
- &hkdf.legacy_ldt_key(),
- ),
+ &broadcast_cm,
));
let mut action_bits = ActionBits::default();
@@ -184,46 +175,31 @@ pub fn deser_adv_v0_encrypted(c: &mut Criterion) {
let adv = adv_builder.into_advertisement().unwrap();
- match crypto_type {
- CryptoMaterialType::MinFootprint => run_with_v0_creds::<
- MinimumFootprintV0CryptoMaterial,
- CryptoProviderImpl,
- >(
- b, identities, adv.as_slice()
- ),
- CryptoMaterialType::Precalculated => run_with_v0_creds::<
- PrecalculatedV0CryptoMaterial,
- CryptoProviderImpl,
- >(
- b, identities, adv.as_slice()
- ),
- }
+ run_with_v0_creds::<CryptoProviderImpl>(
+ b,
+ crypto_type,
+ identities,
+ adv.as_slice(),
+ )
},
);
}
}
}
-type DefaultV0Credential = np_adv::credential::simple::SimpleV0Credential<
- np_adv::credential::v0::MinimumFootprintV0CryptoMaterial,
- (),
->;
-type DefaultV1Credential = np_adv::credential::simple::SimpleV1Credential<
- np_adv::credential::v1::MinimumFootprintV1CryptoMaterial,
- (),
->;
-type DefaultBothCredentialSource =
- np_adv::credential::source::OwnedBothCredentialSource<DefaultV0Credential, DefaultV1Credential>;
-
pub fn deser_adv_v0_plaintext(c: &mut Criterion) {
- let mut adv_builder = LegacyAdvBuilder::new(PublicIdentity::default());
+ let mut adv_builder = LegacyAdvBuilder::new(PublicIdentity);
let mut action_bits = ActionBits::default();
action_bits.set_action(ContextSyncSeqNum::try_from(3).unwrap());
adv_builder.add_data_element(ActionsDataElement::from(action_bits)).unwrap();
let adv = adv_builder.into_advertisement().unwrap();
- let cred_source: DefaultBothCredentialSource = DefaultBothCredentialSource::new_empty();
+ let cred_book = CredentialBookBuilder::<EmptyMatchedCredential>::build_cached_slice_book::<
+ 0,
+ 0,
+ CryptoProviderImpl,
+ >(&[], &[]);
for &num_advs in &[1, 10, 100, 1000] {
c.bench_function(
@@ -232,9 +208,10 @@ pub fn deser_adv_v0_plaintext(c: &mut Criterion) {
b.iter(|| {
for _ in 0..num_advs {
black_box(
- deserialize_advertisement::<_, _, _, _, CryptoProviderImpl>(
+ deserialize_advertisement::<_, CryptoProviderImpl>(
+ deserialization_arena!(),
black_box(adv.as_slice()),
- black_box(&cred_source),
+ black_box(&cred_book),
)
.expect("Should succeed"),
);
@@ -247,22 +224,20 @@ pub fn deser_adv_v0_plaintext(c: &mut Criterion) {
/// Benchmark decrypting a V0 advertisement with credentials built from the reversed list of
/// identities
-fn run_with_v0_creds<M, C>(b: &mut Bencher, identities: Vec<V0Identity<C>>, adv: &[u8])
-where
- M: V0CryptoMaterialExt,
+fn run_with_v0_creds<C>(
+ b: &mut Bencher,
+ crypto_material_type: CryptoMaterialType,
+ identities: Vec<V0Identity<C>>,
+ adv: &[u8],
+) where
C: CryptoProvider,
{
let mut creds = identities
.into_iter()
- .map(|i| {
- let hkdf = np_hkdf::NpKeySeedHkdf::<C>::new(&i.key_seed);
- SimpleV0Credential::new(
- M::build_cred::<C>(
- i.key_seed,
- hkdf.legacy_metadata_key_hmac_key().calculate_hmac(&i.legacy_metadata_key),
- ),
- i.key_seed,
- )
+ .map(|identity| identity.into_discovery_credential())
+ .map(|discovery_credential| MatchableCredential {
+ discovery_credential,
+ match_data: EmptyMatchedCredential,
})
.collect::<Vec<_>>();
@@ -270,34 +245,55 @@ where
// cred source for predictably bad performance
creds.reverse();
- let cred_source = SliceCredentialSource::new(&creds);
- b.iter(|| {
- black_box(deserialize_v0_advertisement::<_, _, C>(adv, &cred_source).map(|_| 0_u8).unwrap())
- });
+ match crypto_material_type {
+ CryptoMaterialType::MinFootprint => {
+ // Cache size of 0 => only min-footprint creds
+ let cred_book = CredentialBookBuilder::<_>::build_cached_slice_book::<
+ 0,
+ 0,
+ CryptoProviderImpl,
+ >(&creds, &[]);
+
+ b.iter(|| {
+ black_box(
+ deserialize_advertisement::<_, C>(deserialization_arena!(), adv, &cred_book)
+ .map(|_| 0_u8)
+ .unwrap(),
+ )
+ });
+ }
+ CryptoMaterialType::Precalculated => {
+ let cred_book = CredentialBookBuilder::<_>::build_precalculated_owned_book::<C>(
+ creds,
+ core::iter::empty(),
+ );
+ b.iter(|| {
+ black_box(
+ deserialize_advertisement::<_, C>(deserialization_arena!(), adv, &cred_book)
+ .map(|_| 0_u8)
+ .unwrap(),
+ )
+ });
+ }
+ }
}
/// Benchmark decrypting a V1 advertisement with credentials built from the reversed list of
/// identities
-fn run_with_v1_creds<M, C>(b: &mut Bencher, identities: Vec<V1Identity<C>>, adv: &[u8])
-where
- M: V1CryptoMaterialExt,
+fn run_with_v1_creds<C>(
+ b: &mut Bencher,
+ crypto_material_type: CryptoMaterialType,
+ identities: Vec<V1Identity<C>>,
+ adv: &[u8],
+) where
C: CryptoProvider,
{
let mut creds = identities
.into_iter()
- .map(|i| {
- let hkdf = np_hkdf::NpKeySeedHkdf::<C>::new(&i.key_seed);
- SimpleV1Credential::new(
- M::build_cred::<C>(
- i.key_seed,
- hkdf.extended_unsigned_metadata_key_hmac_key()
- .calculate_hmac(&i.extended_metadata_key),
- hkdf.extended_signed_metadata_key_hmac_key()
- .calculate_hmac(&i.extended_metadata_key),
- i.key_pair.public(),
- ),
- i.key_seed,
- )
+ .map(|identity| identity.into_discovery_credential())
+ .map(|discovery_credential| MatchableCredential {
+ discovery_credential,
+ match_data: EmptyMatchedCredential,
})
.collect::<Vec<_>>();
@@ -305,12 +301,41 @@ where
// cred source for predictably bad performance
creds.reverse();
- let cred_source = SliceCredentialSource::new(&creds);
- b.iter(|| {
- black_box(deserialize_v1_advertisement::<_, _, C>(adv, &cred_source).map(|_| 0_u8).unwrap())
- });
+ match crypto_material_type {
+ CryptoMaterialType::MinFootprint => {
+ // Cache size of 0 => only min-footprint creds
+ let cred_book = CredentialBookBuilder::<_>::build_cached_slice_book::<
+ 0,
+ 0,
+ CryptoProviderImpl,
+ >(&[], &creds);
+
+ b.iter(|| {
+ black_box(
+ deserialize_advertisement::<_, C>(deserialization_arena!(), adv, &cred_book)
+ .map(|_| 0_u8)
+ .unwrap(),
+ )
+ });
+ }
+ CryptoMaterialType::Precalculated => {
+ let cred_book = CredentialBookBuilder::<_>::build_precalculated_owned_book::<C>(
+ core::iter::empty(),
+ creds,
+ );
+ b.iter(|| {
+ black_box(
+ deserialize_advertisement::<_, C>(deserialization_arena!(), adv, &cred_book)
+ .map(|_| 0_u8)
+ .unwrap(),
+ )
+ });
+ }
+ }
}
-fn add_des<I: SectionEncoder>(sb: &mut SectionBuilder<I>) {
+fn add_des<I: SectionEncoder>(
+ sb: &mut SectionBuilder<&mut np_adv::extended::serialize::AdvBuilder, I>,
+) {
sb.add_de_res(|_| TxPower::try_from(17).map(TxPowerDataElement::from)).unwrap();
sb.add_de_res(|_| GenericDataElement::try_from(100_u32.into(), &[0; 10])).unwrap();
}
@@ -325,20 +350,29 @@ criterion_main!(benches);
struct V0Identity<C: CryptoProvider> {
key_seed: [u8; 32],
- legacy_metadata_key: [u8; 14],
+ legacy_metadata_key: ShortMetadataKey,
_marker: PhantomData<C>,
}
impl<C: CryptoProvider> V0Identity<C> {
/// Generate a new identity with random crypto material
fn random<R: rand::Rng + rand::CryptoRng>(rng: &mut R) -> Self {
- Self { key_seed: rng.gen(), legacy_metadata_key: rng.gen(), _marker: PhantomData }
+ Self {
+ key_seed: rng.gen(),
+ legacy_metadata_key: ShortMetadataKey(rng.gen()),
+ _marker: PhantomData,
+ }
+ }
+ /// Convert this `V0Identity` into a V0 discovery credential.
+ fn into_discovery_credential(self) -> V0DiscoveryCredential {
+ SimpleBroadcastCryptoMaterial::<V0>::new(self.key_seed, self.legacy_metadata_key)
+ .derive_v0_discovery_credential::<C>()
}
}
struct V1Identity<C: CryptoProvider> {
key_seed: [u8; 32],
- extended_metadata_key: [u8; 16],
+ extended_metadata_key: MetadataKey,
key_pair: np_ed25519::KeyPair<C>,
}
@@ -347,77 +381,23 @@ impl<C: CryptoProvider> V1Identity<C> {
fn random(rng: &mut C::CryptoRng) -> Self {
Self {
key_seed: rng.gen(),
- extended_metadata_key: rng.gen(),
+ extended_metadata_key: MetadataKey(rng.gen()),
key_pair: np_ed25519::KeyPair::<C>::generate(),
}
}
+ /// Convert this `V1Identity` into a `V1DiscoveryCredential`.
+ fn into_discovery_credential(self) -> V1DiscoveryCredential {
+ SimpleSignedBroadcastCryptoMaterial::new(
+ self.key_seed,
+ self.extended_metadata_key,
+ self.key_pair.private_key(),
+ )
+ .derive_v1_discovery_credential::<C>()
+ }
}
-#[derive(strum_macros::EnumIter, Debug)]
+#[derive(strum_macros::EnumIter, Clone, Copy, Debug)]
enum CryptoMaterialType {
MinFootprint,
Precalculated,
}
-
-// if we get confident this is a valid shared way, could move this to the main trait
-trait V0CryptoMaterialExt: V0CryptoMaterial {
- fn build_cred<C: CryptoProvider>(
- key_seed: [u8; 32],
- legacy_metadata_key_hmac: [u8; 32],
- ) -> Self;
-}
-
-trait V1CryptoMaterialExt: V1CryptoMaterial {
- fn build_cred<C: CryptoProvider>(
- key_seed: [u8; 32],
- unsigned_metadata_key_hmac: [u8; 32],
- signed_metadata_key_hmac: [u8; 32],
- pub_key: np_ed25519::PublicKey<C>,
- ) -> Self;
-}
-
-impl V0CryptoMaterialExt for MinimumFootprintV0CryptoMaterial {
- fn build_cred<C: CryptoProvider>(
- key_seed: [u8; 32],
- legacy_metadata_key_hmac: [u8; 32],
- ) -> Self {
- Self::new(key_seed, legacy_metadata_key_hmac)
- }
-}
-
-impl V0CryptoMaterialExt for PrecalculatedV0CryptoMaterial {
- fn build_cred<C: CryptoProvider>(
- key_seed: [u8; 32],
- legacy_metadata_key_hmac: [u8; 32],
- ) -> Self {
- Self::new::<C>(&key_seed, legacy_metadata_key_hmac)
- }
-}
-
-impl V1CryptoMaterialExt for MinimumFootprintV1CryptoMaterial {
- fn build_cred<C: CryptoProvider>(
- key_seed: [u8; 32],
- unsigned_metadata_key_hmac: [u8; 32],
- signed_metadata_key_hmac: [u8; 32],
- pub_key: np_ed25519::PublicKey<C>,
- ) -> Self {
- Self::new(key_seed, unsigned_metadata_key_hmac, signed_metadata_key_hmac, pub_key)
- }
-}
-
-impl V1CryptoMaterialExt for PrecalculatedV1CryptoMaterial {
- fn build_cred<C: CryptoProvider>(
- key_seed: [u8; 32],
- unsigned_metadata_key_hmac: [u8; 32],
- signed_metadata_key_hmac: [u8; 32],
- pub_key: np_ed25519::PublicKey<C>,
- ) -> Self {
- let min_foot = MinimumFootprintV1CryptoMaterial::new(
- key_seed,
- unsigned_metadata_key_hmac,
- signed_metadata_key_hmac,
- pub_key,
- );
- min_foot.to_precalculated::<C>()
- }
-}
diff --git a/nearby/presence/np_adv/src/array_vec.rs b/nearby/presence/np_adv/src/array_vec.rs
new file mode 100644
index 0000000..bcc387e
--- /dev/null
+++ b/nearby/presence/np_adv/src/array_vec.rs
@@ -0,0 +1,205 @@
+// Copyright 2023 Google LLC
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+//! Provides an [`ArrayVecOption`] wrapper implementation over [`ArrayVec`],
+//! which stores all the elements in an `Option` in order to satisfy
+//! `ArrayVec`'s requirement that the elements must implement `Default`.
+
+use tinyvec::{ArrayVec, ArrayVecIterator};
+#[cfg(any(test, feature = "alloc"))]
+extern crate alloc;
+#[cfg(any(test, feature = "alloc"))]
+use alloc::vec::Vec;
+
+/// A wrapper of [`ArrayVec`] that stores it values as [`Option`], in order to
+/// satisfy `ArrayVec`'s requirement that the elements must implement `Default`.
+/// The implementation guarantees that any items in the wrapped `ArrayVec`
+/// within `0..len` is `Some`, and therefore will not panic when unwrapped.
+#[derive(Debug, Clone, PartialEq, Eq)]
+pub struct ArrayVecOption<A, const N: usize>(ArrayVec<[Option<A>; N]>);
+
+// Cannot derive due to https://github.com/rust-lang/rust/issues/74462
+impl<A, const N: usize> Default for ArrayVecOption<A, N> {
+ fn default() -> Self {
+ Self(Default::default())
+ }
+}
+
+/// Iterator type returned by `ArrayVecOption.iter()`, which can be used as
+/// `impl Iterator<Item = &A>`
+pub type ArrayVecOptionRefIter<'a, A> =
+ core::iter::Map<core::slice::Iter<'a, Option<A>>, fn(&'a Option<A>) -> &'a A>;
+
+/// Iterator type returned by `ArrayVecOption.into_iter()`, which can be used as
+/// `impl Iterator<Item = A>`
+pub type ArrayVecOptionIntoIter<A, const N: usize> =
+ core::iter::Map<ArrayVecIterator<[Option<A>; N]>, fn(Option<A>) -> A>;
+
+impl<A, const N: usize> ArrayVecOption<A, N> {
+ /// Returns an iterator over this vec.
+ pub fn iter(&self) -> ArrayVecOptionRefIter<A> {
+ self.0
+ .iter()
+ .map(|v| v.as_ref().expect("ArrayVecOption value before .len() should not be None"))
+ }
+
+ /// The length of the vec (in elements).
+ pub fn len(&self) -> usize {
+ self.0.len()
+ }
+
+ /// Checks if the length is 0.
+ pub fn is_empty(&self) -> bool {
+ self.0.is_empty()
+ }
+
+ /// Returns the first element of the vec, or `None` if it is empty.
+ pub fn first(&self) -> Option<&A> {
+ self.iter().next()
+ }
+
+ /// Place an element onto the end of the vec.
+ ///
+ /// # Panics
+ /// * If the length of the vec would overflow the capacity.
+ pub fn push(&mut self, value: A) {
+ self.0.push(Some(value))
+ }
+
+ /// Returns a reference to an element at the given index.
+ pub fn get(&self, index: usize) -> Option<&A> {
+ self.0.get(index).and_then(|opt| opt.as_ref())
+ }
+
+ /// Sorts the slice with a key extraction function, but might not preserve
+ /// the order of equal elements.
+ ///
+ /// This sort is unstable (i.e., may reorder equal elements), in-place
+ /// (i.e., does not allocate), and *O*(*m* \* *n* \* log(*n*)) worst-case,
+ /// where the key function is *O*(*m*).
+ pub fn sort_unstable_by_key<K: Ord>(&mut self, f: impl Fn(&A) -> K) {
+ self.0.sort_unstable_by_key(|a| {
+ f(a.as_ref().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).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
+ /// `Option` in the process.
+ #[cfg(any(test, feature = "alloc"))]
+ pub fn into_vec(self) -> Vec<A> {
+ self.into_iter().collect()
+ }
+}
+
+impl<A, const N: usize> IntoIterator for ArrayVecOption<A, N> {
+ type Item = A;
+ type IntoIter = ArrayVecOptionIntoIter<A, N>;
+ fn into_iter(self) -> Self::IntoIter {
+ self.0
+ .into_iter()
+ .map(|v| v.expect("ArrayVecOption value before .len() should not be None"))
+ }
+}
+
+// Implement `FromIterator` to enable `iter.collect::<ArrayVecOption<_>>()`
+impl<A, const N: usize> FromIterator<A> for ArrayVecOption<A, N> {
+ fn from_iter<T: IntoIterator<Item = A>>(iter: T) -> Self {
+ Self(iter.into_iter().map(Some).collect())
+ }
+}
+
+impl<A, const N: usize> core::ops::Index<usize> for ArrayVecOption<A, N> {
+ type Output = A;
+ fn index(&self, index: usize) -> &Self::Output {
+ self.0[index].as_ref().expect("This panics if provided index is out of bounds")
+ }
+}
+
+#[cfg(test)]
+mod test {
+ extern crate std;
+ use super::ArrayVecOption;
+ use std::vec;
+
+ #[test]
+ fn test_default_array_vec() {
+ let vec = ArrayVecOption::<u32, 5>::default();
+ assert_eq!(0, vec.len());
+ assert_eq!(None, vec.iter().next());
+ assert!(vec.is_empty());
+ assert_eq!(None, vec.first());
+ assert_eq!(None, vec.get(0));
+ assert_eq!(vec![0_u32; 0], vec.into_vec());
+ }
+
+ #[test]
+ fn test_array_vec_with_contents() {
+ let mut vec = ArrayVecOption::<u32, 5>::default();
+ vec.push(1);
+ vec.push(2);
+ vec.push(3);
+ vec.push(4);
+ vec.push(5);
+ assert_eq!(5, vec.len());
+ let mut iter = vec.iter();
+ assert_eq!(Some(&1_u32), iter.next());
+ assert_eq!(Some(&2_u32), iter.next());
+ assert_eq!(Some(&3_u32), iter.next());
+ assert_eq!(Some(&4_u32), iter.next());
+ assert_eq!(Some(&5_u32), iter.next());
+ assert_eq!(None, iter.next());
+ assert!(!vec.is_empty());
+ assert_eq!(Some(&1_u32), vec.first());
+ assert_eq!(Some(&5_u32), vec.get(4));
+ assert_eq!(vec![1_u32, 2, 3, 4, 5], vec.clone().into_vec());
+
+ let _ = vec.swap_remove(2);
+ assert_eq!(vec![1_u32, 2, 5, 4], vec.clone().into_vec());
+ }
+
+ #[test]
+ #[should_panic]
+ fn test_array_vec_push_overflow() {
+ let mut vec = ArrayVecOption::<u32, 5>::default();
+ vec.push(1);
+ vec.push(2);
+ vec.push(3);
+ vec.push(4);
+ vec.push(5);
+ vec.push(6);
+ }
+
+ #[test]
+ fn test_sort() {
+ let mut vec = ArrayVecOption::<u32, 5>::default();
+ vec.push(3);
+ vec.push(1);
+ vec.push(4);
+ vec.push(1);
+ vec.push(2);
+ vec.sort_unstable_by_key(|k| *k);
+ assert_eq!(vec![1_u32, 1, 2, 3, 4], vec.clone().into_vec());
+ }
+
+ #[test]
+ fn test_collect() {
+ let vec: ArrayVecOption<u32, 5> = [5_u32, 4, 3, 2, 1].into_iter().collect();
+ assert_eq!(vec![5_u32, 4, 3, 2, 1], vec.clone().into_vec());
+ }
+}
diff --git a/nearby/presence/np_adv/src/credential/book.rs b/nearby/presence/np_adv/src/credential/book.rs
new file mode 100644
index 0000000..902d55a
--- /dev/null
+++ b/nearby/presence/np_adv/src/credential/book.rs
@@ -0,0 +1,544 @@
+// 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.
+
+//! Traits defining credential books. These are used in the deserialization path to provide
+//! the credentials to try for either advertisement version.
+//! See [`CredentialBookBuilder`] for batteries-included implementations.
+
+use crate::credential::source::{
+ CredentialSource, DiscoveryCredentialSource, SliceCredentialSource,
+};
+use crate::credential::v0::{V0DiscoveryCryptoMaterial, V0};
+use crate::credential::v1::{
+ SignedSectionIdentityResolutionMaterial, SignedSectionVerificationMaterial,
+ UnsignedSectionIdentityResolutionMaterial, UnsignedSectionVerificationMaterial,
+ V1DiscoveryCryptoMaterial, V1,
+};
+#[cfg(feature = "alloc")]
+use crate::credential::ReferencedMatchedCredential;
+use crate::credential::{
+ DiscoveryCryptoMaterial, MatchableCredential, MatchedCredential, ProtocolVersion,
+};
+use core::borrow::Borrow;
+use core::marker::PhantomData;
+use crypto_provider::CryptoProvider;
+
+#[cfg(feature = "alloc")]
+extern crate alloc;
+#[cfg(feature = "alloc")]
+use alloc::vec::Vec;
+
+/// A collection of credentials to try when attempting to deserialize
+/// advertisements of either advertisement version which is
+/// valid for the given lifetime.
+pub trait CredentialBook<'a>
+where
+ Self: 'a,
+{
+ /// The type of the matched credentials for the credentials
+ /// held within this credential-book. May lend from the credential-book.
+ type Matched: MatchedCredential;
+
+ /// The type of V0 crypto-materials yielded by this credential-book.
+ type V0Crypto: V0DiscoveryCryptoMaterial;
+
+ /// The type of V1 crypto-materials yielded by this credential-book.
+ type V1Crypto: V1DiscoveryCryptoMaterial;
+
+ /// The iterator type returned from [`CredentialBook#v0_iter()`],
+ /// which yields `(crypto-material, match_data)` pairs.
+ /// This is a lending iterator which may borrow things from `self`.
+ type V0Iterator: Iterator<Item = (Self::V0Crypto, Self::Matched)>;
+
+ /// The iterator type returned from [`CredentialBook#v1_iter()`],
+ /// which yields `(crypto-material, match_data)` pairs.
+ /// This is a lending iterator which may borrow things from `self`.
+ type V1Iterator: Iterator<Item = (Self::V1Crypto, Self::Matched)>;
+
+ /// Returns an iterator over V0 credentials in this credential book.
+ fn v0_iter(&'a self) -> Self::V0Iterator;
+
+ /// Returns an iterator over V1 credentials in this credential book.
+ fn v1_iter(&'a self) -> Self::V1Iterator;
+}
+
+/// Convenient marker struct for building credential-books with
+/// some given match-data type.
+pub struct CredentialBookBuilder<M: MatchedCredential> {
+ _marker: PhantomData<M>,
+}
+
+impl<M: MatchedCredential> CredentialBookBuilder<M> {
+ /// Constructs a [`CachedSliceCredentialBook`] from the given slices of discovery credentials,
+ /// populating its internal buffers of cached credential crypto-materials for up to the
+ /// given number of credentials in each version (up to `N0` credentials cached in V0,
+ /// and up to `N1` credentials cached in `V1`.)
+ pub fn build_cached_slice_book<'a, const N0: usize, const N1: usize, P: CryptoProvider>(
+ v0_creds: &'a [MatchableCredential<V0, M>],
+ v1_creds: &'a [MatchableCredential<V1, M>],
+ ) -> CachedSliceCredentialBook<'a, M, N0, N1> {
+ let v0_source = SliceCredentialSource::new(v0_creds);
+ let v0_cache = init_cache_from_source::<V0, _, N0, P>(&v0_source);
+ let v0_source = CachedCredentialSource::<_, _, N0>::new(v0_source, v0_cache);
+
+ let v1_source = SliceCredentialSource::new(v1_creds);
+ let v1_cache = init_cache_from_source::<V1, _, N1, P>(&v1_source);
+ let v1_source = CachedCredentialSource::<_, _, N1>::new(v1_source, v1_cache);
+
+ CredentialBookFromSources::new(v0_source, v1_source)
+ }
+
+ #[cfg(feature = "alloc")]
+ /// Constructs a new credential-book which owns all of the given credentials,
+ /// and maintains pre-calculated cryptographic information about them
+ /// for speedy advertisement deserialization.
+ pub fn build_precalculated_owned_book<P: CryptoProvider>(
+ v0_iter: impl IntoIterator<Item = MatchableCredential<V0, M>>,
+ v1_iter: impl IntoIterator<Item = MatchableCredential<V1, M>>,
+ ) -> PrecalculatedOwnedCredentialBook<M> {
+ let v0_source = PrecalculatedOwnedCredentialSource::<V0, M>::new::<P>(v0_iter);
+ let v1_source = PrecalculatedOwnedCredentialSource::<V1, M>::new::<P>(v1_iter);
+ CredentialBookFromSources::new(v0_source, v1_source)
+ }
+}
+
+// Now, for the implementation details. External implementors still need
+// to be able to reference these types (since the returned types from
+// [`CredentialBookBuilder`]'s convenience methods are just type-aliases),
+// and if they want, they can use them as building-blocks to construct
+// their own [`CredentialBook`]s, but they're largely just scaffolding.
+
+/// A structure bundling both V0 and V1 credential-sources to define
+/// a credential-book which owns both of these independent sources.
+pub struct CredentialBookFromSources<S0, S1> {
+ /// The source for V0 credentials.
+ v0_source: S0,
+ /// The source for V1 credentials.
+ v1_source: S1,
+}
+
+impl<'a, S0, S1> CredentialBookFromSources<S0, S1>
+where
+ Self: 'a,
+ S0: CredentialSource<'a, V0>,
+ S1: CredentialSource<'a, V1, Matched = <S0 as CredentialSource<'a, V0>>::Matched>,
+ <S0 as CredentialSource<'a, V0>>::Crypto: V0DiscoveryCryptoMaterial,
+ <S1 as CredentialSource<'a, V1>>::Crypto: V1DiscoveryCryptoMaterial,
+{
+ /// Constructs a new [`CredentialBook`] out of the two given
+ /// credential-sources, one for v0 and one for v1. The match-data
+ /// of both credential sources must be the same.
+ pub fn new(v0_source: S0, v1_source: S1) -> Self {
+ Self { v0_source, v1_source }
+ }
+}
+
+impl<'a, S0, S1> CredentialBook<'a> for CredentialBookFromSources<S0, S1>
+where
+ Self: 'a,
+ S0: CredentialSource<'a, V0>,
+ S1: CredentialSource<'a, V1, Matched = <S0 as CredentialSource<'a, V0>>::Matched>,
+ <S0 as CredentialSource<'a, V0>>::Crypto: V0DiscoveryCryptoMaterial,
+ <S1 as CredentialSource<'a, V1>>::Crypto: V1DiscoveryCryptoMaterial,
+{
+ type Matched = <S0 as CredentialSource<'a, V0>>::Matched;
+ type V0Crypto = <S0 as CredentialSource<'a, V0>>::Crypto;
+ type V1Crypto = <S1 as CredentialSource<'a, V1>>::Crypto;
+ type V0Iterator = <S0 as CredentialSource<'a, V0>>::Iterator;
+ type V1Iterator = <S1 as CredentialSource<'a, V1>>::Iterator;
+
+ fn v0_iter(&'a self) -> Self::V0Iterator {
+ self.v0_source.iter()
+ }
+ fn v1_iter(&'a self) -> Self::V1Iterator {
+ self.v1_source.iter()
+ }
+}
+
+/// Type-level function used internally
+/// by [`CachedCredentialSource`] in order to uniformly
+/// refer to the "precalculated" crypto-material variants
+/// for each protocol version.
+pub(crate) mod precalculated_for_version {
+ use crate::credential::v0::{
+ PrecalculatedV0DiscoveryCryptoMaterial, V0DiscoveryCredential, V0,
+ };
+ use crate::credential::v1::{
+ PrecalculatedV1DiscoveryCryptoMaterial, V1DiscoveryCredential, V1,
+ };
+ use crate::credential::{DiscoveryCryptoMaterial, ProtocolVersion};
+ use crypto_provider::CryptoProvider;
+
+ pub trait MappingTrait<V: ProtocolVersion> {
+ type Output: DiscoveryCryptoMaterial<V>;
+ /// Provides pre-calculated crypto-material for the given
+ /// discovery credential.
+ fn precalculate<C: CryptoProvider>(
+ discovery_credential: &V::DiscoveryCredential,
+ ) -> Self::Output;
+ }
+ pub enum Marker {}
+ impl MappingTrait<V0> for Marker {
+ type Output = PrecalculatedV0DiscoveryCryptoMaterial;
+ fn precalculate<C: CryptoProvider>(
+ discovery_credential: &V0DiscoveryCredential,
+ ) -> PrecalculatedV0DiscoveryCryptoMaterial {
+ PrecalculatedV0DiscoveryCryptoMaterial::new::<C>(discovery_credential)
+ }
+ }
+ impl MappingTrait<V1> for Marker {
+ type Output = PrecalculatedV1DiscoveryCryptoMaterial;
+ fn precalculate<C: CryptoProvider>(
+ discovery_credential: &V1DiscoveryCredential,
+ ) -> PrecalculatedV1DiscoveryCryptoMaterial {
+ discovery_credential.to_precalculated::<C>()
+ }
+ }
+}
+
+type PrecalculatedCryptoForProtocolVersion<V> =
+ <precalculated_for_version::Marker as precalculated_for_version::MappingTrait<V>>::Output;
+
+fn precalculate_crypto_material<V: ProtocolVersion, C: CryptoProvider>(
+ discovery_credential: &V::DiscoveryCredential,
+) -> PrecalculatedCryptoForProtocolVersion<V>
+where
+ precalculated_for_version::Marker: precalculated_for_version::MappingTrait<V>,
+{
+ <precalculated_for_version::Marker as precalculated_for_version::MappingTrait<V>>::precalculate::<
+ C,
+ >(discovery_credential)
+}
+
+/// Iterator type for [`CachedCredentialSource`].
+pub struct CachedCredentialSourceIterator<
+ 'a,
+ V: ProtocolVersion + 'a,
+ S: DiscoveryCredentialSource<'a, V> + 'a,
+ const N: usize,
+> where
+ precalculated_for_version::Marker: precalculated_for_version::MappingTrait<V>,
+{
+ /// The current index of the (enumerated) elements that we're iterating over.
+ /// Always counts up at every iteration, and may lie outside of the range
+ /// of the `cache` slice for uncached elements.
+ current_index: usize,
+ /// The cache of pre-calculated crypto-materials from the original source.
+ ///
+ /// The [`self.source_iterator`]'s (up-to) first `N` elements
+ /// must match (up-to) the first `N` elements in the cache,
+ /// and they must both appear in the same order.
+ cache: &'a [Option<PrecalculatedCryptoForProtocolVersion<V>>; N],
+ /// The iterator over the credentials in the original source
+ source_iterator: S::Iterator,
+}
+
+impl<'a, V: ProtocolVersion + 'a, S: DiscoveryCredentialSource<'a, V> + 'a, const N: usize> Iterator
+ for CachedCredentialSourceIterator<'a, V, S, N>
+where
+ precalculated_for_version::Marker: precalculated_for_version::MappingTrait<V>,
+{
+ type Item = (PossiblyCachedDiscoveryCryptoMaterial<'a, V>, S::Matched);
+ fn next(&mut self) -> Option<Self::Item> {
+ // Regardless of what we're going to do with the crypto-materials, we still need
+ // to advance the underlying iterator, and bail if it runs out.
+ let (discovery_credential, match_data) = self.source_iterator.next()?;
+ // Check whether/not we have cached crypto-material for the current index
+ let crypto = match self.cache.get(self.current_index) {
+ Some(precalculated_crypto_entry) => {
+ let precalculated_crypto = precalculated_crypto_entry
+ .as_ref()
+ .expect("iterator still going, but cache is not full?");
+ PossiblyCachedDiscoveryCryptoMaterial::from_precalculated(precalculated_crypto)
+ }
+ None => PossiblyCachedDiscoveryCryptoMaterial::from_discovery_credential(
+ discovery_credential.borrow().clone(),
+ ),
+ };
+ // Advance the index forward to point to the next item in the cache.
+ self.current_index += 1;
+ Some((crypto, match_data))
+ }
+}
+
+/// A [`CredentialSource`] which augments the externally-provided [`DiscoveryCredentialSource`] with
+/// a cache containing up to the specified number of pre-calculated credentials.
+pub struct CachedCredentialSource<V: ProtocolVersion, S, const N: usize>
+where
+ precalculated_for_version::Marker: precalculated_for_version::MappingTrait<V>,
+{
+ wrapped: S,
+ cache: [Option<PrecalculatedCryptoForProtocolVersion<V>>; N],
+}
+
+/// Helper function to construct a cache for a [`CachedCredentialSource`] from
+/// a reference to some source of discovery-credentials.
+///
+/// This function needs to be kept separate from [`CachedCredentialSource#new`]
+/// to get around otherwise-tricky lifetime issues around ephemerally-borrowing
+/// from a moved object within the body of a function.
+pub(crate) fn init_cache_from_source<'a, V: ProtocolVersion, S, const N: usize, P: CryptoProvider>(
+ original: &'a S,
+) -> [Option<PrecalculatedCryptoForProtocolVersion<V>>; N]
+where
+ S: DiscoveryCredentialSource<'a, V> + 'a,
+ precalculated_for_version::Marker: precalculated_for_version::MappingTrait<V>,
+{
+ let mut cache = [0u8; N].map(|_| None);
+ for (cache_entry, source_credential) in cache.iter_mut().zip(original.iter()) {
+ let (discovery_credential, _) = source_credential;
+ let precalculated_crypto_material =
+ precalculate_crypto_material::<V, P>(discovery_credential.borrow());
+ let _ = cache_entry.insert(precalculated_crypto_material);
+ }
+ cache
+}
+
+impl<'a, V: ProtocolVersion, S, const N: usize> CachedCredentialSource<V, S, N>
+where
+ S: DiscoveryCredentialSource<'a, V> + 'a,
+ precalculated_for_version::Marker: precalculated_for_version::MappingTrait<V>,
+{
+ /// Constructs a [`CachedCredentialSource`] from the given [`DiscoveryCredentialSource`]
+ /// and the given (initial) cache contents, as constructed via the
+ /// [`init_cache_from_source`] helper function.
+ pub(crate) fn new(
+ wrapped: S,
+ cache: [Option<PrecalculatedCryptoForProtocolVersion<V>>; N],
+ ) -> Self {
+ Self { wrapped, cache }
+ }
+}
+
+/// Internal implementation of [`PossiblyCachedDiscoveryCryptoMaterial`] to hide
+/// what crypto-material variants we're actually storing in cached
+/// credential-books.
+pub(crate) enum PossiblyCachedDiscoveryCryptoMaterialKind<'a, V: ProtocolVersion>
+where
+ precalculated_for_version::Marker: precalculated_for_version::MappingTrait<V>,
+{
+ Discovery(V::DiscoveryCredential),
+ Precalculated(&'a PrecalculatedCryptoForProtocolVersion<V>),
+}
+
+/// Crypto-materials that are potentially references to
+/// already-cached precomputed variants, or raw discovery
+/// credentials.
+pub struct PossiblyCachedDiscoveryCryptoMaterial<'a, V: ProtocolVersion>
+where
+ precalculated_for_version::Marker: precalculated_for_version::MappingTrait<V>,
+{
+ pub(crate) wrapped: PossiblyCachedDiscoveryCryptoMaterialKind<'a, V>,
+}
+
+impl<'a, V: ProtocolVersion> PossiblyCachedDiscoveryCryptoMaterial<'a, V>
+where
+ precalculated_for_version::Marker: precalculated_for_version::MappingTrait<V>,
+{
+ fn from_discovery_credential(discovery_credential: V::DiscoveryCredential) -> Self {
+ Self { wrapped: PossiblyCachedDiscoveryCryptoMaterialKind::Discovery(discovery_credential) }
+ }
+ fn from_precalculated(precalculated: &'a PrecalculatedCryptoForProtocolVersion<V>) -> Self {
+ Self { wrapped: PossiblyCachedDiscoveryCryptoMaterialKind::Precalculated(precalculated) }
+ }
+}
+
+impl<'a, V: ProtocolVersion> DiscoveryCryptoMaterial<V>
+ for PossiblyCachedDiscoveryCryptoMaterial<'a, V>
+where
+ precalculated_for_version::Marker: precalculated_for_version::MappingTrait<V>,
+{
+ fn metadata_nonce<C: CryptoProvider>(&self) -> [u8; 12] {
+ match &self.wrapped {
+ PossiblyCachedDiscoveryCryptoMaterialKind::Discovery(x) => x.metadata_nonce::<C>(),
+ PossiblyCachedDiscoveryCryptoMaterialKind::Precalculated(x) => x.metadata_nonce::<C>(),
+ }
+ }
+}
+
+impl<'a> V0DiscoveryCryptoMaterial for PossiblyCachedDiscoveryCryptoMaterial<'a, V0> {
+ fn ldt_adv_cipher<C: CryptoProvider>(&self) -> ldt_np_adv::LdtNpAdvDecrypterXtsAes128<C> {
+ match &self.wrapped {
+ PossiblyCachedDiscoveryCryptoMaterialKind::Discovery(x) => x.ldt_adv_cipher::<C>(),
+ PossiblyCachedDiscoveryCryptoMaterialKind::Precalculated(x) => x.ldt_adv_cipher::<C>(),
+ }
+ }
+}
+
+impl<'a> V1DiscoveryCryptoMaterial for PossiblyCachedDiscoveryCryptoMaterial<'a, V1> {
+ fn signed_identity_resolution_material<C: CryptoProvider>(
+ &self,
+ ) -> SignedSectionIdentityResolutionMaterial {
+ match &self.wrapped {
+ PossiblyCachedDiscoveryCryptoMaterialKind::Discovery(x) => {
+ x.signed_identity_resolution_material::<C>()
+ }
+ PossiblyCachedDiscoveryCryptoMaterialKind::Precalculated(x) => {
+ x.signed_identity_resolution_material::<C>()
+ }
+ }
+ }
+
+ fn unsigned_identity_resolution_material<C: CryptoProvider>(
+ &self,
+ ) -> UnsignedSectionIdentityResolutionMaterial {
+ match &self.wrapped {
+ PossiblyCachedDiscoveryCryptoMaterialKind::Discovery(x) => {
+ x.unsigned_identity_resolution_material::<C>()
+ }
+ PossiblyCachedDiscoveryCryptoMaterialKind::Precalculated(x) => {
+ x.unsigned_identity_resolution_material::<C>()
+ }
+ }
+ }
+
+ fn signed_verification_material<C: CryptoProvider>(&self) -> SignedSectionVerificationMaterial {
+ match &self.wrapped {
+ PossiblyCachedDiscoveryCryptoMaterialKind::Discovery(x) => {
+ x.signed_verification_material::<C>()
+ }
+ PossiblyCachedDiscoveryCryptoMaterialKind::Precalculated(x) => {
+ x.signed_verification_material::<C>()
+ }
+ }
+ }
+
+ fn unsigned_verification_material<C: CryptoProvider>(
+ &self,
+ ) -> UnsignedSectionVerificationMaterial {
+ match &self.wrapped {
+ PossiblyCachedDiscoveryCryptoMaterialKind::Discovery(x) => {
+ x.unsigned_verification_material::<C>()
+ }
+ PossiblyCachedDiscoveryCryptoMaterialKind::Precalculated(x) => {
+ x.unsigned_verification_material::<C>()
+ }
+ }
+ }
+}
+
+impl<'a, V: ProtocolVersion, S, const N: usize> CredentialSource<'a, V>
+ for CachedCredentialSource<V, S, N>
+where
+ Self: 'a,
+ S: DiscoveryCredentialSource<'a, V> + 'a,
+ precalculated_for_version::Marker: precalculated_for_version::MappingTrait<V>,
+ PossiblyCachedDiscoveryCryptoMaterial<'a, V>: DiscoveryCryptoMaterial<V>,
+{
+ type Matched = <S as DiscoveryCredentialSource<'a, V>>::Matched;
+ type Crypto = PossiblyCachedDiscoveryCryptoMaterial<'a, V>;
+ type Iterator = CachedCredentialSourceIterator<'a, V, S, N>;
+
+ fn iter(&'a self) -> Self::Iterator {
+ CachedCredentialSourceIterator {
+ current_index: 0,
+ cache: &self.cache,
+ source_iterator: self.wrapped.iter(),
+ }
+ }
+}
+
+/// A simple credentials source for environments which are,
+/// for all practical purposes, not space-constrained, and hence
+/// can store an arbitrary amount of pre-calculated crypto-materials.
+///
+/// Requires `alloc` as a result of internally leveraging a `Vec`.
+#[cfg(feature = "alloc")]
+pub struct PrecalculatedOwnedCredentialSource<V: ProtocolVersion, M: MatchedCredential>
+where
+ precalculated_for_version::Marker: precalculated_for_version::MappingTrait<V>,
+{
+ credentials: Vec<(PrecalculatedCryptoForProtocolVersion<V>, M)>,
+}
+
+#[cfg(feature = "alloc")]
+impl<'a, V: ProtocolVersion + 'a, M: MatchedCredential + 'a>
+ PrecalculatedOwnedCredentialSource<V, M>
+where
+ precalculated_for_version::Marker: precalculated_for_version::MappingTrait<V>,
+{
+ /// Pre-calculates crypto material for the given credentials, and constructs a
+ /// credentials source which holds owned copies of this crypto-material.
+ pub fn new<P: CryptoProvider>(
+ credential_iter: impl IntoIterator<Item = MatchableCredential<V, M>>,
+ ) -> Self {
+ let credentials = credential_iter
+ .into_iter()
+ .map(|credential| {
+ (
+ precalculate_crypto_material::<V, P>(&credential.discovery_credential),
+ credential.match_data,
+ )
+ })
+ .collect();
+ Self { credentials }
+ }
+}
+
+#[cfg(feature = "alloc")]
+fn reference_crypto_and_match_data<C, M: MatchedCredential>(
+ pair_ref: &(C, M),
+) -> (&C, ReferencedMatchedCredential<M>) {
+ let (c, m) = pair_ref;
+ (c, m.into())
+}
+
+#[cfg(feature = "alloc")]
+impl<'a, V: ProtocolVersion, M: MatchedCredential> CredentialSource<'a, V>
+ for PrecalculatedOwnedCredentialSource<V, M>
+where
+ Self: 'a,
+ precalculated_for_version::Marker: precalculated_for_version::MappingTrait<V>,
+ &'a PrecalculatedCryptoForProtocolVersion<V>: DiscoveryCryptoMaterial<V>,
+{
+ type Matched = ReferencedMatchedCredential<'a, M>;
+ type Crypto = &'a PrecalculatedCryptoForProtocolVersion<V>;
+ type Iterator = core::iter::Map<
+ core::slice::Iter<'a, (PrecalculatedCryptoForProtocolVersion<V>, M)>,
+ fn(
+ &'a (PrecalculatedCryptoForProtocolVersion<V>, M),
+ )
+ -> (&'a PrecalculatedCryptoForProtocolVersion<V>, ReferencedMatchedCredential<'a, M>),
+ >;
+
+ fn iter(&'a self) -> Self::Iterator {
+ self.credentials.iter().map(reference_crypto_and_match_data)
+ }
+}
+
+/// Type-alias for credential sources which are provided via slice credential-sources
+/// with a pre-calculated credential cache layered on top.
+pub type CachedSliceCredentialSource<'a, V, M, const N: usize> =
+ CachedCredentialSource<V, SliceCredentialSource<'a, V, M>, N>;
+
+/// A [`CredentialBook`] whose sources for V0 and V1 credentials come from the given slices
+/// of discovery credentials, with crypto-materials for up to the given number of credentials
+/// from the beginning of each slice kept in an in-memory cache.
+pub type CachedSliceCredentialBook<'a, M, const N0: usize, const N1: usize> =
+ CredentialBookFromSources<
+ CachedSliceCredentialSource<'a, V0, M, N0>,
+ CachedSliceCredentialSource<'a, V1, M, N1>,
+ >;
+
+#[cfg(feature = "alloc")]
+/// A credential-book which owns all of its (non-matched) credential data,
+/// and maintains pre-calculated cryptographic information about all
+/// stored credentials for speedy advertisement deserialization.
+///
+/// Use this credential book if memory usage is not terribly tight,
+/// and you're operating in an environment with an allocator.
+pub type PrecalculatedOwnedCredentialBook<M> = CredentialBookFromSources<
+ PrecalculatedOwnedCredentialSource<V0, M>,
+ PrecalculatedOwnedCredentialSource<V1, M>,
+>;
diff --git a/nearby/presence/np_adv/src/credential/mod.rs b/nearby/presence/np_adv/src/credential/mod.rs
index a138f0b..3cab51e 100644
--- a/nearby/presence/np_adv/src/credential/mod.rs
+++ b/nearby/presence/np_adv/src/credential/mod.rs
@@ -18,68 +18,375 @@
//! efficiency gains with implementations tailored to suit (e.g. caching a few hot credentials
//! rather than reading from disk every time, etc).
-use core::fmt::Debug;
-use crypto_provider::CryptoProvider;
+use crate::MetadataKey;
+
+use crate::credential::v0::{V0DiscoveryCredential, V0ProtocolVersion};
-use self::{
- simple::{SimpleV0Credential, SimpleV1Credential},
- source::OwnedBothCredentialSource,
- v0::MinimumFootprintV0CryptoMaterial,
- v1::MinimumFootprintV1CryptoMaterial,
-};
+use core::convert::Infallible;
+use core::fmt::Debug;
+use crypto_provider::{CryptoProvider, CryptoRng};
-pub mod simple;
+pub mod book;
pub mod source;
+#[cfg(test)]
+pub mod tests;
pub mod v0;
pub mod v1;
-/// A credential which has an associated [`MatchedCredential`]
-pub trait MatchableCredential {
- /// The [MatchedCredential] provided by this [`MatchableCredential`].
- type Matched<'m>: MatchedCredential<'m>
- where
- Self: 'm;
-
- /// Returns the subset of credential data that should be associated with a successfully
- /// decrypted advertisement or advertisement section.
- fn matched(&self) -> Self::Matched<'_>;
+/// Information about a credential as supplied by the caller.
+#[derive(Clone)]
+pub struct MatchableCredential<V: ProtocolVersion, M: MatchedCredential> {
+ /// The discovery credential/cryptographic information associated
+ /// with this particular credential which is used for discovering
+ /// advertisements/advertisement sections generated via the
+ /// paired sender credential.
+ pub discovery_credential: V::DiscoveryCredential,
+ /// The data which will be yielded back to the caller upon a successful
+ /// identity-match against this credential.
+ pub match_data: M,
}
-/// Convenient type-level function for referring to the match data for a [`MatchableCredential`].
-pub type MatchedCredFromCred<'s, C> = <C as MatchableCredential>::Matched<'s>;
+impl<V: ProtocolVersion, M: MatchedCredential> MatchableCredential<V, M> {
+ /// De-structures this credential into the pairing of a discovery
+ /// credential and some matched credential data.
+ pub fn into_pair(self) -> (V::DiscoveryCredential, M) {
+ (self.discovery_credential, self.match_data)
+ }
+ /// Views this credential as a (borrowed) discovery-credential
+ /// combined with some matched credential data
+ /// (which is copied - see documentation on [`MatchedCredential`])
+ pub fn as_pair(&self) -> (&V::DiscoveryCredential, ReferencedMatchedCredential<M>) {
+ (&self.discovery_credential, ReferencedMatchedCredential::from(&self.match_data))
+ }
+}
/// The portion of a credential's data to be bundled with the advertisement content it was used to
-/// decrypt.
+/// decrypt. At a minimum, this includes any encrypted identity-specific metadata.
///
-/// As it is `Debug` and `Eq`, implementors should not hold any cryptographic material to avoid
+/// As it is `Debug` and `Eq`, implementors should not hold any cryptographic secrets to avoid
/// accidental logging, timing side channels on comparison, etc, or should use custom impls of
/// those traits rather than deriving them.
-pub trait MatchedCredential<'m>: Debug + PartialEq + Eq {}
+///
+/// Instances of `MatchedCredential` may be cloned whenever advertisement content is
+/// successfully associated with a credential (see [`crate::WithMatchedCredential`]). As a
+/// result, it's recommended to use matched-credentials which reference
+/// some underlying match-data, but don't necessarily own it.
+/// See [`ReferencedMatchedCredential`] for the most common case of shared references.
+pub trait MatchedCredential: Debug + PartialEq + Eq + Clone {
+ /// The type returned for successful calls to [`Self::fetch_encrypted_metadata`].
+ type EncryptedMetadata: AsRef<[u8]>;
-/// A V0 credential containing some [`v0::V0CryptoMaterial`]
-pub trait V0Credential: MatchableCredential {
- /// The [v0::V0CryptoMaterial] provided by this V0Credential impl.
- type CryptoMaterial: v0::V0CryptoMaterial;
+ /// The type of errors for [`Self::fetch_encrypted_metadata`].
+ type EncryptedMetadataFetchError: Debug;
- /// Returns the crypto material associated with the credential.
+ /// Attempts to obtain the (AES-GCM)-encrypted metadata bytes for the credential,
+ /// with possible failure based on the availability of the underlying data (i.e:
+ /// failing disk reads.)
///
- /// Used to decrypted encrypted advertisement content.
- fn crypto_material(&self) -> &Self::CryptoMaterial;
+ /// If your implementation does not maintain any encrypted metadata for each credential,
+ /// you may simply return an empty byte-array from this method.
+ ///
+ /// If your method for obtaining metadata cannot fail, use
+ /// the `core::convert::Infallible` type for the error type
+ /// [`Self::EncryptedMetadataFetchError`].
+ fn fetch_encrypted_metadata(
+ &self,
+ ) -> Result<Self::EncryptedMetadata, Self::EncryptedMetadataFetchError>;
}
-/// A V1 credential containing some [`v1::V1CryptoMaterial`]
-pub trait V1Credential: MatchableCredential {
- /// The [v1::V1CryptoMaterial] provided by this Credential impl.
- type CryptoMaterial: v1::V1CryptoMaterial;
+/// [`MatchedCredential`] wrapper around a shared reference to a [`MatchedCredential`].
+/// This is done instead of providing a blanket impl of [`MatchedCredential`] for
+/// reference types to allow for downstream crates to impl [`MatchedCredential`] on
+/// specific reference types.
+#[derive(Clone, Debug, PartialEq, Eq)]
+pub struct ReferencedMatchedCredential<'a, M: MatchedCredential> {
+ wrapped: &'a M,
+}
- /// Returns the crypto material associated with the credential.
- ///
- /// Used to decrypt encrypted advertisement content.
- fn crypto_material(&self) -> &Self::CryptoMaterial;
+impl<'a, M: MatchedCredential> From<&'a M> for ReferencedMatchedCredential<'a, M> {
+ fn from(wrapped: &'a M) -> Self {
+ Self { wrapped }
+ }
+}
+
+impl<'a, M: MatchedCredential> AsRef<M> for ReferencedMatchedCredential<'a, M> {
+ fn as_ref(&self) -> &M {
+ self.wrapped
+ }
+}
+
+impl<'a, M: MatchedCredential> MatchedCredential for ReferencedMatchedCredential<'a, M> {
+ type EncryptedMetadata = <M as MatchedCredential>::EncryptedMetadata;
+ type EncryptedMetadataFetchError = <M as MatchedCredential>::EncryptedMetadataFetchError;
+ fn fetch_encrypted_metadata(
+ &self,
+ ) -> Result<Self::EncryptedMetadata, Self::EncryptedMetadataFetchError> {
+ self.wrapped.fetch_encrypted_metadata()
+ }
+}
+
+/// A simple implementation of [`MatchedCredential`] where all match-data
+/// is contained in the encrypted metadata byte-field.
+#[derive(Debug, PartialEq, Eq, Clone)]
+pub struct MetadataMatchedCredential<A: AsRef<[u8]> + Clone + Debug + PartialEq + Eq> {
+ encrypted_metadata: A,
}
-/// An owned credential store that contains minimum footprint crypto materials
-pub type MinimumFootprintCredentialSource<T> = OwnedBothCredentialSource<
- SimpleV0Credential<MinimumFootprintV0CryptoMaterial, T>,
- SimpleV1Credential<MinimumFootprintV1CryptoMaterial, T>,
->;
+#[cfg(any(test, feature = "alloc"))]
+impl MetadataMatchedCredential<alloc::vec::Vec<u8>> {
+ /// Builds a [`MetadataMatchedCredential`] whose contents are given
+ /// as plaintext to be encrypted using AES-GCM against the given
+ /// broadcast crypto-material.
+ pub fn encrypt_from_plaintext<V, B, C>(broadcast_cm: &B, plaintext_metadata: &[u8]) -> Self
+ where
+ V: ProtocolVersion,
+ B: BroadcastCryptoMaterial<V>,
+ C: CryptoProvider,
+ {
+ let encrypted_metadata = broadcast_cm.encrypt_metadata::<C>(plaintext_metadata);
+ Self { encrypted_metadata }
+ }
+}
+
+impl<A: AsRef<[u8]> + Clone + Debug + PartialEq + Eq> MetadataMatchedCredential<A> {
+ /// Builds a new [`MetadataMatchedCredential`] with the given
+ /// encrypted metadata.
+ pub fn new(encrypted_metadata: A) -> Self {
+ Self { encrypted_metadata }
+ }
+}
+
+impl<A: AsRef<[u8]> + Clone + Debug + PartialEq + Eq> MatchedCredential
+ for MetadataMatchedCredential<A>
+{
+ type EncryptedMetadata = A;
+ type EncryptedMetadataFetchError = Infallible;
+ fn fetch_encrypted_metadata(&self) -> Result<Self::EncryptedMetadata, Infallible> {
+ Ok(self.encrypted_metadata.clone())
+ }
+}
+
+/// Trivial implementation of [`MatchedCredential`] which consists of no match-data.
+/// Suitable for usage scenarios where the decoded advertisement contents matter,
+/// but not necessarily which devices generated the contents.
+///
+/// Attempting to obtain the encrypted metadata from this type of credential
+/// will always yield an empty byte-array.
+#[derive(Default, Debug, PartialEq, Eq, Clone)]
+pub struct EmptyMatchedCredential;
+
+impl MatchedCredential for EmptyMatchedCredential {
+ type EncryptedMetadata = [u8; 0];
+ type EncryptedMetadataFetchError = Infallible;
+ fn fetch_encrypted_metadata(
+ &self,
+ ) -> Result<Self::EncryptedMetadata, Self::EncryptedMetadataFetchError> {
+ Ok([0u8; 0])
+ }
+}
+
+#[cfg(any(test, feature = "devtools"))]
+/// A [`MatchedCredential`] which consists only of the `key_seed` in the crypto-material
+/// for the credential. Note that this is unique per-credential by construction,
+/// and so this provides natural match-data for credentials in settings where
+/// there may not be any other information available.
+///
+/// Since this matched-credential type contains cryptographic information mirroring
+/// a credential's crypto-material, this structure is not suitable for production
+/// usage outside of unit tests and dev-tools.
+///
+/// Additionally, note that the metadata on this particular kind of matched credential
+/// is deliberately made inaccessible. This is done because a key-seed representation
+/// is only suitable in very limited circumstances where no other meaningful
+/// identifying information is available, such as that which is contained in metadata.
+/// Attempting to obtain the encrypted metadata from this type of matched credential
+/// will always yield an empty byte-array.
+#[derive(Default, Debug, PartialEq, Eq, Clone)]
+pub struct KeySeedMatchedCredential {
+ key_seed: [u8; 32],
+}
+
+#[cfg(any(test, feature = "devtools"))]
+impl From<[u8; 32]> for KeySeedMatchedCredential {
+ fn from(key_seed: [u8; 32]) -> Self {
+ Self { key_seed }
+ }
+}
+#[cfg(any(test, feature = "devtools"))]
+impl From<KeySeedMatchedCredential> for [u8; 32] {
+ fn from(matched: KeySeedMatchedCredential) -> Self {
+ matched.key_seed
+ }
+}
+
+#[cfg(any(test, feature = "devtools"))]
+impl MatchedCredential for KeySeedMatchedCredential {
+ type EncryptedMetadata = [u8; 0];
+ type EncryptedMetadataFetchError = Infallible;
+ fn fetch_encrypted_metadata(
+ &self,
+ ) -> Result<Self::EncryptedMetadata, Self::EncryptedMetadataFetchError> {
+ Ok([0u8; 0])
+ }
+}
+
+/// Error returned when metadata decryption fails.
+#[derive(Debug, Eq, PartialEq)]
+pub struct MetadataDecryptionError;
+
+/// Seal for protocol versions to enforce totality.
+pub(crate) mod protocol_version_seal {
+ /// Internal-only supertrait of protocol versions
+ /// for the purpose of sealing the trait.
+ pub trait ProtocolVersionSeal: Clone {}
+}
+
+/// Marker trait for protocol versions (V0/V1)
+/// and associated data about them.
+pub trait ProtocolVersion: protocol_version_seal::ProtocolVersionSeal {
+ /// The discovery credential type for this protocol version, which
+ /// is the minimal amount of cryptographic materials that we need
+ /// in order to discover advertisements/sections which make
+ /// use of the sender-paired version of the credential.
+ type DiscoveryCredential: DiscoveryCryptoMaterial<Self> + Clone;
+
+ /// The native-length metadata key for this protocol version
+ /// [i.e: if V0, a 14-byte metadata key, or if V1, a 16-byte
+ /// metadata key.]
+ type MetadataKey: Clone + AsRef<[u8]>;
+
+ /// Computes the metadata nonce for this version from the given key-seed.
+ fn metadata_nonce_from_key_seed<C: CryptoProvider>(key_seed: &[u8; 32]) -> [u8; 12];
+
+ /// Expands the passed metadata key (if needed) to a 16-byte metadata key
+ /// which may be used for metadata encryption/decryption
+ fn expand_metadata_key<C: CryptoProvider>(metadata_key: Self::MetadataKey) -> MetadataKey;
+
+ /// Generates a random metadata key using the given cryptographically-secure Rng
+ fn gen_random_metadata_key<R: CryptoRng>(rng: &mut R) -> Self::MetadataKey;
+
+ #[cfg(any(test, feature = "alloc"))]
+ /// Decrypt the given metadata using the given metadata nonce and version-specific
+ /// metadata key. Returns [`MetadataDecryptionError`] in the case that
+ /// the decryption operation failed.
+ fn decrypt_metadata<C: CryptoProvider>(
+ metadata_nonce: [u8; 12],
+ metadata_key: Self::MetadataKey,
+ encrypted_metadata: &[u8],
+ ) -> Result<alloc::vec::Vec<u8>, MetadataDecryptionError> {
+ use crypto_provider::{
+ aead::{Aead, AeadInit},
+ aes::Aes128Key,
+ };
+
+ let metadata_key = Self::expand_metadata_key::<C>(metadata_key);
+ let metadata_key = Aes128Key::from(metadata_key.0);
+ let aead = <<C as CryptoProvider>::Aes128Gcm as AeadInit<Aes128Key>>::new(&metadata_key);
+ // No additional authenticated data for encrypted metadata.
+ aead.decrypt(encrypted_metadata, &[], &metadata_nonce).map_err(|_| MetadataDecryptionError)
+ }
+}
+
+/// Trait for structures which provide cryptographic
+/// materials for discovery in a particular protocol version.
+/// See [`crate::credential::v0::V0DiscoveryCryptoMaterial`]
+/// and [`crate::credential::v1::V1DiscoveryCryptoMaterial`]
+/// for V0 and V1 specializations.
+pub trait DiscoveryCryptoMaterial<V: ProtocolVersion> {
+ /// Constructs or copies the metadata nonce used for decryption of associated credential
+ /// metadata for the identity represented via this crypto material.
+ fn metadata_nonce<C: CryptoProvider>(&self) -> [u8; 12];
+}
+
+/// Cryptographic materials necessary for broadcasting encrypted
+/// advertisement contents with the given protocol version.
+pub trait BroadcastCryptoMaterial<V: ProtocolVersion> {
+ /// Yields a copy of the key seed to be used to derive other key materials used
+ /// in the encryption of broadcasted advertisement contents.
+ fn key_seed(&self) -> [u8; 32];
+
+ /// Yields a copy of the metadata-key (size dependent on protocol version)
+ /// to tag advertisement contents sent with this broadcast crypto-material.
+ fn metadata_key(&self) -> V::MetadataKey;
+
+ /// Yields the 16-byte expanded metadata key, suitable for metadata encryption.
+ fn expanded_metadata_key<C: CryptoProvider>(&self) -> MetadataKey {
+ V::expand_metadata_key::<C>(self.metadata_key())
+ }
+
+ /// Constructs the metadata nonce used for encryption of associated credential
+ /// metadata for the identity represented via this crypto material.
+ fn metadata_nonce<C: CryptoProvider>(&self) -> [u8; 12] {
+ V::metadata_nonce_from_key_seed::<C>(&self.key_seed())
+ }
+
+ /// Derives a V0 discovery credential from this V0 broadcast crypto-material
+ /// which may be used to discover v0 advertisements broadcasted with this credential.`
+ fn derive_v0_discovery_credential<C: CryptoProvider>(&self) -> V0DiscoveryCredential
+ where
+ V: V0ProtocolVersion,
+ {
+ let key_seed = self.key_seed();
+ let hkdf = np_hkdf::NpKeySeedHkdf::<C>::new(&key_seed);
+ let metadata_key_hmac =
+ hkdf.legacy_metadata_key_hmac_key().calculate_hmac(self.metadata_key().as_ref());
+ V0DiscoveryCredential::new(key_seed, metadata_key_hmac)
+ }
+
+ #[cfg(any(test, feature = "alloc"))]
+ /// Encrypts the given plaintext metadata bytes to allow that metadata
+ /// to be shared with receiving devices.
+ fn encrypt_metadata<C: CryptoProvider>(
+ &self,
+ plaintext_metadata: &[u8],
+ ) -> alloc::vec::Vec<u8> {
+ use crypto_provider::{
+ aead::{Aead, AeadInit},
+ aes::Aes128Key,
+ };
+ let plaintext_metadata_key = self.expanded_metadata_key::<C>();
+ let plaintext_metadata_key = Aes128Key::from(plaintext_metadata_key.0);
+
+ let aead =
+ <<C as CryptoProvider>::Aes128Gcm as AeadInit<Aes128Key>>::new(&plaintext_metadata_key);
+ // No additional authenticated data for encrypted metadata.
+ aead.encrypt(plaintext_metadata, &[], &self.metadata_nonce::<C>())
+ .expect("Metadata encryption should be infallible")
+ }
+}
+
+/// Concrete implementation of [`BroadcastCryptoMaterial<V>`] for
+/// a particular protocol version which keeps the key seed
+/// and the metadata key contiguous in memory.
+///
+/// Broadcast crypto-material specified in this way will only
+/// be usable for (unsigned) advertisement content broadcasts
+/// in the given protocol version.
+///
+/// For more flexible expression of broadcast credentials,
+/// feel free to directly implement one or more of the
+/// [`BroadcastCryptoMaterial`] and/or
+/// [`crate::credential::v1::SignedBroadcastCryptoMaterial`]
+/// traits on your own struct, dependent on the details
+/// of your own broadcast credentials.
+pub struct SimpleBroadcastCryptoMaterial<V: ProtocolVersion> {
+ key_seed: [u8; 32],
+ metadata_key: V::MetadataKey,
+}
+
+impl<V: ProtocolVersion> SimpleBroadcastCryptoMaterial<V> {
+ /// Builds some simple broadcast crypto-materials out of
+ /// the provided key-seed and version-specific metadata-key.
+ pub fn new(key_seed: [u8; 32], metadata_key: V::MetadataKey) -> Self {
+ Self { key_seed, metadata_key }
+ }
+}
+
+impl<V: ProtocolVersion> BroadcastCryptoMaterial<V> for SimpleBroadcastCryptoMaterial<V> {
+ fn key_seed(&self) -> [u8; 32] {
+ self.key_seed
+ }
+ fn metadata_key(&self) -> V::MetadataKey {
+ self.metadata_key.clone()
+ }
+}
diff --git a/nearby/presence/np_adv/src/credential/simple.rs b/nearby/presence/np_adv/src/credential/simple.rs
deleted file mode 100644
index 6ea2436..0000000
--- a/nearby/presence/np_adv/src/credential/simple.rs
+++ /dev/null
@@ -1,148 +0,0 @@
-// Copyright 2023 Google LLC
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-// http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-//! Simple implementations of credentials. These can be combined with the provided crypto material
-//! implementations to have a working credential type.
-//!
-//! ```rust
-//! use np_adv::credential::{
-//! simple::SimpleV0Credential,
-//! v0::MinimumFootprintV0CryptoMaterial,
-//! };
-//! type MyV0Credential = SimpleV0Credential<MinimumFootprintV0CryptoMaterial, ()>;
-//! ```
-
-use core::fmt::Debug;
-
-use super::*;
-use super::{v0::*, v1::*};
-
-/// A simple implementation of [`V0Credential`] that wraps a [`V0CryptoMaterial`] and some `T` data that
-/// will be exposed via the [`MatchedCredential`].
-pub struct SimpleV0Credential<C, T>
-where
- C: V0CryptoMaterial,
- T: Debug + Eq,
-{
- material: C,
- match_data: T,
-}
-
-impl<C, T> SimpleV0Credential<C, T>
-where
- C: V0CryptoMaterial,
- T: Debug + Eq,
-{
- /// Construct a new credential.
- ///
- /// `material` will be returned by [V0Credential::crypto_material].
- /// `match_data` will be returned by [SimpleV0Credential::matched], wrapped in [SimpleMatchedCredential].
- pub fn new(material: C, match_data: T) -> Self {
- Self { material, match_data }
- }
-}
-
-impl<C, T> MatchableCredential for SimpleV0Credential<C, T>
-where
- C: V0CryptoMaterial,
- T: Debug + Eq,
-{
- type Matched<'m> = SimpleMatchedCredential<'m, T> where Self: 'm;
-
- fn matched(&'_ self) -> Self::Matched<'_> {
- SimpleMatchedCredential { data: &self.match_data }
- }
-}
-
-impl<C, T> V0Credential for SimpleV0Credential<C, T>
-where
- C: V0CryptoMaterial,
- T: Debug + Eq,
-{
- type CryptoMaterial = C;
-
- fn crypto_material(&self) -> &Self::CryptoMaterial {
- &self.material
- }
-}
-
-/// A simple implementation of [V1Credential] that wraps a [V1CryptoMaterial] and some `T` data that
-/// will be exposed via the [MatchedCredential].
-pub struct SimpleV1Credential<C, T>
-where
- C: V1CryptoMaterial,
- T: Debug + Eq,
-{
- material: C,
- match_data: T,
-}
-
-impl<C, T> SimpleV1Credential<C, T>
-where
- C: V1CryptoMaterial,
- T: Debug + Eq,
-{
- /// Construct a new credential.
- ///
- /// `material` will be returned by [V1Credential::crypto_material].
- /// `match_data` will be returned by [SimpleV1Credential::matched], wrapped in [SimpleMatchedCredential].
- pub fn new(material: C, match_data: T) -> Self {
- Self { material, match_data }
- }
-}
-
-impl<C, T> MatchableCredential for SimpleV1Credential<C, T>
-where
- C: V1CryptoMaterial,
- T: Debug + Eq,
-{
- type Matched<'m> = SimpleMatchedCredential<'m, T> where Self: 'm;
-
- fn matched(&'_ self) -> Self::Matched<'_> {
- SimpleMatchedCredential { data: &self.match_data }
- }
-}
-
-impl<C, T> V1Credential for SimpleV1Credential<C, T>
-where
- C: V1CryptoMaterial,
- T: Debug + Eq,
-{
- type CryptoMaterial = C;
-
- fn crypto_material(&self) -> &Self::CryptoMaterial {
- &self.material
- }
-}
-
-/// The [MatchedCredential] used by [SimpleV0Credential]
-/// and by [SimpleV1Credential].
-#[derive(Debug, PartialEq, Eq)]
-pub struct SimpleMatchedCredential<'m, T: Debug + PartialEq + Eq> {
- data: &'m T,
-}
-
-impl<'m, T: Debug + PartialEq + Eq> SimpleMatchedCredential<'m, T> {
- /// Construct a new instance that wraps `data`.
- pub fn new(data: &'m T) -> Self {
- Self { data }
- }
-
- /// Returns the underlying matched credential data.
- pub fn matched_data(&self) -> &'m T {
- self.data
- }
-}
-
-impl<'m, T: Debug + PartialEq + Eq> MatchedCredential<'m> for SimpleMatchedCredential<'m, T> {}
diff --git a/nearby/presence/np_adv/src/credential/source.rs b/nearby/presence/np_adv/src/credential/source.rs
index e102b7e..709bbd6 100644
--- a/nearby/presence/np_adv/src/credential/source.rs
+++ b/nearby/presence/np_adv/src/credential/source.rs
@@ -12,139 +12,135 @@
// See the License for the specific language governing permissions and
// limitations under the License.
-//! Traits defining sources for credentials. These are used in the deserialization path to provide
-//! the credentials to try. [`SliceCredentialSource`] and [`OwnedCredentialSource`] implementations
-//! are also defined in this module.
-
-use super::*;
-use alloc::vec::Vec;
-
-/// A source of credentials to try when decrypting advertisements,
-/// which really just wraps an iterator over a given credential type.
-pub trait CredentialSource<C: MatchableCredential> {
- /// The iterator type produced that emits credentials
- type Iterator<'a>: Iterator<Item = &'a C>
- where
- Self: 'a,
- C: 'a;
-
- /// Iterate over the available credentials
- fn iter(&self) -> Self::Iterator<'_>;
-}
-
-/// Trait for combined credential sources able to yield credential sources for both V0 and V1.
-pub trait BothCredentialSource<C0, C1>
+//! Definitions of traits for structures which supply
+//! credentials for discovering advertisements/advertisement
+//! sections for a _particular_ protocol version.
+
+use crate::credential::{
+ DiscoveryCryptoMaterial, MatchableCredential, MatchedCredential, ProtocolVersion,
+ ReferencedMatchedCredential,
+};
+use core::borrow::Borrow;
+
+/// Specialized version of the [`CredentialSource`] trait for
+/// credential-sources which provide discovery credentials
+/// for a specific protocol version.
+///
+/// If you want ready-made structures which can provide
+/// credentials for both V0 and V1 protocol versions,
+/// see [`crate::credential::book::CredentialBook`]
+/// and [`crate::credential::book::CredentialBookBuilder`] instead.
+///
+/// It's preferred to use this kind of credential-source
+/// in client code, if possible, and then lift to a
+/// [`CredentialSource`] using [`AsCredentialSource`]
+/// instead of implementing [`CredentialSource`] directly,
+/// since it's better to trust this crate to handle
+/// the details of what's in [`DiscoveryCryptoMaterial`]s
+/// for specific protocol versions.
+pub trait DiscoveryCredentialSource<'a, V: ProtocolVersion>
where
- C0: V0Credential,
- C1: V1Credential,
+ Self: 'a,
{
- /// The type of the underlying credential-source for v0 credentials
- type V0Source: CredentialSource<C0>;
- /// The type of the underlying credential-source for v1 credentials
- type V1Source: CredentialSource<C1>;
+ /// The kind of data yielded to the caller upon a successful
+ /// identity-match.
+ type Matched: MatchedCredential;
- /// Gets a source for v0 credentials maintained by this `BothCredentialSource`.
- fn v0(&self) -> &Self::V0Source;
+ /// The kind of crypto-material yielded from the wrapped
+ /// iterator, which allows borrowing a discovery credential.
+ type Crypto: DiscoveryCryptoMaterial<V> + Borrow<V::DiscoveryCredential>;
- /// Gets a source for v1 credentials maintained by this `BothCredentialSource`.
- fn v1(&self) -> &Self::V1Source;
+ /// The iterator type produced which emits credentials.
+ /// This is a lending iterator which may borrow things from `self`.
+ type Iterator: Iterator<Item = (Self::Crypto, Self::Matched)>;
- /// Convenient function alias to [`self.v0().iter()`] for iterating
- /// over v0 credentials.
- fn iter_v0(&self) -> <Self::V0Source as CredentialSource<C0>>::Iterator<'_> {
- self.v0().iter()
- }
-
- /// Convenient function alias to the [`CredentialSource<C1>#iter()`] for iterating
- /// over v0 credentials.
- fn iter_v1(&self) -> <Self::V1Source as CredentialSource<C1>>::Iterator<'_> {
- self.v1().iter()
- }
+ /// Iterate over the available credentials
+ fn iter(&'a self) -> Self::Iterator;
}
-/// A simple [CredentialSource] that just iterates over a provided slice of credentials
-pub struct SliceCredentialSource<'c, C: MatchableCredential> {
- credentials: &'c [C],
-}
+/// A source of credentials for a particular protocol version,
+/// utilizing any [`DiscoveryCryptoMaterial`] which is usable
+/// for discovering advertisements in that protocol version.
+///
+/// This trait is largely leveraged as a tool for building
+/// new kinds of [`crate::credential::book::CredentialBook`]s
+/// via the [`crate::credential::book::CredentialBookFromSources`]
+/// wrapper. It differs from the [`DiscoveryCredentialSource`]
+/// trait in that the crypto-materials do not have to be
+/// discovery credentials, and can instead be some pre-calculated
+/// crypto-materials.
+///
+/// See [`crate::credential::book::CachedCredentialSource`]
+/// for an example of this pattern.
+pub trait CredentialSource<'a, V: ProtocolVersion>
+where
+ Self: 'a,
+{
+ /// The kind of data yielded to the caller upon a successful
+ /// identity-match.
+ type Matched: MatchedCredential;
-impl<'c, C: MatchableCredential> SliceCredentialSource<'c, C> {
- /// Construct the credential source from the provided credentials.
- pub fn new(credentials: &'c [C]) -> Self {
- Self { credentials }
- }
-}
+ /// The kind of crypto-material yielded from the wrapped
+ /// iterator.
+ type Crypto: DiscoveryCryptoMaterial<V>;
-impl<'c, C: MatchableCredential> CredentialSource<C> for SliceCredentialSource<'c, C> {
- type Iterator<'i> = core::slice::Iter<'i, C>
- where Self: 'i;
+ /// The iterator type produced which emits credentials.
+ /// This is a lending iterator which may borrow things from `self`.
+ type Iterator: Iterator<Item = (Self::Crypto, Self::Matched)>;
- fn iter(&'_ self) -> Self::Iterator<'_> {
- self.credentials.iter()
- }
-}
-
-/// A simple credential source which owns all of its credentials.
-pub struct OwnedCredentialSource<C: MatchableCredential> {
- credentials: Vec<C>,
+ /// Iterate over the available credentials
+ fn iter(&'a self) -> Self::Iterator;
}
-impl<C: MatchableCredential> OwnedCredentialSource<C> {
- /// Constructs an owned credential source from the given credentials
- pub fn new(credentials: Vec<C>) -> Self {
- Self { credentials }
- }
-}
+// Note: This is needed to get around coherence problems
+// with the [`CredentialSource`] trait's relationship
+// with [`DiscoveryCredentialSource`] if it were declared
+// as a sub-trait (i.e: conflicting impls)
+/// Wrapper which turns any [`DiscoveryCredentialSource`]
+/// into a [`CredentialSource`].
+pub struct AsCredentialSource<S>(pub S);
-impl<C: MatchableCredential> CredentialSource<C> for OwnedCredentialSource<C> {
- type Iterator<'i> = core::slice::Iter<'i, C>
- where Self: 'i;
+impl<'a, V: ProtocolVersion, S: DiscoveryCredentialSource<'a, V>> CredentialSource<'a, V>
+ for AsCredentialSource<S>
+{
+ type Matched = <S as DiscoveryCredentialSource<'a, V>>::Matched;
+ type Crypto = <S as DiscoveryCredentialSource<'a, V>>::Crypto;
+ type Iterator = <S as DiscoveryCredentialSource<'a, V>>::Iterator;
- fn iter(&'_ self) -> Self::Iterator<'_> {
- self.credentials.iter()
+ fn iter(&'a self) -> Self::Iterator {
+ self.0.iter()
}
}
-/// An owned credential source for both v0 and v1 credentials,
-pub struct OwnedBothCredentialSource<C0, C1>
-where
- C0: V0Credential,
- C1: V1Credential,
-{
- v0_source: OwnedCredentialSource<C0>,
- v1_source: OwnedCredentialSource<C1>,
+/// A simple [`DiscoveryCredentialSource`] which iterates over a provided slice of credentials
+pub struct SliceCredentialSource<'c, V: ProtocolVersion, M: MatchedCredential> {
+ credentials: &'c [MatchableCredential<V, M>],
}
-impl<C0, C1> OwnedBothCredentialSource<C0, C1>
-where
- C0: V0Credential,
- C1: V1Credential,
-{
- /// Creates a new `OwnedBothCredentialSource` from credential-lists
- /// for both V0 and V1
- pub fn new(v0_credentials: Vec<C0>, v1_credentials: Vec<C1>) -> Self {
- let v0_source = OwnedCredentialSource::new(v0_credentials);
- let v1_source = OwnedCredentialSource::new(v1_credentials);
- Self { v0_source, v1_source }
- }
-
- /// Creates a new credential source that is empty.
- pub fn new_empty() -> Self {
- Self::new(Vec::new(), Vec::new())
+impl<'c, V: ProtocolVersion, M: MatchedCredential> SliceCredentialSource<'c, V, M> {
+ /// Construct the credential supplier from the provided slice of credentials.
+ pub fn new(credentials: &'c [MatchableCredential<V, M>]) -> Self {
+ Self { credentials }
}
}
-impl<C0, C1> BothCredentialSource<C0, C1> for OwnedBothCredentialSource<C0, C1>
+impl<'a, 'b, V: ProtocolVersion, M: MatchedCredential> DiscoveryCredentialSource<'a, V>
+ for SliceCredentialSource<'b, V, M>
where
- C0: V0Credential,
- C1: V1Credential,
+ 'b: 'a,
+ Self: 'b,
+ &'a <V as ProtocolVersion>::DiscoveryCredential: DiscoveryCryptoMaterial<V>,
{
- type V0Source = OwnedCredentialSource<C0>;
- type V1Source = OwnedCredentialSource<C1>;
-
- fn v0(&self) -> &Self::V0Source {
- &self.v0_source
- }
- fn v1(&self) -> &Self::V1Source {
- &self.v1_source
+ type Matched = ReferencedMatchedCredential<'a, M>;
+ type Crypto = &'a V::DiscoveryCredential;
+ type Iterator = core::iter::Map<
+ core::slice::Iter<'a, MatchableCredential<V, M>>,
+ fn(
+ &'a MatchableCredential<V, M>,
+ ) -> (&'a V::DiscoveryCredential, ReferencedMatchedCredential<M>),
+ >;
+
+ fn iter(&'a self) -> Self::Iterator {
+ self.credentials.iter().map(MatchableCredential::<V, M>::as_pair)
}
}
diff --git a/nearby/presence/np_adv/src/credential/tests.rs b/nearby/presence/np_adv/src/credential/tests.rs
new file mode 100644
index 0000000..8b12bfc
--- /dev/null
+++ b/nearby/presence/np_adv/src/credential/tests.rs
@@ -0,0 +1,208 @@
+// 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.
+
+//! Tests of functionality related to credentials, credential-views, and credential suppliers.
+
+extern crate alloc;
+
+use crate::credential::{
+ book::{
+ init_cache_from_source, CachedCredentialSource, PossiblyCachedDiscoveryCryptoMaterialKind,
+ },
+ source::{CredentialSource, SliceCredentialSource},
+ v0::{V0DiscoveryCredential, V0},
+ v1::{
+ SignedBroadcastCryptoMaterial, SimpleSignedBroadcastCryptoMaterial, V1DiscoveryCredential,
+ V1DiscoveryCryptoMaterial, V1,
+ },
+ BroadcastCryptoMaterial, EmptyMatchedCredential, KeySeedMatchedCredential, MatchableCredential,
+ MetadataDecryptionError, ProtocolVersion, ReferencedMatchedCredential,
+ SimpleBroadcastCryptoMaterial,
+};
+use crate::legacy::ShortMetadataKey;
+use crate::MetadataKey;
+use alloc::{vec, vec::Vec};
+use crypto_provider_default::CryptoProviderImpl;
+
+fn get_zeroed_v0_discovery_credential() -> V0DiscoveryCredential {
+ V0DiscoveryCredential::new([0u8; 32], [0u8; 32])
+}
+
+fn get_constant_packed_v1_discovery_credential(value: u8) -> V1DiscoveryCredential {
+ let key_pair = np_ed25519::KeyPair::<CryptoProviderImpl>::generate();
+ SimpleSignedBroadcastCryptoMaterial::new(
+ [value; 32],
+ MetadataKey([value; 16]),
+ // NOTE: This winds up being unused in these test cases
+ key_pair.private_key(),
+ )
+ .derive_v1_discovery_credential::<CryptoProviderImpl>()
+}
+
+#[test]
+fn cached_credential_source_keeps_same_entries_as_original() {
+ let creds: [MatchableCredential<V1, KeySeedMatchedCredential>; 5] =
+ [0u8, 1, 2, 3, 4].map(|x| {
+ let match_data = KeySeedMatchedCredential::from([x; 32]);
+ MatchableCredential {
+ discovery_credential: get_constant_packed_v1_discovery_credential(x),
+ match_data,
+ }
+ });
+ let supplier = SliceCredentialSource::new(&creds);
+ let cache = init_cache_from_source::<_, _, 3, CryptoProviderImpl>(&supplier);
+ let cached = CachedCredentialSource::new(supplier, cache);
+ let cached_view = &cached;
+ assert_eq!(cached_view.iter().count(), 5);
+ // Now we're going to check that the pairings between the match-data
+ // and the MIC hmac key wind up being the same between the original
+ // creds list and what's provided by the cached source.
+ let expected: Vec<_> = creds
+ .iter()
+ .map(|cred| {
+ (
+ cred.discovery_credential
+ .unsigned_verification_material::<CryptoProviderImpl>()
+ .mic_hmac_key,
+ ReferencedMatchedCredential::from(&cred.match_data),
+ )
+ })
+ .collect();
+ let actual: Vec<_> = cached_view
+ .iter()
+ .map(|(crypto_material, match_data)| {
+ (
+ crypto_material.unsigned_verification_material::<CryptoProviderImpl>().mic_hmac_key,
+ match_data,
+ )
+ })
+ .collect();
+ assert_eq!(actual, expected);
+}
+
+#[test]
+fn cached_credential_source_has_requested_cache_size() {
+ let creds: [MatchableCredential<V0, EmptyMatchedCredential>; 10] =
+ [0u8; 10].map(|_| MatchableCredential {
+ discovery_credential: get_zeroed_v0_discovery_credential(),
+ match_data: EmptyMatchedCredential,
+ });
+ let supplier = SliceCredentialSource::new(&creds);
+ let cache = init_cache_from_source::<_, _, 5, CryptoProviderImpl>(&supplier);
+ let cached = CachedCredentialSource::new(supplier, cache);
+ let cached_view = &cached;
+ assert_eq!(cached_view.iter().count(), 10);
+ for (i, (cred, _)) in cached_view.iter().enumerate() {
+ if i < 5 {
+ // Should be cached
+ if let PossiblyCachedDiscoveryCryptoMaterialKind::Precalculated(_) = cred.wrapped {
+ } else {
+ panic!("Credential #{} was not cached", i);
+ }
+ } else {
+ // Should be discovery credentials
+ if let PossiblyCachedDiscoveryCryptoMaterialKind::Discovery(_) = cred.wrapped {
+ } else {
+ panic!("Credential #{} was not supposed to be cached", i);
+ }
+ }
+ }
+}
+
+#[test]
+fn v0_metadata_decryption_works_same_metadata_key() {
+ let key_seed = [3u8; 32];
+ let metadata_key = ShortMetadataKey([5u8; 14]);
+
+ let metadata = vec![7u8; 42];
+
+ let broadcast_cm = SimpleBroadcastCryptoMaterial::<V0>::new(key_seed, metadata_key);
+
+ let encrypted_metadata = broadcast_cm.encrypt_metadata::<CryptoProviderImpl>(&metadata);
+
+ let metadata_nonce = broadcast_cm.metadata_nonce::<CryptoProviderImpl>();
+
+ let decryption_result = V0::decrypt_metadata::<CryptoProviderImpl>(
+ metadata_nonce,
+ metadata_key,
+ &encrypted_metadata,
+ );
+ assert_eq!(decryption_result, Ok(metadata))
+}
+
+#[test]
+fn v1_metadata_decryption_works_same_metadata_key() {
+ let key_seed = [9u8; 32];
+ let metadata_key = MetadataKey([2u8; 16]);
+
+ let metadata = vec![6u8; 51];
+
+ let broadcast_cm = SimpleBroadcastCryptoMaterial::<V1>::new(key_seed, metadata_key);
+
+ let encrypted_metadata = broadcast_cm.encrypt_metadata::<CryptoProviderImpl>(&metadata);
+
+ let metadata_nonce = broadcast_cm.metadata_nonce::<CryptoProviderImpl>();
+
+ let decryption_result = V1::decrypt_metadata::<CryptoProviderImpl>(
+ metadata_nonce,
+ metadata_key,
+ &encrypted_metadata,
+ );
+ assert_eq!(decryption_result, Ok(metadata))
+}
+
+#[test]
+fn v0_metadata_decryption_fails_different_metadata_key() {
+ let key_seed = [3u8; 32];
+ let encrypting_metadata_key = ShortMetadataKey([5u8; 14]);
+
+ let metadata = vec![7u8; 42];
+
+ let broadcast_cm = SimpleBroadcastCryptoMaterial::<V0>::new(key_seed, encrypting_metadata_key);
+
+ let encrypted_metadata = broadcast_cm.encrypt_metadata::<CryptoProviderImpl>(&metadata);
+
+ let metadata_nonce = broadcast_cm.metadata_nonce::<CryptoProviderImpl>();
+
+ let decrypting_metadata_key = ShortMetadataKey([6u8; 14]);
+
+ let decryption_result = V0::decrypt_metadata::<CryptoProviderImpl>(
+ metadata_nonce,
+ decrypting_metadata_key,
+ &encrypted_metadata,
+ );
+ assert_eq!(decryption_result, Err(MetadataDecryptionError))
+}
+
+#[test]
+fn v1_metadata_decryption_fails_different_metadata_key() {
+ let key_seed = [251u8; 32];
+ let encrypting_metadata_key = MetadataKey([127u8; 16]);
+
+ let metadata = vec![255u8; 42];
+
+ let broadcast_cm = SimpleBroadcastCryptoMaterial::<V1>::new(key_seed, encrypting_metadata_key);
+
+ let encrypted_metadata = broadcast_cm.encrypt_metadata::<CryptoProviderImpl>(&metadata);
+
+ let metadata_nonce = broadcast_cm.metadata_nonce::<CryptoProviderImpl>();
+
+ let decrypting_metadata_key = MetadataKey([249u8; 16]);
+
+ let decryption_result = V1::decrypt_metadata::<CryptoProviderImpl>(
+ metadata_nonce,
+ decrypting_metadata_key,
+ &encrypted_metadata,
+ );
+ assert_eq!(decryption_result, Err(MetadataDecryptionError))
+}
diff --git a/nearby/presence/np_adv/src/credential/v0.rs b/nearby/presence/np_adv/src/credential/v0.rs
index e2d1b5d..c89909d 100644
--- a/nearby/presence/np_adv/src/credential/v0.rs
+++ b/nearby/presence/np_adv/src/credential/v0.rs
@@ -13,32 +13,33 @@
// limitations under the License.
//! Cryptographic materials for v0 advertisement-format credentials.
+use crate::credential::{protocol_version_seal, DiscoveryCryptoMaterial, ProtocolVersion};
+use crate::legacy::ShortMetadataKey;
+use crate::MetadataKey;
+use crypto_provider::{CryptoProvider, CryptoRng};
-use super::*;
-
-/// Cryptographic material for an individual NP credential used to decrypt v0 advertisements.
-// Space-time tradeoffs:
-// - LDT keys (64b) take about 1.4us.
-pub trait V0CryptoMaterial {
- /// Returns an LDT NP advertisement cipher built with the provided `Aes`
- fn ldt_adv_cipher<C: CryptoProvider>(&self) -> ldt_np_adv::LdtNpAdvDecrypterXtsAes128<C>;
-}
-
-/// A [`V0CryptoMaterial`] that minimizes memory footprint at the expense of CPU time when
-/// providing derived key material
-pub struct MinimumFootprintV0CryptoMaterial {
+/// Cryptographic information about a particular V0 discovery credential
+/// necessary to match and decrypt encrypted V0 advertisements.
+#[derive(Clone)]
+pub struct V0DiscoveryCredential {
key_seed: [u8; 32],
legacy_metadata_key_hmac: [u8; 32],
}
-impl MinimumFootprintV0CryptoMaterial {
- /// Construct an [MinimumFootprintV0CryptoMaterial] from the provided identity data.
+impl V0DiscoveryCredential {
+ /// Construct an [V0DiscoveryCredential] from the provided identity data.
pub fn new(key_seed: [u8; 32], legacy_metadata_key_hmac: [u8; 32]) -> Self {
Self { key_seed, legacy_metadata_key_hmac }
}
}
-impl V0CryptoMaterial for MinimumFootprintV0CryptoMaterial {
+impl DiscoveryCryptoMaterial<V0> for V0DiscoveryCredential {
+ fn metadata_nonce<C: CryptoProvider>(&self) -> [u8; 12] {
+ V0::metadata_nonce_from_key_seed::<C>(&self.key_seed)
+ }
+}
+
+impl V0DiscoveryCryptoMaterial for V0DiscoveryCredential {
fn ldt_adv_cipher<C: CryptoProvider>(&self) -> ldt_np_adv::LdtNpAdvDecrypterXtsAes128<C> {
let hkdf = np_hkdf::NpKeySeedHkdf::new(&self.key_seed);
ldt_np_adv::build_np_adv_decrypter(
@@ -49,27 +50,73 @@ impl V0CryptoMaterial for MinimumFootprintV0CryptoMaterial {
}
}
-/// [`V0CryptoMaterial`] that minimizes CPU time when providing key material at
+/// Type-level identifier for the V0 protocol version.
+#[derive(Debug, Clone)]
+pub enum V0 {}
+
+impl protocol_version_seal::ProtocolVersionSeal for V0 {}
+
+impl ProtocolVersion for V0 {
+ type DiscoveryCredential = V0DiscoveryCredential;
+ type MetadataKey = ShortMetadataKey;
+
+ fn metadata_nonce_from_key_seed<C: CryptoProvider>(key_seed: &[u8; 32]) -> [u8; 12] {
+ let hkdf = np_hkdf::NpKeySeedHkdf::<C>::new(key_seed);
+ hkdf.legacy_metadata_nonce()
+ }
+ fn expand_metadata_key<C: CryptoProvider>(metadata_key: ShortMetadataKey) -> MetadataKey {
+ metadata_key.expand::<C>()
+ }
+ fn gen_random_metadata_key<R: CryptoRng>(rng: &mut R) -> ShortMetadataKey {
+ ShortMetadataKey(rng.gen())
+ }
+}
+
+/// Trait which exists purely to be able to restrict the protocol
+/// version of certain type-bounds to V0.
+pub trait V0ProtocolVersion: ProtocolVersion {}
+
+impl V0ProtocolVersion for V0 {}
+
+/// Cryptographic material for an individual NP credential used to decrypt v0 advertisements.
+/// Unlike [`V0DiscoveryCredential`], derived information about cryptographic materials may
+/// be stored in implementors of this trait.
+// Space-time tradeoffs:
+// - LDT keys (64b) take about 1.4us.
+pub trait V0DiscoveryCryptoMaterial: DiscoveryCryptoMaterial<V0> {
+ /// Returns an LDT NP advertisement cipher built with the provided `Aes`
+ fn ldt_adv_cipher<C: CryptoProvider>(&self) -> ldt_np_adv::LdtNpAdvDecrypterXtsAes128<C>;
+}
+
+/// [`V0DiscoveryCryptoMaterial`] that minimizes CPU time when providing key material at
/// the expense of occupied memory.
-pub struct PrecalculatedV0CryptoMaterial {
+pub struct PrecalculatedV0DiscoveryCryptoMaterial {
pub(crate) legacy_ldt_key: ldt::LdtKey<xts_aes::XtsAes128Key>,
pub(crate) legacy_metadata_key_hmac: [u8; 32],
pub(crate) legacy_metadata_key_hmac_key: [u8; 32],
+ pub(crate) metadata_nonce: [u8; 12],
}
-impl PrecalculatedV0CryptoMaterial {
+impl PrecalculatedV0DiscoveryCryptoMaterial {
/// Construct a new instance from the provided credential material.
- pub fn new<C: CryptoProvider>(key_seed: &[u8; 32], legacy_metadata_key_hmac: [u8; 32]) -> Self {
- let hkdf = np_hkdf::NpKeySeedHkdf::<C>::new(key_seed);
+ pub(crate) fn new<C: CryptoProvider>(discovery_credential: &V0DiscoveryCredential) -> Self {
+ let hkdf = np_hkdf::NpKeySeedHkdf::<C>::new(&discovery_credential.key_seed);
Self {
legacy_ldt_key: hkdf.legacy_ldt_key(),
- legacy_metadata_key_hmac,
+ legacy_metadata_key_hmac: discovery_credential.legacy_metadata_key_hmac,
legacy_metadata_key_hmac_key: *hkdf.legacy_metadata_key_hmac_key().as_bytes(),
+ metadata_nonce: hkdf.legacy_metadata_nonce(),
}
}
}
-impl V0CryptoMaterial for PrecalculatedV0CryptoMaterial {
+impl DiscoveryCryptoMaterial<V0> for PrecalculatedV0DiscoveryCryptoMaterial {
+ fn metadata_nonce<C: CryptoProvider>(&self) -> [u8; 12] {
+ self.metadata_nonce
+ }
+}
+
+impl V0DiscoveryCryptoMaterial for PrecalculatedV0DiscoveryCryptoMaterial {
fn ldt_adv_cipher<C: CryptoProvider>(&self) -> ldt_np_adv::LdtNpAdvDecrypterXtsAes128<C> {
ldt_np_adv::build_np_adv_decrypter(
&self.legacy_ldt_key,
@@ -78,3 +125,30 @@ impl V0CryptoMaterial for PrecalculatedV0CryptoMaterial {
)
}
}
+
+// Implementations for reference types -- we don't provide a blanket impl for references
+// due to the potential to conflict with downstream crates' implementations.
+
+impl<'a> DiscoveryCryptoMaterial<V0> for &'a V0DiscoveryCredential {
+ fn metadata_nonce<C: CryptoProvider>(&self) -> [u8; 12] {
+ (*self).metadata_nonce::<C>()
+ }
+}
+
+impl<'a> V0DiscoveryCryptoMaterial for &'a V0DiscoveryCredential {
+ fn ldt_adv_cipher<C: CryptoProvider>(&self) -> ldt_np_adv::LdtNpAdvDecrypterXtsAes128<C> {
+ (*self).ldt_adv_cipher::<C>()
+ }
+}
+
+impl<'a> DiscoveryCryptoMaterial<V0> for &'a PrecalculatedV0DiscoveryCryptoMaterial {
+ fn metadata_nonce<C: CryptoProvider>(&self) -> [u8; 12] {
+ (*self).metadata_nonce::<C>()
+ }
+}
+
+impl<'a> V0DiscoveryCryptoMaterial for &'a PrecalculatedV0DiscoveryCryptoMaterial {
+ fn ldt_adv_cipher<C: CryptoProvider>(&self) -> ldt_np_adv::LdtNpAdvDecrypterXtsAes128<C> {
+ (*self).ldt_adv_cipher::<C>()
+ }
+}
diff --git a/nearby/presence/np_adv/src/credential/v1.rs b/nearby/presence/np_adv/src/credential/v1.rs
index 5c17d27..386799f 100644
--- a/nearby/presence/np_adv/src/credential/v1.rs
+++ b/nearby/presence/np_adv/src/credential/v1.rs
@@ -14,17 +14,133 @@
//! Cryptographic materials for v1 advertisement-format credentials.
-use core::borrow::Borrow;
-use crypto_provider::{aes::Aes128Key, ed25519, CryptoProvider};
+use crate::credential::{
+ protocol_version_seal, BroadcastCryptoMaterial, DiscoveryCryptoMaterial, ProtocolVersion,
+};
+use crate::MetadataKey;
+use crypto_provider::{aes::Aes128Key, ed25519, ed25519::PublicKey, CryptoProvider, CryptoRng};
use np_hkdf::UnsignedSectionKeys;
+/// Cryptographic information about a particular V1 discovery credential
+/// necessary to match and decrypt encrypted V1 sections.
+#[derive(Clone)]
+pub struct V1DiscoveryCredential {
+ key_seed: [u8; 32],
+ expected_unsigned_metadata_key_hmac: [u8; 32],
+ expected_signed_metadata_key_hmac: [u8; 32],
+ pub_key: ed25519::RawPublicKey,
+}
+impl V1DiscoveryCredential {
+ /// Construct a V1 discovery credential from the provided identity data.
+ pub fn new(
+ key_seed: [u8; 32],
+ expected_unsigned_metadata_key_hmac: [u8; 32],
+ expected_signed_metadata_key_hmac: [u8; 32],
+ pub_key: ed25519::RawPublicKey,
+ ) -> Self {
+ Self {
+ key_seed,
+ expected_unsigned_metadata_key_hmac,
+ expected_signed_metadata_key_hmac,
+ pub_key,
+ }
+ }
+
+ /// Constructs pre-calculated crypto material from this discovery credential.
+ pub(crate) fn to_precalculated<C: CryptoProvider>(
+ &self,
+ ) -> PrecalculatedV1DiscoveryCryptoMaterial {
+ let signed_identity_resolution_material = self.signed_identity_resolution_material::<C>();
+ let unsigned_identity_resolution_material =
+ self.unsigned_identity_resolution_material::<C>();
+ let signed_verification_material = self.signed_verification_material::<C>();
+ let unsigned_verification_material = self.unsigned_verification_material::<C>();
+ let metadata_nonce = self.metadata_nonce::<C>();
+ PrecalculatedV1DiscoveryCryptoMaterial {
+ signed_identity_resolution_material,
+ unsigned_identity_resolution_material,
+ signed_verification_material,
+ unsigned_verification_material,
+ metadata_nonce,
+ }
+ }
+}
+
+impl DiscoveryCryptoMaterial<V1> for V1DiscoveryCredential {
+ fn metadata_nonce<C: CryptoProvider>(&self) -> [u8; 12] {
+ V1::metadata_nonce_from_key_seed::<C>(&self.key_seed)
+ }
+}
+
+impl V1DiscoveryCryptoMaterial for V1DiscoveryCredential {
+ fn signed_identity_resolution_material<C: CryptoProvider>(
+ &self,
+ ) -> SignedSectionIdentityResolutionMaterial {
+ let hkdf = np_hkdf::NpKeySeedHkdf::<C>::new(&self.key_seed);
+ SignedSectionIdentityResolutionMaterial::from_hkdf_and_expected_metadata_key_hmac(
+ &hkdf,
+ self.expected_signed_metadata_key_hmac,
+ )
+ }
+
+ fn unsigned_identity_resolution_material<C: CryptoProvider>(
+ &self,
+ ) -> UnsignedSectionIdentityResolutionMaterial {
+ let hkdf = np_hkdf::NpKeySeedHkdf::<C>::new(&self.key_seed);
+ UnsignedSectionIdentityResolutionMaterial::from_hkdf_and_expected_metadata_key_hmac(
+ &hkdf,
+ self.expected_unsigned_metadata_key_hmac,
+ )
+ }
+
+ fn signed_verification_material<C: CryptoProvider>(&self) -> SignedSectionVerificationMaterial {
+ SignedSectionVerificationMaterial { pub_key: self.pub_key }
+ }
+
+ fn unsigned_verification_material<C: CryptoProvider>(
+ &self,
+ ) -> UnsignedSectionVerificationMaterial {
+ let hkdf = np_hkdf::NpKeySeedHkdf::<C>::new(&self.key_seed);
+ let mic_hmac_key = *UnsignedSectionKeys::hmac_key(&hkdf).as_bytes();
+ UnsignedSectionVerificationMaterial { mic_hmac_key }
+ }
+}
+
+/// Type-level identifier for the V1 protocol version.
+#[derive(Debug, Clone)]
+pub enum V1 {}
+
+impl protocol_version_seal::ProtocolVersionSeal for V1 {}
+
+impl ProtocolVersion for V1 {
+ type DiscoveryCredential = V1DiscoveryCredential;
+ type MetadataKey = MetadataKey;
+
+ fn metadata_nonce_from_key_seed<C: CryptoProvider>(key_seed: &[u8; 32]) -> [u8; 12] {
+ let hkdf = np_hkdf::NpKeySeedHkdf::<C>::new(key_seed);
+ hkdf.extended_metadata_nonce()
+ }
+ fn expand_metadata_key<C: CryptoProvider>(metadata_key: MetadataKey) -> MetadataKey {
+ metadata_key
+ }
+ fn gen_random_metadata_key<R: CryptoRng>(rng: &mut R) -> MetadataKey {
+ MetadataKey(rng.gen())
+ }
+}
+
+/// Trait which exists purely to be able to restrict the protocol
+/// version of certain type-bounds to V1.
+pub trait V1ProtocolVersion: ProtocolVersion {}
+
+impl V1ProtocolVersion for V1 {}
+
/// Cryptographic materials necessary for determining whether or not
/// a given V1 advertisement section matches an identity.
/// Per the construction of the V1 specification, this is also
/// the information necessary to decrypt the raw byte contents
/// of an encrypted V1 section.
#[derive(Clone)]
-pub struct SectionIdentityResolutionMaterial {
+pub(crate) struct SectionIdentityResolutionMaterial {
/// The AES key for decrypting section ciphertext
pub(crate) aes_key: Aes128Key,
/// The metadata key HMAC key for deriving and verifying the identity metadata
@@ -44,11 +160,18 @@ impl SignedSectionIdentityResolutionMaterial {
pub(crate) fn from_raw(raw: SectionIdentityResolutionMaterial) -> Self {
Self(raw)
}
+ /// Extracts the underlying section-identity resolution material carried around
+ /// within this wrapper for resolution of signed sections.
+ pub(crate) fn into_raw_resolution_material(self) -> SectionIdentityResolutionMaterial {
+ self.0
+ }
+ #[cfg(any(test, feature = "devtools"))]
/// Gets the underlying section-identity resolution material carried around
/// within this wrapper for resolution of signed sections.
pub(crate) fn as_raw_resolution_material(&self) -> &SectionIdentityResolutionMaterial {
&self.0
}
+
/// Constructs identity-resolution material for a signed section whose
/// discovery credential leverages the provided HKDF and has the given
/// expected metadata-key HMAC.
@@ -74,8 +197,14 @@ impl UnsignedSectionIdentityResolutionMaterial {
pub(crate) fn from_raw(raw: SectionIdentityResolutionMaterial) -> Self {
Self(raw)
}
+ /// Extracts the underlying section-identity resolution material carried around
+ /// within this wrapper for resolution of unsigned sections.
+ pub(crate) fn into_raw_resolution_material(self) -> SectionIdentityResolutionMaterial {
+ self.0
+ }
/// Gets the underlying section-identity resolution material carried around
/// within this wrapper for resolution of unsigned sections.
+ #[cfg(any(test, feature = "devtools"))]
pub(crate) fn as_raw_resolution_material(&self) -> &SectionIdentityResolutionMaterial {
&self.0
}
@@ -94,51 +223,6 @@ impl UnsignedSectionIdentityResolutionMaterial {
}
}
-/// Wrapper enum around signed/unsigned identity resolution material
-/// which may be borrowed. Used only in implementation details
-/// of identity-resolution.
-pub(crate) enum BorrowableIdentityResolutionMaterial<S, U>
-where
- S: Borrow<SignedSectionIdentityResolutionMaterial>,
- U: Borrow<UnsignedSectionIdentityResolutionMaterial>,
-{
- Signed(S),
- Unsigned(U),
-}
-
-impl<S, U> BorrowableIdentityResolutionMaterial<S, U>
-where
- S: Borrow<SignedSectionIdentityResolutionMaterial>,
- U: Borrow<UnsignedSectionIdentityResolutionMaterial>,
-{
- pub(crate) fn as_raw_resolution_material(&self) -> &SectionIdentityResolutionMaterial {
- match self {
- Self::Signed(x) => x.borrow().as_raw_resolution_material(),
- Self::Unsigned(x) => x.borrow().as_raw_resolution_material(),
- }
- }
- pub(crate) fn signed_from_crypto_material<'a, C, P>(crypto_material: &'a C) -> Self
- where
- C: V1CryptoMaterial<
- SignedIdentityResolverReference<'a> = S,
- UnsignedIdentityResolverReference<'a> = U,
- >,
- P: CryptoProvider,
- {
- Self::Signed(crypto_material.signed_identity_resolution_material::<P>())
- }
- pub(crate) fn unsigned_from_crypto_material<'a, C, P>(crypto_material: &'a C) -> Self
- where
- C: V1CryptoMaterial<
- SignedIdentityResolverReference<'a> = S,
- UnsignedIdentityResolverReference<'a> = U,
- >,
- P: CryptoProvider,
- {
- Self::Unsigned(crypto_material.unsigned_identity_resolution_material::<P>())
- }
-}
-
/// Crypto materials for V1 signed sections which are not employed in identity resolution,
/// but may be necessary to verify a signed section.
#[derive(Clone)]
@@ -184,88 +268,54 @@ impl UnsignedSectionVerificationMaterial {
// is only used on the matching identity, not all identities.
/// Cryptographic material for an individual NP credential used to decrypt and verify v1 sections.
-pub trait V1CryptoMaterial {
- /// The return type of `Self::signed_identity_resolution_material`, which is some
- /// data-type which allows borrowing `SignedSectionIdentityResolutionMaterial`
- type SignedIdentityResolverReference<'a>: Borrow<SignedSectionIdentityResolutionMaterial>
- where
- Self: 'a;
- /// The return type of `Self::unsigned_identity_resolution_material`, which is some
- /// data-type which allows borrowing `UnsignedSectionIdentityResolutionMaterial`
- type UnsignedIdentityResolverReference<'a>: Borrow<UnsignedSectionIdentityResolutionMaterial>
- where
- Self: 'a;
-
- /// Constructs or references the identity resolution material for signed sections
+pub trait V1DiscoveryCryptoMaterial: DiscoveryCryptoMaterial<V1> {
+ /// Constructs or copies the identity resolution material for signed sections
fn signed_identity_resolution_material<C: CryptoProvider>(
&self,
- ) -> Self::SignedIdentityResolverReference<'_>;
+ ) -> SignedSectionIdentityResolutionMaterial;
- /// Constructs or references the identity resolution material for unsigned sections
+ /// Constructs or copies the identity resolution material for unsigned sections
fn unsigned_identity_resolution_material<C: CryptoProvider>(
&self,
- ) -> Self::UnsignedIdentityResolverReference<'_>;
+ ) -> UnsignedSectionIdentityResolutionMaterial;
/// Constructs or copies non-identity-resolution deserialization material for signed
/// sections.
- ///
- /// Note: We mandate "copies" here due to the relatively small size of verification-only crypto
- /// materials (32 bytes).
fn signed_verification_material<C: CryptoProvider>(&self) -> SignedSectionVerificationMaterial;
/// Constructs or copies non-identity-resolution deserialization material for unsigned
/// sections.
- ///
- /// Note: We mandate "copies" here due to the relatively small size of verification-only crypto
- /// materials (32 bytes).
fn unsigned_verification_material<C: CryptoProvider>(
&self,
) -> UnsignedSectionVerificationMaterial;
-
- /// Constructs pre-calculated crypto material out of this crypto-material.
- fn to_precalculated<C: CryptoProvider>(self) -> PrecalculatedV1CryptoMaterial
- where
- Self: Sized,
- {
- let signed_identity_resolution_material =
- self.signed_identity_resolution_material::<C>().borrow().clone();
- let unsigned_identity_resolution_material =
- self.unsigned_identity_resolution_material::<C>().borrow().clone();
- let signed_verification_material = self.signed_verification_material::<C>();
- let unsigned_verification_material = self.unsigned_verification_material::<C>();
- PrecalculatedV1CryptoMaterial {
- signed_identity_resolution_material,
- unsigned_identity_resolution_material,
- signed_verification_material,
- unsigned_verification_material,
- }
- }
}
-/// [`V1CryptoMaterial`] that minimizes CPU time when providing key material at
+/// V1 [`DiscoveryCryptoMaterial`] that minimizes CPU time when providing key material at
/// the expense of occupied memory
-pub struct PrecalculatedV1CryptoMaterial {
+pub struct PrecalculatedV1DiscoveryCryptoMaterial {
pub(crate) signed_identity_resolution_material: SignedSectionIdentityResolutionMaterial,
pub(crate) unsigned_identity_resolution_material: UnsignedSectionIdentityResolutionMaterial,
pub(crate) signed_verification_material: SignedSectionVerificationMaterial,
pub(crate) unsigned_verification_material: UnsignedSectionVerificationMaterial,
+ pub(crate) metadata_nonce: [u8; 12],
}
-impl V1CryptoMaterial for PrecalculatedV1CryptoMaterial {
- type SignedIdentityResolverReference<'a> = &'a SignedSectionIdentityResolutionMaterial
- where Self: 'a;
- type UnsignedIdentityResolverReference<'a> = &'a UnsignedSectionIdentityResolutionMaterial
- where Self: 'a;
+impl DiscoveryCryptoMaterial<V1> for PrecalculatedV1DiscoveryCryptoMaterial {
+ fn metadata_nonce<C: CryptoProvider>(&self) -> [u8; 12] {
+ self.metadata_nonce
+ }
+}
+impl V1DiscoveryCryptoMaterial for PrecalculatedV1DiscoveryCryptoMaterial {
fn signed_identity_resolution_material<C: CryptoProvider>(
&self,
- ) -> Self::SignedIdentityResolverReference<'_> {
- &self.signed_identity_resolution_material
+ ) -> SignedSectionIdentityResolutionMaterial {
+ self.signed_identity_resolution_material.clone()
}
fn unsigned_identity_resolution_material<C: CryptoProvider>(
&self,
- ) -> Self::UnsignedIdentityResolverReference<'_> {
- &self.unsigned_identity_resolution_material
+ ) -> UnsignedSectionIdentityResolutionMaterial {
+ self.unsigned_identity_resolution_material.clone()
}
fn signed_verification_material<C: CryptoProvider>(&self) -> SignedSectionVerificationMaterial {
self.signed_verification_material.clone()
@@ -277,67 +327,126 @@ impl V1CryptoMaterial for PrecalculatedV1CryptoMaterial {
}
}
-/// [`V1CryptoMaterial`] that minimizes memory footprint at the expense of CPU
-/// time when providing derived key material.
-pub struct MinimumFootprintV1CryptoMaterial {
- key_seed: [u8; 32],
- expected_unsigned_metadata_key_hmac: [u8; 32],
- expected_signed_metadata_key_hmac: [u8; 32],
- pub_key: ed25519::RawPublicKey,
+// Implementations for reference types -- we don't provide a blanket impl for references
+// due to the potential to conflict with downstream crates' implementations.
+
+impl<'a> DiscoveryCryptoMaterial<V1> for &'a V1DiscoveryCredential {
+ fn metadata_nonce<C: CryptoProvider>(&self) -> [u8; 12] {
+ (*self).metadata_nonce::<C>()
+ }
}
-impl MinimumFootprintV1CryptoMaterial {
- /// Construct an [MinimumFootprintV1CryptoMaterial] from the provided identity data.
- pub fn new<C: CryptoProvider>(
- key_seed: [u8; 32],
- expected_unsigned_metadata_key_hmac: [u8; 32],
- expected_signed_metadata_key_hmac: [u8; 32],
- pub_key: np_ed25519::PublicKey<C>,
- ) -> Self {
- Self {
- key_seed,
- expected_unsigned_metadata_key_hmac,
- expected_signed_metadata_key_hmac,
- pub_key: pub_key.to_bytes(),
- }
+impl<'a> V1DiscoveryCryptoMaterial for &'a V1DiscoveryCredential {
+ fn signed_identity_resolution_material<C: CryptoProvider>(
+ &self,
+ ) -> SignedSectionIdentityResolutionMaterial {
+ (*self).signed_identity_resolution_material::<C>()
+ }
+ fn unsigned_identity_resolution_material<C: CryptoProvider>(
+ &self,
+ ) -> UnsignedSectionIdentityResolutionMaterial {
+ (*self).unsigned_identity_resolution_material::<C>()
+ }
+ fn signed_verification_material<C: CryptoProvider>(&self) -> SignedSectionVerificationMaterial {
+ (*self).signed_verification_material::<C>()
+ }
+ fn unsigned_verification_material<C: CryptoProvider>(
+ &self,
+ ) -> UnsignedSectionVerificationMaterial {
+ (*self).unsigned_verification_material::<C>()
}
}
-impl V1CryptoMaterial for MinimumFootprintV1CryptoMaterial {
- type SignedIdentityResolverReference<'a> = SignedSectionIdentityResolutionMaterial
- where Self: 'a;
- type UnsignedIdentityResolverReference<'a> = UnsignedSectionIdentityResolutionMaterial
- where Self: 'a;
+impl<'a> DiscoveryCryptoMaterial<V1> for &'a PrecalculatedV1DiscoveryCryptoMaterial {
+ fn metadata_nonce<C: CryptoProvider>(&self) -> [u8; 12] {
+ (*self).metadata_nonce::<C>()
+ }
+}
+impl<'a> V1DiscoveryCryptoMaterial for &'a PrecalculatedV1DiscoveryCryptoMaterial {
fn signed_identity_resolution_material<C: CryptoProvider>(
&self,
- ) -> Self::SignedIdentityResolverReference<'_> {
- let hkdf = np_hkdf::NpKeySeedHkdf::<C>::new(&self.key_seed);
- SignedSectionIdentityResolutionMaterial::from_hkdf_and_expected_metadata_key_hmac(
- &hkdf,
- self.expected_signed_metadata_key_hmac,
- )
+ ) -> SignedSectionIdentityResolutionMaterial {
+ (*self).signed_identity_resolution_material::<C>()
}
-
fn unsigned_identity_resolution_material<C: CryptoProvider>(
&self,
- ) -> Self::UnsignedIdentityResolverReference<'_> {
- let hkdf = np_hkdf::NpKeySeedHkdf::<C>::new(&self.key_seed);
- UnsignedSectionIdentityResolutionMaterial::from_hkdf_and_expected_metadata_key_hmac(
- &hkdf,
- self.expected_unsigned_metadata_key_hmac,
- )
+ ) -> UnsignedSectionIdentityResolutionMaterial {
+ (*self).unsigned_identity_resolution_material::<C>()
}
-
fn signed_verification_material<C: CryptoProvider>(&self) -> SignedSectionVerificationMaterial {
- SignedSectionVerificationMaterial { pub_key: self.pub_key }
+ (*self).signed_verification_material::<C>()
}
-
fn unsigned_verification_material<C: CryptoProvider>(
&self,
) -> UnsignedSectionVerificationMaterial {
- let hkdf = np_hkdf::NpKeySeedHkdf::<C>::new(&self.key_seed);
- let mic_hmac_key = *UnsignedSectionKeys::hmac_key(&hkdf).as_bytes();
- UnsignedSectionVerificationMaterial { mic_hmac_key }
+ (*self).unsigned_verification_material::<C>()
+ }
+}
+
+/// Extension of [`BroadcastCryptoMaterial`] for `V1` to add
+/// crypto-materials which are necessary to sign V1 sections.
+pub trait SignedBroadcastCryptoMaterial: BroadcastCryptoMaterial<V1> {
+ /// Gets the advertisement-signing private key for constructing
+ /// signature-verified V1 sections.
+ ///
+ /// The private key is returned in an opaque, crypto-provider-independent
+ /// form to provide a safeguard against leaking the bytes of the key.
+ fn signing_key(&self) -> ed25519::PrivateKey;
+
+ /// Constructs the V1 discovery credential which may be used to discover
+ /// V1 advertisement sections broadcasted using this broadcast crypto-material
+ fn derive_v1_discovery_credential<C: CryptoProvider>(&self) -> V1DiscoveryCredential {
+ 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 = pub_key.to_bytes();
+
+ let hkdf = np_hkdf::NpKeySeedHkdf::<C>::new(&key_seed);
+ let unsigned = hkdf
+ .extended_unsigned_metadata_key_hmac_key()
+ .calculate_hmac(metadata_key.0.as_slice());
+ let signed =
+ hkdf.extended_signed_metadata_key_hmac_key().calculate_hmac(metadata_key.0.as_slice());
+ V1DiscoveryCredential::new(key_seed, unsigned, signed, pub_key)
+ }
+}
+
+/// Concrete implementation of a [`SignedBroadcastCryptoMaterial`] which keeps the key
+/// seed, the V1 metadata key, and the signing key contiguous in memory.
+///
+/// For more flexible expression of broadcast
+/// credentials, feel free to directly implement [`SignedBroadcastCryptoMaterial`]
+/// for your own broadcast-credential-storing data-type.
+pub struct SimpleSignedBroadcastCryptoMaterial {
+ key_seed: [u8; 32],
+ metadata_key: MetadataKey,
+ signing_key: ed25519::PrivateKey,
+}
+
+impl SimpleSignedBroadcastCryptoMaterial {
+ /// Builds some simple V1 signed broadcast crypto-materials out of
+ /// the provided key-seed, metadata-key, and signing key.
+ pub fn new(
+ key_seed: [u8; 32],
+ metadata_key: MetadataKey,
+ signing_key: ed25519::PrivateKey,
+ ) -> Self {
+ Self { key_seed, metadata_key, signing_key }
+ }
+}
+
+impl BroadcastCryptoMaterial<V1> for SimpleSignedBroadcastCryptoMaterial {
+ fn key_seed(&self) -> [u8; 32] {
+ self.key_seed
+ }
+ fn metadata_key(&self) -> MetadataKey {
+ self.metadata_key
+ }
+}
+
+impl SignedBroadcastCryptoMaterial for SimpleSignedBroadcastCryptoMaterial {
+ fn signing_key(&self) -> ed25519::PrivateKey {
+ self.signing_key.clone()
}
}
diff --git a/nearby/presence/np_adv/src/deser_v0_tests.rs b/nearby/presence/np_adv/src/deser_v0_tests.rs
index 7f4d888..f6643de 100644
--- a/nearby/presence/np_adv/src/deser_v0_tests.rs
+++ b/nearby/presence/np_adv/src/deser_v0_tests.rs
@@ -12,33 +12,38 @@
// 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;
use crate::{
credential::{
- simple::{SimpleMatchedCredential, SimpleV0Credential},
- source::SliceCredentialSource,
- v0::MinimumFootprintV0CryptoMaterial,
+ book::{CredentialBook, CredentialBookBuilder},
+ v0::{V0DiscoveryCredential, V0},
+ EmptyMatchedCredential, MatchableCredential, MatchedCredential,
+ SimpleBroadcastCryptoMaterial,
},
de_type::EncryptedIdentityDataElementType,
- deserialize_v0_advertisement,
+ deserialization_arena,
+ deserialization_arena::DeserializationArena,
+ deserialize_advertisement,
legacy::{
actions::{ActionBits, ActionsDataElement, ToActionElement},
data_elements::DataElement,
deserialize::PlainDataElement,
serialize::{AdvBuilder, Identity, LdtIdentity},
- BLE_ADV_SVC_CONTENT_LEN,
+ ShortMetadataKey, BLE_ADV_SVC_CONTENT_LEN,
},
shared_data::ContextSyncSeqNum,
- CredentialSource, PlaintextIdentityMode, PublicIdentity, V0AdvContents, V0Credential,
+ HasIdentityMatch, PlaintextIdentityMode, PublicIdentity, V0AdvertisementContents,
};
use array_view::ArrayView;
use core::marker::PhantomData;
use crypto_provider::CryptoProvider;
use crypto_provider_default::CryptoProviderImpl;
-use ldt_np_adv::{LdtEncrypterXtsAes128, LegacySalt};
+use ldt_np_adv::LegacySalt;
use std::{prelude::rust_2021::*, vec};
use strum::IntoEnumIterator as _;
@@ -50,10 +55,18 @@ fn v0_all_identities_resolvable() {
let (adv, adv_config) = adv_random_identity(&mut rng, &identities);
- let creds = identities.iter().map(|i| i.credential()).collect::<Vec<_>>();
- let cred_source = SliceCredentialSource::new(&creds);
+ let creds = identities
+ .iter()
+ .map(|i| MatchableCredential {
+ discovery_credential: i.discovery_credential(),
+ match_data: EmptyMatchedCredential,
+ })
+ .collect::<Vec<_>>();
- let contents = deser_v0::<_, _, CryptoProviderImpl>(&cred_source, adv.as_slice());
+ let arena = deserialization_arena!();
+ let cred_book =
+ CredentialBookBuilder::build_cached_slice_book::<0, 0, CryptoProviderImpl>(&creds, &[]);
+ let contents = deser_v0::<_, CryptoProviderImpl>(arena, adv.as_slice(), &cred_book);
assert_adv_equals(&adv_config, &contents);
}
@@ -67,24 +80,31 @@ fn v0_only_non_matching_identities_available() {
let (adv, adv_config) = adv_random_identity(&mut rng, &identities);
- let creds = identities
+ let credentials = identities
.iter()
.filter(|i| {
// remove identity used, if any
!adv_config.identity.map(|sci| sci.key_seed == i.key_seed).unwrap_or(false)
})
- .map(|i| i.credential())
+ .map(|i| MatchableCredential {
+ discovery_credential: i.discovery_credential(),
+ match_data: EmptyMatchedCredential,
+ })
.collect::<Vec<_>>();
- let cred_source = SliceCredentialSource::new(&creds);
- let contents = deser_v0::<_, _, CryptoProviderImpl>(&cred_source, adv.as_slice());
+ let arena = deserialization_arena!();
+ let cred_book = CredentialBookBuilder::build_cached_slice_book::<0, 0, CryptoProviderImpl>(
+ &credentials,
+ &[],
+ );
+ let contents = deser_v0::<_, CryptoProviderImpl>(arena, adv.as_slice(), &cred_book);
match adv_config.identity {
// we ended up generating plaintext, so it's fine
None => assert_adv_equals(&adv_config, &contents),
Some(_) => {
// we generated an encrypted adv, but didn't include the credential
- assert_eq!(V0AdvContents::NoMatchingCredentials, contents);
+ assert_eq!(V0AdvertisementContents::NoMatchingCredentials, contents);
}
}
}
@@ -98,43 +118,48 @@ fn v0_no_creds_available_error_if_encrypted() {
let (adv, adv_config) = adv_random_identity(&mut rng, &identities);
- let creds = Vec::<SimpleV0Credential<MinimumFootprintV0CryptoMaterial, [u8; 32]>>::new();
- let cred_source = SliceCredentialSource::new(&creds);
+ let creds = Vec::<MatchableCredential<V0, EmptyMatchedCredential>>::new();
- let contents = deser_v0::<_, _, CryptoProviderImpl>(&cred_source, adv.as_slice());
+ let arena = deserialization_arena!();
+ let cred_book =
+ CredentialBookBuilder::build_cached_slice_book::<0, 0, CryptoProviderImpl>(&creds, &[]);
+ let contents = deser_v0::<_, CryptoProviderImpl>(arena, adv.as_slice(), &cred_book);
match adv_config.identity {
// we ended up generating plaintext, so it's fine
None => assert_adv_equals(&adv_config, &contents),
Some(_) => {
// we generated an encrypted adv, but didn't include the credential
- assert_eq!(V0AdvContents::NoMatchingCredentials, contents);
+ assert_eq!(V0AdvertisementContents::NoMatchingCredentials, contents);
}
}
}
}
-fn assert_adv_equals<'m>(
+/// Short-hand for asserting that the contents of two V0 advertisements
+/// are the same for tests where we only ever have 0-1 broadcasting
+/// identities in play.
+fn assert_adv_equals<M: MatchedCredential + AsRef<EmptyMatchedCredential>>(
adv_config: &AdvConfig,
- adv: &V0AdvContents<'m, SimpleMatchedCredential<'m, [u8; 32]>>,
+ adv: &V0AdvertisementContents<M>,
) {
match adv_config.identity {
None => match adv {
- V0AdvContents::Plaintext(p) => {
+ V0AdvertisementContents::Plaintext(p) => {
let mut action_bits = ActionBits::default();
action_bits.set_action(ContextSyncSeqNum::try_from(3).unwrap());
let de = ActionsDataElement::from(action_bits);
assert_eq!(adv_config.plaintext_mode.unwrap(), p.identity());
assert_eq!(
- vec![&PlainDataElement::Actions(de)],
- p.data_elements().collect::<Vec<_>>()
+ vec![PlainDataElement::Actions(de)],
+ p.data_elements().collect::<Result<Vec<_>, _>>().unwrap()
)
}
_ => panic!("should be a plaintext adv"),
},
Some(_) => match adv {
- V0AdvContents::Decrypted(wmc) => {
+ V0AdvertisementContents::Decrypted(wmc) => {
assert!(adv_config.plaintext_mode.is_none());
// different generic type param, so can't re-use the DE from above
@@ -143,15 +168,15 @@ fn assert_adv_equals<'m>(
let de = ActionsDataElement::from(action_bits);
assert_eq!(
- vec![&PlainDataElement::Actions(de)],
- wmc.contents().data_elements().collect::<Vec<_>>()
+ vec![PlainDataElement::Actions(de)],
+ wmc.contents().data_elements().collect::<Result<Vec<_>, _>>().unwrap()
);
assert_eq!(
adv_config.identity.unwrap().identity_type,
wmc.contents().identity_type()
);
assert_eq!(
- &adv_config.identity.unwrap().legacy_metadata_key,
+ adv_config.identity.unwrap().legacy_metadata_key,
wmc.contents().metadata_key()
);
}
@@ -160,16 +185,19 @@ fn assert_adv_equals<'m>(
}
}
-fn deser_v0<'s, C, S, P>(
- cred_source: &'s S,
- adv: &[u8],
-) -> V0AdvContents<'s, SimpleMatchedCredential<'s, [u8; 32]>>
+fn deser_v0<'adv, B, P>(
+ arena: DeserializationArena<'adv>,
+ adv: &'adv [u8],
+ cred_book: &'adv B,
+) -> V0AdvertisementContents<'adv, B::Matched>
where
- C: V0Credential<Matched<'s> = SimpleMatchedCredential<'s, [u8; 32]>> + 's,
- S: CredentialSource<C>,
+ B: CredentialBook<'adv>,
P: CryptoProvider,
{
- deserialize_v0_advertisement::<C, S, P>(adv, cred_source).unwrap()
+ deserialize_advertisement::<_, P>(arena, adv, cred_book)
+ .expect("Should be a valid advertisement")
+ .into_v0()
+ .expect("Should be V0")
}
/// Populate an advertisement with a randomly chosen identity and a DE
@@ -179,7 +207,7 @@ fn adv_random_identity<'a, R: rand::Rng>(
) -> (ArrayView<u8, { BLE_ADV_SVC_CONTENT_LEN }>, AdvConfig<'a>) {
let identity = identities.choose(&mut rng).unwrap();
if rng.gen_bool(0.5) {
- let mut adv_builder = AdvBuilder::new(PublicIdentity::default());
+ let mut adv_builder = AdvBuilder::new(PublicIdentity);
add_de(&mut adv_builder);
(
@@ -187,11 +215,14 @@ fn adv_random_identity<'a, R: rand::Rng>(
AdvConfig::new(None, Some(PlaintextIdentityMode::Public)),
)
} else {
+ let broadcast_cm = SimpleBroadcastCryptoMaterial::<V0>::new(
+ identity.key_seed,
+ identity.legacy_metadata_key,
+ );
let mut adv_builder = AdvBuilder::new(LdtIdentity::<CryptoProviderImpl>::new(
identity.identity_type,
LegacySalt::from(rng.gen::<[u8; 2]>()),
- identity.legacy_metadata_key,
- LdtEncrypterXtsAes128::<CryptoProviderImpl>::new(&identity.hkdf().legacy_ldt_key()),
+ &broadcast_cm,
));
add_de(&mut adv_builder);
@@ -214,7 +245,7 @@ where
struct TestIdentity<C: CryptoProvider> {
identity_type: EncryptedIdentityDataElementType,
key_seed: [u8; 32],
- legacy_metadata_key: [u8; 14],
+ legacy_metadata_key: ShortMetadataKey,
_marker: PhantomData<C>,
}
@@ -227,20 +258,17 @@ impl<C: CryptoProvider> TestIdentity<C> {
.choose(rng)
.unwrap(),
key_seed: rng.gen(),
- legacy_metadata_key: rng.gen(),
+ legacy_metadata_key: ShortMetadataKey(rng.gen()),
_marker: PhantomData,
}
}
- /// Returns a credential using crypto material from this identity
- fn credential(&self) -> SimpleV0Credential<MinimumFootprintV0CryptoMaterial, [u8; 32]> {
+ /// Returns a discovery-credential using crypto material from this identity
+ fn discovery_credential(&self) -> V0DiscoveryCredential {
let hkdf = self.hkdf();
- SimpleV0Credential::new(
- MinimumFootprintV0CryptoMaterial::new(
- self.key_seed,
- hkdf.legacy_metadata_key_hmac_key().calculate_hmac(&self.legacy_metadata_key),
- ),
+ V0DiscoveryCredential::new(
self.key_seed,
+ hkdf.legacy_metadata_key_hmac_key().calculate_hmac(&self.legacy_metadata_key.0),
)
}
diff --git a/nearby/presence/np_adv/src/deser_v1_tests.rs b/nearby/presence/np_adv/src/deser_v1_tests.rs
index 8076e99..e0cabc9 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 _};
@@ -19,12 +21,17 @@ extern crate std;
use crate::{
credential::{
- simple::{SimpleMatchedCredential, SimpleV1Credential},
- source::SliceCredentialSource,
- v1::MinimumFootprintV1CryptoMaterial,
+ book::{CredentialBook, CredentialBookBuilder},
+ v1::{
+ SignedBroadcastCryptoMaterial, SimpleSignedBroadcastCryptoMaterial,
+ V1DiscoveryCredential,
+ },
+ EmptyMatchedCredential, MatchableCredential, MatchedCredential,
},
de_type::EncryptedIdentityDataElementType,
- deserialize_v1_advertisement,
+ deserialization_arena,
+ deserialization_arena::DeserializationArena,
+ deserialize_advertisement,
extended::{
data_elements::GenericDataElement,
deserialize::VerificationMode,
@@ -33,8 +40,8 @@ use crate::{
PublicSectionEncoder, SectionBuilder, SectionEncoder, SignedEncryptedSectionEncoder,
},
},
- AdvDeserializationError, AdvDeserializationErrorDetailsHazmat, CredentialSource,
- PlaintextIdentityMode, Section, V1AdvContents, V1Credential, V1DeserializedSection,
+ AdvDeserializationError, AdvDeserializationErrorDetailsHazmat, HasIdentityMatch, MetadataKey,
+ PlaintextIdentityMode, Section, V1AdvertisementContents, V1DeserializedSection,
};
use crypto_provider::{CryptoProvider, CryptoRng};
use std::{collections, prelude::rust_2021::*, vec};
@@ -55,11 +62,20 @@ fn v1_plaintext() {
let section_configs: Vec<SectionConfig<CryptoProviderImpl>> =
fill_plaintext_adv(&mut rng, &mut adv_builder);
let adv = adv_builder.into_advertisement();
- let creds = identities.iter().map(|i| i.credential()).collect::<Vec<_>>();
- let cred_source = SliceCredentialSource::new(&creds);
+ let creds = identities
+ .iter()
+ .map(|i| MatchableCredential {
+ discovery_credential: i.discovery_credential(),
+ match_data: EmptyMatchedCredential,
+ })
+ .collect::<Vec<_>>();
+
+ let arena = deserialization_arena!();
// check if the section is empty or there is more than one public section
+ let cred_book =
+ CredentialBookBuilder::build_cached_slice_book::<0, 0, CryptoProviderImpl>(&[], &creds);
if section_configs.is_empty() {
- let v1_error = deser_v1_error::<_, _, CryptoProviderImpl>(&cred_source, &adv);
+ let v1_error = deser_v1_error::<_, CryptoProviderImpl>(arena, &adv, &cred_book);
assert_eq!(
v1_error,
AdvDeserializationError::ParseError {
@@ -68,7 +84,7 @@ fn v1_plaintext() {
}
); //assert a adv deserialization error
} else {
- let v1_contents = deser_v1::<_, _, CryptoProviderImpl>(&cred_source, &adv);
+ let v1_contents = deser_v1::<_, CryptoProviderImpl>(arena, &adv, &cred_book);
assert_eq!(0, v1_contents.invalid_sections_count());
assert_eq!(section_configs.len(), v1_contents.sections.len());
for (section_config, section) in section_configs.iter().zip(v1_contents.sections.iter())
@@ -90,11 +106,19 @@ fn v1_all_identities_resolvable_ciphertext() {
let mut adv_builder = AdvBuilder::new(AdvertisementType::Encrypted);
let section_configs = fill_encrypted_adv(&mut rng, &identities, &mut adv_builder);
let adv = adv_builder.into_advertisement();
- let creds = identities.iter().map(|i| i.credential()).collect::<Vec<_>>();
- let cred_source = SliceCredentialSource::new(&creds);
+ let creds = identities
+ .iter()
+ .map(|i| MatchableCredential {
+ discovery_credential: i.discovery_credential(),
+ match_data: EmptyMatchedCredential,
+ })
+ .collect::<Vec<_>>();
+ let arena = deserialization_arena!();
// check if the section header would be 0 or if the section is empty
+ let cred_book =
+ CredentialBookBuilder::build_cached_slice_book::<0, 0, CryptoProviderImpl>(&[], &creds);
if section_configs.is_empty() {
- let v1_error = deser_v1_error::<_, _, CryptoProviderImpl>(&cred_source, &adv);
+ let v1_error = deser_v1_error::<_, CryptoProviderImpl>(arena, &adv, &cred_book);
assert_eq!(
v1_error,
AdvDeserializationError::ParseError {
@@ -103,7 +127,7 @@ fn v1_all_identities_resolvable_ciphertext() {
}
); //assert a adv deserialization error
} else {
- let v1_contents = deser_v1::<_, _, CryptoProviderImpl>(&cred_source, &adv);
+ let v1_contents = deser_v1::<_, CryptoProviderImpl>(arena, &adv, &cred_book);
assert_eq!(0, v1_contents.invalid_sections_count());
assert_eq!(section_configs.len(), v1_contents.sections.len());
for (section_config, section) in section_configs.iter().zip(v1_contents.sections.iter())
@@ -133,12 +157,18 @@ fn v1_only_non_matching_identities_available_ciphertext() {
.iter()
.any(|sc| sc.identity.map(|sci| sci.key_seed == i.key_seed).unwrap_or(false))
})
- .map(|i| i.credential())
+ .map(|i| MatchableCredential {
+ discovery_credential: i.discovery_credential(),
+ match_data: EmptyMatchedCredential,
+ })
.collect::<Vec<_>>();
- let cred_source = SliceCredentialSource::new(&creds);
+
+ let arena = deserialization_arena!();
// check if the section header would be 0
+ let cred_book =
+ CredentialBookBuilder::build_cached_slice_book::<0, 0, CryptoProviderImpl>(&[], &creds);
if section_configs.is_empty() {
- let v1_error = deser_v1_error::<_, _, CryptoProviderImpl>(&cred_source, &adv);
+ let v1_error = deser_v1_error::<_, CryptoProviderImpl>(arena, &adv, &cred_book);
assert_eq!(
v1_error,
AdvDeserializationError::ParseError {
@@ -147,7 +177,7 @@ fn v1_only_non_matching_identities_available_ciphertext() {
}
); //assert a adv deserialization error
} else {
- let v1_contents = deser_v1::<_, _, CryptoProviderImpl>(&cred_source, &adv);
+ let v1_contents = deser_v1::<_, CryptoProviderImpl>(arena, &adv, &cred_book);
// all encrypted identity sections are invalid
let encrypted_section_count =
section_configs.iter().filter(|sc| sc.identity.is_some()).count();
@@ -178,13 +208,15 @@ fn v1_no_creds_available_ciphertext() {
&mut adv_builder,
);
let adv = adv_builder.into_advertisement();
- let cred_source: SliceCredentialSource<
- '_,
- SimpleV1Credential<MinimumFootprintV1CryptoMaterial, [u8; 32]>,
- > = SliceCredentialSource::new(&[]);
+ let arena = deserialization_arena!();
// check if the section header would be 0
+ let cred_book = CredentialBookBuilder::<EmptyMatchedCredential>::build_cached_slice_book::<
+ 0,
+ 0,
+ CryptoProviderImpl,
+ >(&[], &[]);
if section_configs.is_empty() {
- let v1_error = deser_v1_error::<_, _, CryptoProviderImpl>(&cred_source, &adv);
+ let v1_error = deser_v1_error::<_, CryptoProviderImpl>(arena, &adv, &cred_book);
assert_eq!(
v1_error,
AdvDeserializationError::ParseError {
@@ -193,7 +225,7 @@ fn v1_no_creds_available_ciphertext() {
}
); //assert a adv deserialization error
} else {
- let v1_contents = deser_v1::<_, _, CryptoProviderImpl>(&cred_source, &adv);
+ let v1_contents = deser_v1::<_, CryptoProviderImpl>(arena, &adv, &cred_book);
// all encrypted identity sections are invalid
let encrypted_section_count =
section_configs.iter().filter(|sc| sc.identity.is_some()).count();
@@ -240,12 +272,20 @@ fn v1_only_some_matching_identities_available_ciphertext() {
let creds = identities
.iter()
.filter(|i| !identities_to_remove.contains(&i.key_seed))
- .map(|i| i.credential())
+ .map(|i| MatchableCredential {
+ discovery_credential: i.discovery_credential(),
+ match_data: EmptyMatchedCredential,
+ })
.collect::<Vec<_>>();
- let cred_source = SliceCredentialSource::new(&creds);
+
+ let arena = deserialization_arena!();
+
+ let cred_book =
+ CredentialBookBuilder::build_cached_slice_book::<0, 0, CryptoProviderImpl>(&[], &creds);
+
// check if the section header would be 0
if section_configs.is_empty() {
- let v1_error = deser_v1_error::<_, _, CryptoProviderImpl>(&cred_source, &adv);
+ let v1_error = deser_v1_error::<_, CryptoProviderImpl>(arena, &adv, &cred_book);
assert_eq!(
v1_error,
AdvDeserializationError::ParseError {
@@ -254,7 +294,7 @@ fn v1_only_some_matching_identities_available_ciphertext() {
}
); //assert a adv deserialization error
} else {
- let v1_contents = deser_v1::<_, _, CryptoProviderImpl>(&cred_source, &adv);
+ let v1_contents = deser_v1::<_, CryptoProviderImpl>(arena, &adv, &cred_book);
let affected_sections: Vec<_> = section_configs
.iter()
@@ -282,9 +322,9 @@ fn v1_only_some_matching_identities_available_ciphertext() {
}
}
-fn assert_section_equals<'m, C: CryptoProvider>(
+fn assert_section_equals<M: MatchedCredential, C: CryptoProvider>(
section_config: &SectionConfig<C>,
- section: &V1DeserializedSection<'m, SimpleMatchedCredential<'m, [u8; 32]>>,
+ section: &V1DeserializedSection<M>,
) {
match section_config.identity {
None => match section {
@@ -294,7 +334,7 @@ fn assert_section_equals<'m, C: CryptoProvider>(
assert_eq!(section_config.plaintext_mode.unwrap(), p.identity());
assert_eq!(
section_config.data_elements,
- p.data_elements().map(|de| de.into()).collect::<Vec<_>>()
+ p.iter_data_elements().map(|de| (&de.unwrap()).into()).collect::<Vec<_>>()
)
}
V1DeserializedSection::Decrypted(_) => panic!("no id, but decrypted section"),
@@ -306,14 +346,17 @@ fn assert_section_equals<'m, C: CryptoProvider>(
assert_eq!(
section_config.data_elements,
- wmc.contents().data_elements().map(|de| de.into()).collect::<Vec<_>>()
+ wmc.contents()
+ .iter_data_elements()
+ .map(|de| (&de.unwrap()).into())
+ .collect::<Vec<GenericDataElement>>()
);
assert_eq!(
section_config.identity.unwrap().identity_type,
wmc.contents().identity_type()
);
assert_eq!(
- &section_config.identity.unwrap().extended_metadata_key,
+ section_config.identity.unwrap().extended_metadata_key,
wmc.contents().metadata_key()
);
assert_eq!(
@@ -325,32 +368,35 @@ fn assert_section_equals<'m, C: CryptoProvider>(
}
}
-fn deser_v1_error<'s, C, S, P>(
- cred_source: &'s S,
- adv: &'s EncodedAdvertisement,
+fn deser_v1_error<'a, B, P>(
+ arena: DeserializationArena<'a>,
+ adv: &'a EncodedAdvertisement,
+ cred_book: &'a B,
) -> AdvDeserializationError
where
- C: V1Credential<Matched<'s> = SimpleMatchedCredential<'s, [u8; 32]>> + 's,
- S: CredentialSource<C>,
+ B: CredentialBook<'a>,
P: CryptoProvider,
{
- let v1_contents = match deserialize_v1_advertisement::<C, S, P>(adv.as_slice(), cred_source) {
+ let v1_contents = match deserialize_advertisement::<_, P>(arena, adv.as_slice(), cred_book) {
Err(e) => e,
_ => panic!("Expecting an error!"),
};
v1_contents
}
-fn deser_v1<'s, C, S, P>(
- cred_source: &'s S,
- adv: &'s EncodedAdvertisement,
-) -> V1AdvContents<'s, SimpleMatchedCredential<'s, [u8; 32]>>
+fn deser_v1<'adv, B, P>(
+ arena: DeserializationArena<'adv>,
+ adv: &'adv EncodedAdvertisement,
+ cred_book: &'adv B,
+) -> V1AdvertisementContents<'adv, B::Matched>
where
- C: V1Credential<Matched<'s> = SimpleMatchedCredential<'s, [u8; 32]>> + 's,
- S: CredentialSource<C>,
+ B: CredentialBook<'adv>,
P: CryptoProvider,
{
- deserialize_v1_advertisement::<C, S, P>(adv.as_slice(), cred_source).unwrap()
+ deserialize_advertisement::<_, P>(arena, adv.as_slice(), cred_book)
+ .expect("Should be a valid advertisement")
+ .into_v1()
+ .expect("Should be V1")
}
/// Populate a random number of sections with randomly chosen identities and random DEs
@@ -392,13 +438,15 @@ fn fill_encrypted_adv<'a, R: rand::Rng, C: CryptoProvider>(
for _ in 0..rng.gen_range(0..=6) {
let chosen_index = rng.gen_range(0..identities.len());
let identity = &identities[chosen_index];
+
+ let broadcast_cm = identity.broadcast_credential();
+
let res = if rng.gen_bool(0.5) {
adv_builder
- .section_builder(MicEncryptedSectionEncoder::new_random_salt(
+ .section_builder(MicEncryptedSectionEncoder::<C>::new_random_salt(
&mut salt_rng,
identity.identity_type,
- &identity.extended_metadata_key,
- &identity.hkdf(),
+ &broadcast_cm,
))
.map(|s| {
SectionConfig::new(
@@ -410,12 +458,10 @@ fn fill_encrypted_adv<'a, R: rand::Rng, C: CryptoProvider>(
})
} else {
adv_builder
- .section_builder(SignedEncryptedSectionEncoder::new_random_salt(
+ .section_builder(SignedEncryptedSectionEncoder::<C>::new_random_salt(
&mut salt_rng,
identity.identity_type,
- &identity.extended_metadata_key,
- &identity.key_pair,
- &identity.hkdf(),
+ &broadcast_cm,
))
.map(|s| {
SectionConfig::new(
@@ -440,7 +486,7 @@ fn fill_encrypted_adv<'a, R: rand::Rng, C: CryptoProvider>(
struct TestIdentity<C: CryptoProvider> {
identity_type: EncryptedIdentityDataElementType,
key_seed: [u8; 32],
- extended_metadata_key: [u8; 16],
+ extended_metadata_key: MetadataKey,
key_pair: np_ed25519::KeyPair<C>,
marker: PhantomData<C>,
}
@@ -453,33 +499,27 @@ impl<C: CryptoProvider> TestIdentity<C> {
.choose(rng)
.unwrap(),
key_seed: rng.gen(),
- extended_metadata_key: rng.gen(),
+ extended_metadata_key: MetadataKey(rng.gen()),
key_pair: np_ed25519::KeyPair::<C>::generate(),
marker: PhantomData,
}
}
- /// Returns a credential using crypto material from this identity
- fn credential(&self) -> SimpleV1Credential<MinimumFootprintV1CryptoMaterial, [u8; 32]> {
- let hkdf = self.hkdf();
- SimpleV1Credential::new(
- MinimumFootprintV1CryptoMaterial::new(
- self.key_seed,
- hkdf.extended_unsigned_metadata_key_hmac_key()
- .calculate_hmac(&self.extended_metadata_key),
- hkdf.extended_signed_metadata_key_hmac_key()
- .calculate_hmac(&self.extended_metadata_key),
- self.key_pair.public(),
- ),
+ /// Returns a (simple, signed) broadcast credential using crypto material from this identity
+ fn broadcast_credential(&self) -> SimpleSignedBroadcastCryptoMaterial {
+ SimpleSignedBroadcastCryptoMaterial::new(
self.key_seed,
+ self.extended_metadata_key,
+ self.key_pair.private_key(),
)
}
- fn hkdf(&self) -> np_hkdf::NpKeySeedHkdf<C> {
- np_hkdf::NpKeySeedHkdf::<C>::new(&self.key_seed)
+ /// Returns a discovery credential using crypto material from this identity
+ fn discovery_credential(&self) -> V1DiscoveryCredential {
+ self.broadcast_credential().derive_v1_discovery_credential::<C>()
}
}
/// Add several DEs with random types and contents
fn add_des<I: SectionEncoder, R: rand::Rng>(
- mut sb: SectionBuilder<I>,
+ mut sb: SectionBuilder<&mut AdvBuilder, I>,
rng: &mut R,
) -> Vec<GenericDataElement> {
let mut des = Vec::new();
diff --git a/nearby/presence/np_adv/src/deserialization_arena.rs b/nearby/presence/np_adv/src/deserialization_arena.rs
new file mode 100644
index 0000000..9b58cbf
--- /dev/null
+++ b/nearby/presence/np_adv/src/deserialization_arena.rs
@@ -0,0 +1,119 @@
+// 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.
+
+//! Types for creating arenas used in deserialization of np_adv. This implementation is purpose-made
+//! for deserializing in `np_adv` and is not intended for general use as an arena.
+
+use crate::extended::BLE_ADV_SVC_CONTENT_LEN;
+
+/// Create a [`DeserializationArena`] suitable for use with deserializing an advertisement.
+#[macro_export]
+macro_rules! deserialization_arena {
+ // Trick borrowed from `pin!`: In an argument to a braced constructor, if we take a reference to
+ // a temporary value, that value will be upgraded to live for the scope of the enclosing block,
+ // avoiding another `let` binding for the buffer which is normally required to keep the buffer
+ // on the stack.
+ // Reference: https://doc.rust-lang.org/reference/destructors.html#temporary-lifetime-extension
+ () => {
+ $crate::deserialization_arena::DeserializationArena {
+ buffer: &mut $crate::deserialization_arena::DeserializationArena::new_buffer(),
+ }
+ };
+}
+
+/// A simple allocator that simply keeps splitting the given slice on allocation. Use the
+/// [`deserialization_arena!`][crate::deserialization_arena!] macro to create an instance.
+pub(crate) struct DeserializationArenaAllocator<'a> {
+ #[doc(hidden)]
+ slice: &'a mut [u8],
+}
+
+impl<'a> DeserializationArenaAllocator<'a> {
+ /// Allocates `len` bytes from the slice given upon construction. In the expected use case, the
+ /// allocated slice should be written to with actual data, overwriting what's contained in
+ /// there. While reading from the allocated slice without first writing to it is safe in the
+ /// Rust memory-safety sense, the returned slice contains "garbage" data from the slice given
+ /// during construction.
+ ///
+ /// Returns an error with [`ArenaOutOfSpace`] if the remaining slice is not long enough to
+ /// allocate `len` bytes.
+ pub fn allocate(&mut self, len: u8) -> Result<&'a mut [u8], ArenaOutOfSpace> {
+ if usize::from(len) > self.slice.len() {
+ return Err(ArenaOutOfSpace);
+ }
+ let (allocated, remaining) = core::mem::take(&mut self.slice).split_at_mut(len.into());
+ self.slice = remaining;
+ // Note: the returned data is logically garbage. While it's deterministic (not UB),
+ // semantically this should be treated as a write only slice.
+ Ok(allocated)
+ }
+}
+
+/// A simple allocator that simply keeps splitting the given slice on allocation. Use the
+/// [`deserialization_arena!`][crate::deserialization_arena!] macro to create an instance.
+pub struct DeserializationArena<'a> {
+ #[doc(hidden)] // Exposed for use by `deserialization_arena!` only.
+ pub buffer: &'a mut [u8; BLE_ADV_SVC_CONTENT_LEN],
+}
+
+impl<'a> DeserializationArena<'a> {
+ #[doc(hidden)] // Exposed for use by `deserialization_arena!` only.
+ pub fn new_buffer() -> [u8; BLE_ADV_SVC_CONTENT_LEN] {
+ [0; BLE_ADV_SVC_CONTENT_LEN]
+ }
+
+ /// Convert this arena into an allocator that can start allocating.
+ pub(crate) fn into_allocator(self) -> DeserializationArenaAllocator<'a> {
+ DeserializationArenaAllocator { slice: self.buffer }
+ }
+}
+
+/// Error indicating that the arena has ran out of space, and deserialization cannot proceed. This
+/// should never happen if the arena is created with [`crate::deserialization_arena!`], since the
+/// total size of decrypted sections should be less than the size of the incoming BLE advertisement.
+#[derive(Debug, PartialEq, Eq)]
+pub(crate) struct ArenaOutOfSpace;
+
+#[cfg(test)]
+mod test {
+ use crate::{deserialization_arena::ArenaOutOfSpace, extended::BLE_ADV_SVC_CONTENT_LEN};
+
+ #[test]
+ fn test_creation() {
+ assert_eq!(BLE_ADV_SVC_CONTENT_LEN, deserialization_arena!().buffer.len());
+ }
+
+ #[test]
+ fn test_allocation() {
+ let arena = deserialization_arena!();
+ let mut allocator = arena.into_allocator();
+ assert_eq!(Ok(&mut [0_u8; 100][..]), allocator.allocate(100));
+ assert_eq!(BLE_ADV_SVC_CONTENT_LEN - 100, allocator.slice.len());
+ }
+
+ #[test]
+ fn test_allocation_overflow() {
+ let arena = deserialization_arena!();
+ let mut allocator = arena.into_allocator();
+ assert_eq!(Err(ArenaOutOfSpace), allocator.allocate(u8::MAX));
+ }
+
+ #[test]
+ fn test_allocation_twice_overflow() {
+ let arena = deserialization_arena!();
+ let mut allocator = arena.into_allocator();
+ assert_eq!(Ok(&mut [0_u8; 128][..]), allocator.allocate(128));
+ assert_eq!(Err(ArenaOutOfSpace), allocator.allocate(128));
+ }
+}
diff --git a/nearby/presence/np_adv/src/extended/data_elements/mod.rs b/nearby/presence/np_adv/src/extended/data_elements/mod.rs
index 2058be1..4e6e481 100644
--- a/nearby/presence/np_adv/src/extended/data_elements/mod.rs
+++ b/nearby/presence/np_adv/src/extended/data_elements/mod.rs
@@ -67,8 +67,8 @@ impl WriteDataElement for GenericDataElement {
}
/// Convert a deserialized DE into one you can serialize
-impl<'a> From<DataElement<'a>> for GenericDataElement {
- fn from(de: DataElement<'a>) -> Self {
+impl<'a> From<&'a DataElement<'a>> for GenericDataElement {
+ fn from(de: &'a DataElement<'a>) -> Self {
Self::try_from(de.de_type(), de.contents())
.expect("Deserialized DE must have a valid length")
}
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 DeType {
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<u32> for DeType {
}
}
+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 2fe6f69..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,24 +12,28 @@
// 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::deserialization_arena;
use crate::extended::data_elements::TxPowerDataElement;
+use crate::extended::deserialize::DataElementParseError;
use crate::extended::serialize::{AdvertisementType, SingleTypeDataElement, WriteDataElement};
use crate::shared_data::TxPower;
use crate::{
- credential::v1::MinimumFootprintV1CryptoMaterial,
+ credential::{v1::V1, SimpleBroadcastCryptoMaterial},
de_type::EncryptedIdentityDataElementType,
extended::{
deserialize::{
encrypted_section::{
- DeserializationError, EncryptedSectionContents,
- IdentityResolutionOrDeserializationError, MicVerificationError,
+ EncryptedSectionContents, IdentityResolutionOrDeserializationError,
+ MicVerificationError,
},
parse_sections,
- test_stubs::{HkdfCryptoMaterial, IntermediateSectionExt},
- CiphertextSection, OffsetDataElement, RawV1Salt,
+ test_stubs::IntermediateSectionExt,
+ CiphertextSection, DataElement, RawV1Salt,
},
serialize::{AdvBuilder, CapacityLimitedVec, MicEncryptedSectionEncoder},
},
@@ -42,20 +46,20 @@ use std::{prelude::rust_2021::*, vec};
#[test]
fn deserialize_mic_encrypted_correct_keys() {
- let metadata_key = [1; 16];
+ let metadata_key = MetadataKey([1; 16]);
let key_seed = [2; 32];
let raw_salt = RawV1Salt([3; 16]);
let section_salt = V1Salt::<CryptoProviderImpl>::from(raw_salt);
let identity_type = EncryptedIdentityDataElementType::Private;
- let key_seed_hkdf = np_hkdf::NpKeySeedHkdf::<CryptoProviderImpl>::new(&key_seed);
let mut adv_builder = AdvBuilder::new(AdvertisementType::Encrypted);
+ let broadcast_cm = SimpleBroadcastCryptoMaterial::<V1>::new(key_seed, metadata_key);
+
let mut section_builder = adv_builder
- .section_builder(MicEncryptedSectionEncoder::new_for_testing(
+ .section_builder(MicEncryptedSectionEncoder::<CryptoProviderImpl>::new(
identity_type,
V1Salt::from(*section_salt.as_array_ref()),
- &metadata_key,
- &key_seed_hkdf,
+ &broadcast_cm,
))
.unwrap();
@@ -88,19 +92,20 @@ fn deserialize_mic_encrypted_correct_keys() {
let keypair = np_ed25519::KeyPair::<CryptoProviderImpl>::generate();
// deserializing to Section works
- let crypto_material = MinimumFootprintV1CryptoMaterial::new::<CryptoProviderImpl>(
- key_seed,
- key_seed_hkdf.extended_unsigned_metadata_key_hmac_key().calculate_hmac(&metadata_key),
- key_seed_hkdf.extended_signed_metadata_key_hmac_key().calculate_hmac(&metadata_key),
- keypair.public(),
- );
+ let discovery_credential =
+ SimpleSignedBroadcastCryptoMaterial::new(key_seed, metadata_key, keypair.private_key())
+ .derive_v1_discovery_credential::<CryptoProviderImpl>();
+
let identity_resolution_material =
- crypto_material.unsigned_identity_resolution_material::<CryptoProviderImpl>();
+ discovery_credential.unsigned_identity_resolution_material::<CryptoProviderImpl>();
let verification_material =
- crypto_material.unsigned_verification_material::<CryptoProviderImpl>();
+ discovery_credential.unsigned_verification_material::<CryptoProviderImpl>();
+ let arena = deserialization_arena!();
+ let mut allocator = arena.into_allocator();
let section = contents
.try_resolve_identity_and_deserialize::<CryptoProviderImpl>(
+ &mut allocator,
&identity_resolution_material,
&verification_material,
)
@@ -114,27 +119,28 @@ fn deserialize_mic_encrypted_correct_keys() {
raw_salt,
SectionContents {
section_header: 19 // encryption info de
- + 2 // de header
- + 16 // metadata key
- + 2 // de contents
- + 16, // mic hmac tag
+ + 2 // de header
+ + 16 // metadata key
+ + 2 // de contents
+ + 16, // mic hmac tag
// battery DE
- de_data: ArrayView::try_from_slice(&[5]).unwrap(),
- data_elements: vec![OffsetDataElement {
- offset: 2.into(),
- de_type: 0x05_u8.into(),
- start_of_contents: 0,
- contents_len: 1
- }]
+ data_element_start_offset: 2,
+ de_region_excl_identity: &[txpower_de.de_header().serialize().as_slice(), &[5],]
+ .concat(),
}
),
section
);
+ let data_elements = section.collect_data_elements().unwrap();
+ assert_eq!(
+ data_elements,
+ &[DataElement { offset: 2.into(), de_type: 0x05_u8.into(), contents: &[5] }]
+ );
assert_eq!(
- vec![(DataElementOffset::from(2_usize), TxPowerDataElement::DE_TYPE, vec![5_u8])],
- section
- .data_elements()
+ vec![(DataElementOffset::from(2_u8), TxPowerDataElement::DE_TYPE, vec![5_u8])],
+ data_elements
+ .into_iter()
.map(|de| (de.offset(), de.de_type(), de.contents().to_vec()))
.collect::<Vec<_>>()
);
@@ -173,18 +179,23 @@ fn deserialize_mic_encrypted_correct_keys() {
contents.contents.compute_identity_resolution_contents::<CryptoProviderImpl>();
let identity_match = identity_resolution_contents
.try_match::<CryptoProviderImpl>(
- identity_resolution_material.as_raw_resolution_material(),
+ &identity_resolution_material.into_raw_resolution_material(),
)
.unwrap();
- let decrypted = contents.contents.decrypt_ciphertext::<CryptoProviderImpl>(identity_match);
+ let arena = deserialization_arena!();
+ let mut allocator = arena.into_allocator();
+ let decrypted = contents
+ .contents
+ .decrypt_ciphertext::<CryptoProviderImpl>(&mut allocator, identity_match)
+ .unwrap();
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.as_slice());
+ assert_eq!(&expected, decrypted.plaintext_contents);
}
}
@@ -240,7 +251,9 @@ fn deserialize_mic_encrypted_incorrect_expected_metadata_key_hmac_error() {
fn deserialize_mic_encrypted_incorrect_salt_error() {
// bad salt -> bad iv -> bad metadata key plaintext
do_bad_deserialize_tampered(
- IdentityResolutionOrDeserializationError::IdentityMatchingError,
+ DeserializeError::IdentityResolutionOrDeserializationError(
+ IdentityResolutionOrDeserializationError::IdentityMatchingError,
+ ),
|_| {},
|adv| adv[23..39].copy_from_slice(&[0xFF; 16]),
);
@@ -250,7 +263,7 @@ fn deserialize_mic_encrypted_incorrect_salt_error() {
fn deserialize_mic_encrypted_de_that_wont_parse() {
// add an extra byte to the section, leading it to try to parse a DE that doesn't exist
do_bad_deserialize_tampered(
- DeserializationError::DeParseError.into(),
+ DeserializeError::DataElementParseError(DataElementParseError::UnexpectedDataAfterEnd),
|sec| sec.try_push(0xFF).unwrap(),
|_| {},
);
@@ -260,7 +273,9 @@ fn deserialize_mic_encrypted_de_that_wont_parse() {
fn deserialize_mic_encrypted_tampered_mic_error() {
// flip the a bit in the first MIC byte
do_bad_deserialize_tampered(
- MicVerificationError::MicMismatch.into(),
+ DeserializeError::IdentityResolutionOrDeserializationError(
+ MicVerificationError::MicMismatch.into(),
+ ),
|_| {},
|adv| {
let mic_start = adv.len() - 16;
@@ -273,7 +288,9 @@ fn deserialize_mic_encrypted_tampered_mic_error() {
fn deserialize_mic_encrypted_tampered_payload_error() {
// flip the last payload bit
do_bad_deserialize_tampered(
- MicVerificationError::MicMismatch.into(),
+ DeserializeError::IdentityResolutionOrDeserializationError(
+ MicVerificationError::MicMismatch.into(),
+ ),
|_| {},
|adv| *adv.last_mut().unwrap() ^= 0x01,
);
@@ -288,7 +305,7 @@ fn do_bad_deserialize_params<C: CryptoProvider>(
mic_hmac_key: Option<np_hkdf::NpHmacSha256Key<C>>,
expected_metadata_key_hmac: Option<[u8; 32]>,
) {
- let metadata_key = [1; 16];
+ let metadata_key = MetadataKey([1; 16]);
let key_seed = [2; 32];
let section_salt: V1Salt<C> = [3; 16].into();
let identity_type = EncryptedIdentityDataElementType::Private;
@@ -296,12 +313,13 @@ fn do_bad_deserialize_params<C: CryptoProvider>(
let mut adv_builder = AdvBuilder::new(AdvertisementType::Encrypted);
+ let broadcast_cm = SimpleBroadcastCryptoMaterial::<V1>::new(key_seed, metadata_key);
+
let mut section_builder = adv_builder
- .section_builder(MicEncryptedSectionEncoder::new_for_testing(
+ .section_builder(MicEncryptedSectionEncoder::<C>::new(
identity_type,
section_salt,
- &metadata_key,
- &key_seed_hkdf,
+ &broadcast_cm,
))
.unwrap();
@@ -336,7 +354,7 @@ fn do_bad_deserialize_params<C: CryptoProvider>(
.unwrap_or_else(|| key_seed_hkdf.extended_unsigned_metadata_key_hmac_key())
.as_bytes(),
expected_metadata_key_hmac: expected_metadata_key_hmac.unwrap_or_else(|| {
- key_seed_hkdf.extended_unsigned_metadata_key_hmac_key().calculate_hmac(&metadata_key)
+ key_seed_hkdf.extended_unsigned_metadata_key_hmac_key().calculate_hmac(&metadata_key.0)
}),
};
let identity_resolution_material =
@@ -352,6 +370,7 @@ fn do_bad_deserialize_params<C: CryptoProvider>(
error,
contents
.try_resolve_identity_and_deserialize::<C>(
+ &mut deserialization_arena!().into_allocator(),
&identity_resolution_material,
&verification_material,
)
@@ -359,28 +378,33 @@ fn do_bad_deserialize_params<C: CryptoProvider>(
);
}
-fn do_bad_deserialize_tampered<
- S: Fn(&mut CapacityLimitedVec<u8, NP_ADV_MAX_SECTION_LEN>),
- A: Fn(&mut Vec<u8>),
->(
- expected_error: IdentityResolutionOrDeserializationError<MicVerificationError>,
- mangle_section: S,
- mangle_adv: A,
+#[derive(Debug, PartialEq)]
+enum DeserializeError {
+ IdentityResolutionOrDeserializationError(
+ IdentityResolutionOrDeserializationError<MicVerificationError>,
+ ),
+ DataElementParseError(DataElementParseError),
+}
+
+fn do_bad_deserialize_tampered(
+ expected_error: DeserializeError,
+ mangle_section: impl Fn(&mut CapacityLimitedVec<u8, NP_ADV_MAX_SECTION_LEN>),
+ mangle_adv: impl Fn(&mut Vec<u8>),
) {
- let metadata_key = [1; 16];
+ let metadata_key = MetadataKey([1; 16]);
let key_seed = [2; 32];
let section_salt: V1Salt<CryptoProviderImpl> = [3; 16].into();
let identity_type = EncryptedIdentityDataElementType::Private;
- let key_seed_hkdf = np_hkdf::NpKeySeedHkdf::new(&key_seed);
let mut adv_builder = AdvBuilder::new(AdvertisementType::Encrypted);
+ let broadcast_cm = SimpleBroadcastCryptoMaterial::<V1>::new(key_seed, metadata_key);
+
let mut section_builder = adv_builder
- .section_builder(MicEncryptedSectionEncoder::new_for_testing(
+ .section_builder(MicEncryptedSectionEncoder::<CryptoProviderImpl>::new(
identity_type,
section_salt,
- &metadata_key,
- &key_seed_hkdf,
+ &broadcast_cm,
))
.unwrap();
@@ -413,22 +437,35 @@ fn do_bad_deserialize_tampered<
panic!("incorrect flavor");
};
- // generate a random key pair since we need _some_ public key
+ // generate a random key pair since we need _some_ public key in our discovery
+ // credential, even if it winds up going unused
let key_pair = np_ed25519::KeyPair::<CryptoProviderImpl>::generate();
- let crypto_material = HkdfCryptoMaterial::new(&key_seed, &metadata_key, key_pair.public());
+ let discovery_credential =
+ SimpleSignedBroadcastCryptoMaterial::new(key_seed, metadata_key, key_pair.private_key())
+ .derive_v1_discovery_credential::<CryptoProviderImpl>();
+
let identity_resolution_material =
- crypto_material.unsigned_identity_resolution_material::<CryptoProviderImpl>();
+ discovery_credential.unsigned_identity_resolution_material::<CryptoProviderImpl>();
let verification_material =
- crypto_material.unsigned_verification_material::<CryptoProviderImpl>();
-
- assert_eq!(
- expected_error,
- contents
- .try_resolve_identity_and_deserialize::<CryptoProviderImpl>(
- &identity_resolution_material,
- &verification_material,
- )
- .unwrap_err()
- );
+ discovery_credential.unsigned_verification_material::<CryptoProviderImpl>();
+
+ match contents.try_resolve_identity_and_deserialize::<CryptoProviderImpl>(
+ &mut deserialization_arena!().into_allocator(),
+ &identity_resolution_material,
+ &verification_material,
+ ) {
+ Ok(section) => {
+ assert_eq!(
+ expected_error,
+ DeserializeError::DataElementParseError(
+ section.collect_data_elements().unwrap_err()
+ )
+ );
+ }
+ Err(e) => assert_eq!(
+ expected_error,
+ DeserializeError::IdentityResolutionOrDeserializationError(e),
+ ),
+ };
}
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 7dc411e..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
@@ -14,18 +14,23 @@
use crate::{
credential::v1::*,
+ deserialization_arena::DeserializationArenaAllocator,
extended::{
deserialize::{
- parse_non_identity_des, DecryptedSection, EncryptedIdentityMetadata, EncryptionInfo,
- SectionContents, SectionMic, VerificationMode,
+ DecryptedSection, EncryptedIdentityMetadata, EncryptionInfo, SectionContents,
+ SectionMic, VerificationMode,
},
section_signature_payload::*,
- to_array_view, METADATA_KEY_LEN, NP_ADV_MAX_SECTION_LEN,
+ METADATA_KEY_LEN, NP_ADV_MAX_SECTION_LEN,
},
- V1Header, NP_SVC_UUID,
+ MetadataKey, V1Header, NP_SVC_UUID,
};
-#[cfg(feature = "devtools")]
+
+#[cfg(any(feature = "devtools", test))]
+extern crate alloc;
+#[cfg(any(feature = "devtools", test))]
use alloc::vec::Vec;
+#[cfg(feature = "devtools")]
use array_view::ArrayView;
use core::fmt::Debug;
use crypto_provider::{
@@ -35,6 +40,8 @@ use crypto_provider::{
};
use np_hkdf::v1_salt::V1Salt;
+use super::ArenaOutOfSpace;
+
#[cfg(test)]
mod mic_decrypt_tests;
#[cfg(test)]
@@ -69,7 +76,7 @@ impl SectionIdentityResolutionContents {
let mut decrypt_buf = self.metadata_key_ciphertext;
let aes_key = &identity_resolution_material.aes_key;
let mut cipher = C::AesCtr128::new(aes_key, NonceAndCounter::from_nonce(self.nonce));
- cipher.decrypt(&mut decrypt_buf[..]);
+ cipher.apply_keystream(&mut decrypt_buf[..]);
let metadata_key_hmac_key: np_hkdf::NpHmacSha256Key<C> =
identity_resolution_material.metadata_key_hmac_key.into();
@@ -77,7 +84,7 @@ impl SectionIdentityResolutionContents {
metadata_key_hmac_key.verify_hmac(&decrypt_buf[..], expected_metadata_key_hmac).ok().map(
move |_| IdentityMatch {
cipher,
- metadata_key_plaintext: decrypt_buf,
+ metadata_key_plaintext: MetadataKey(decrypt_buf),
nonce: self.nonce,
},
)
@@ -88,7 +95,7 @@ impl SectionIdentityResolutionContents {
/// against some particular V1 identity-resolution crypto-materials.
pub(crate) struct IdentityMatch<C: CryptoProvider> {
/// Decrypted metadata key ciphertext
- metadata_key_plaintext: [u8; METADATA_KEY_LEN],
+ metadata_key_plaintext: MetadataKey,
/// The AES-Ctr nonce to be used in section decryption and verification
nonce: AesCtrNonce,
/// The state of the AES-Ctr cipher after successfully decrypting
@@ -98,25 +105,25 @@ pub(crate) struct IdentityMatch<C: CryptoProvider> {
}
/// Maximum length of a section's contents, after the metadata-key.
+#[allow(unused)]
const MAX_SECTION_CONTENTS_LEN: usize = NP_ADV_MAX_SECTION_LEN - METADATA_KEY_LEN;
/// Bare, decrypted contents from an encrypted section,
/// including the decrypted metadata key and the decrypted section ciphertext.
/// At this point, verification of the plaintext contents has not yet been performed.
-pub(crate) struct RawDecryptedSection {
- pub(crate) metadata_key_plaintext: [u8; METADATA_KEY_LEN],
+pub(crate) struct RawDecryptedSection<'adv> {
+ pub(crate) metadata_key_plaintext: MetadataKey,
pub(crate) nonce: AesCtrNonce,
- pub(crate) plaintext_contents: ArrayView<u8, MAX_SECTION_CONTENTS_LEN>,
+ pub(crate) plaintext_contents: &'adv [u8],
}
#[cfg(feature = "devtools")]
-impl RawDecryptedSection {
+impl<'adv> RawDecryptedSection<'adv> {
pub(crate) fn to_raw_bytes(&self) -> ArrayView<u8, NP_ADV_MAX_SECTION_LEN> {
let mut result = Vec::new();
- result.extend_from_slice(&self.metadata_key_plaintext);
- result.extend_from_slice(self.plaintext_contents.as_slice());
- // Won't panic because of the involved lengths
- ArrayView::try_from_slice(&result).unwrap()
+ result.extend_from_slice(&self.metadata_key_plaintext.0);
+ result.extend_from_slice(self.plaintext_contents);
+ ArrayView::try_from_slice(&result).expect("Won't panic because of the involved lengths")
}
}
@@ -173,8 +180,10 @@ impl<'a> EncryptedSectionContents<'a> {
&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 }
}
@@ -183,33 +192,37 @@ impl<'a> EncryptedSectionContents<'a> {
/// and returns the raw bytes of the decrypted plaintext.
pub(crate) fn decrypt_ciphertext<C: CryptoProvider>(
&self,
+ arena: &mut DeserializationArenaAllocator<'a>,
mut identity_match: IdentityMatch<C>,
- ) -> RawDecryptedSection {
+ ) -> Result<RawDecryptedSection<'a>, ArenaOutOfSpace> {
// Fill decrypt_buf with the ciphertext after the metadata key
- let mut decrypt_buf = tinyvec::ArrayVec::<[u8; MAX_SECTION_CONTENTS_LEN]>::new();
- decrypt_buf.extend_from_slice(&self.all_ciphertext[METADATA_KEY_LEN..]);
+ let decrypt_buf =
+ arena.allocate(u8::try_from(self.all_ciphertext.len() - METADATA_KEY_LEN).expect(
+ "all_ciphertext.len() must be in [METADATA_KEY_LEN, NP_ADV_MAX_SECTION_LEN]",
+ ))?;
+ decrypt_buf.copy_from_slice(&self.all_ciphertext[METADATA_KEY_LEN..]);
// Decrypt everything after the metadata key
- identity_match.cipher.decrypt(&mut decrypt_buf);
+ identity_match.cipher.apply_keystream(decrypt_buf);
- let plaintext_contents = to_array_view(decrypt_buf);
-
- RawDecryptedSection {
+ Ok(RawDecryptedSection {
metadata_key_plaintext: identity_match.metadata_key_plaintext,
nonce: identity_match.nonce,
- plaintext_contents,
- }
+ plaintext_contents: decrypt_buf,
+ })
}
+
/// Try decrypting into some raw bytes given some raw identity-resolution material.
#[cfg(feature = "devtools")]
pub(crate) fn try_resolve_identity_and_decrypt<P: CryptoProvider>(
&self,
+ allocator: &mut DeserializationArenaAllocator<'a>,
identity_resolution_material: &SectionIdentityResolutionMaterial,
- ) -> Option<ArrayView<u8, NP_ADV_MAX_SECTION_LEN>> {
+ ) -> Option<Result<ArrayView<u8, NP_ADV_MAX_SECTION_LEN>, ArenaOutOfSpace>> {
let identity_resolution_contents = self.compute_identity_resolution_contents::<P>();
identity_resolution_contents.try_match(identity_resolution_material).map(|identity_match| {
- let decrypted_section = self.decrypt_ciphertext::<P>(identity_match);
- decrypted_section.to_raw_bytes()
+ let decrypted_section = self.decrypt_ciphertext::<P>(allocator, identity_match)?;
+ Ok(decrypted_section.to_raw_bytes())
})
}
}
@@ -225,13 +238,14 @@ impl<'a> SignatureEncryptedSection<'a> {
/// with some paired verification material for the matched identity.
pub(crate) fn try_deserialize<P>(
&self,
+ arena: &mut DeserializationArenaAllocator<'a>,
identity_match: IdentityMatch<P>,
verification_material: &SignedSectionVerificationMaterial,
- ) -> Result<DecryptedSection, DeserializationError<SignatureVerificationError>>
+ ) -> Result<DecryptedSection<'a>, DeserializationError<SignatureVerificationError>>
where
P: CryptoProvider,
{
- let raw_decrypted = self.contents.decrypt_ciphertext(identity_match);
+ let raw_decrypted = self.contents.decrypt_ciphertext(arena, identity_match)?;
let metadata_key = raw_decrypted.metadata_key_plaintext;
let nonce = raw_decrypted.nonce;
let remaining = raw_decrypted.plaintext_contents;
@@ -241,13 +255,8 @@ impl<'a> SignatureEncryptedSection<'a> {
}
// should not panic due to above check
- let (non_identity_des, sig) = remaining
- .as_slice()
- .split_at(remaining.len() - crypto_provider::ed25519::SIGNATURE_LENGTH);
-
- // de offset 2 because of leading encryption info and identity DEs
- let (_, ref_des) = parse_non_identity_des(2)(non_identity_des)
- .map_err(|_| DeserializationError::DeParseError)?;
+ let (non_identity_des, sig) =
+ remaining.split_at(remaining.len() - crypto_provider::ed25519::SIGNATURE_LENGTH);
// All implementations only check for 64 bytes, and this will always result in a 64 byte signature.
let expected_signature =
@@ -279,16 +288,20 @@ impl<'a> SignatureEncryptedSection<'a> {
VerificationMode::Signature,
metadata_key,
salt.into(),
- SectionContents::new(self.contents.section_header, &ref_des),
+ // de offset 2 because of leading encryption info and identity DEs
+ SectionContents::new(self.contents.section_header, non_identity_des, 2),
))
}
+
/// Try decrypting into some raw bytes given some raw signed crypto-material.
#[cfg(feature = "devtools")]
pub(crate) fn try_resolve_identity_and_decrypt<P: CryptoProvider>(
&self,
+ allocator: &mut DeserializationArenaAllocator<'a>,
identity_resolution_material: &SignedSectionIdentityResolutionMaterial,
- ) -> Option<ArrayView<u8, NP_ADV_MAX_SECTION_LEN>> {
+ ) -> Option<Result<ArrayView<u8, NP_ADV_MAX_SECTION_LEN>, ArenaOutOfSpace>> {
self.contents.try_resolve_identity_and_decrypt::<P>(
+ allocator,
identity_resolution_material.as_raw_resolution_material(),
)
}
@@ -297,6 +310,7 @@ impl<'a> SignatureEncryptedSection<'a> {
#[cfg(test)]
pub(crate) fn try_resolve_identity_and_deserialize<P: CryptoProvider>(
&self,
+ allocator: &mut DeserializationArenaAllocator<'a>,
identity_resolution_material: &SignedSectionIdentityResolutionMaterial,
verification_material: &SignedSectionVerificationMaterial,
) -> Result<
@@ -308,9 +322,9 @@ impl<'a> SignatureEncryptedSection<'a> {
match section_identity_resolution_contents
.try_match::<P>(identity_resolution_material.as_raw_resolution_material())
{
- Some(identity_match) => {
- self.try_deserialize(identity_match, verification_material).map_err(|e| e.into())
- }
+ Some(identity_match) => self
+ .try_deserialize(allocator, identity_match, verification_material)
+ .map_err(|e| e.into()),
None => Err(IdentityResolutionOrDeserializationError::IdentityMatchingError),
}
}
@@ -353,10 +367,16 @@ impl<V: VerificationError> From<V> for IdentityResolutionOrDeserializationError<
/// detailed.
#[derive(Debug, PartialEq, Eq)]
pub(crate) enum DeserializationError<V: VerificationError> {
- /// Parsing of decrypted DEs failed
- DeParseError,
/// Verification failed
VerificationError(V),
+ /// The given arena ran out of space
+ ArenaOutOfSpace,
+}
+
+impl<V: VerificationError> From<ArenaOutOfSpace> for DeserializationError<V> {
+ fn from(_: ArenaOutOfSpace) -> Self {
+ Self::ArenaOutOfSpace
+ }
}
impl<V: VerificationError> From<V> for DeserializationError<V> {
@@ -396,13 +416,14 @@ impl<'a> MicEncryptedSection<'a> {
/// Returns an error if the credential is incorrect or if the section data is malformed.
pub(crate) fn try_deserialize<P>(
&self,
+ allocator: &mut DeserializationArenaAllocator<'a>,
identity_match: IdentityMatch<P>,
verification_material: &UnsignedSectionVerificationMaterial,
- ) -> Result<DecryptedSection, DeserializationError<MicVerificationError>>
+ ) -> Result<DecryptedSection<'a>, DeserializationError<MicVerificationError>>
where
P: CryptoProvider,
{
- let raw_decrypted = self.contents.decrypt_ciphertext(identity_match);
+ let raw_decrypted = self.contents.decrypt_ciphertext(allocator, identity_match)?;
let metadata_key = raw_decrypted.metadata_key_plaintext;
let nonce = raw_decrypted.nonce;
let remaining_des = raw_decrypted.plaintext_contents;
@@ -421,10 +442,6 @@ impl<'a> MicEncryptedSection<'a> {
.verify_truncated_left(&self.mic.mic)
.map_err(|_e| MicVerificationError::MicMismatch)?;
- // offset 2 for encryption info and identity DEs
- let (_, ref_des) = parse_non_identity_des(2)(remaining_des.as_slice())
- .map_err(|_| DeserializationError::DeParseError)?;
-
let salt = self.contents.salt::<P>();
Ok(DecryptedSection::new(
@@ -432,7 +449,8 @@ impl<'a> MicEncryptedSection<'a> {
VerificationMode::Mic,
metadata_key,
salt.into(),
- SectionContents::new(self.contents.section_header, &ref_des),
+ // offset 2 for encryption info and identity DEs
+ SectionContents::new(self.contents.section_header, remaining_des, 2),
))
}
@@ -440,9 +458,11 @@ impl<'a> MicEncryptedSection<'a> {
#[cfg(feature = "devtools")]
pub(crate) fn try_resolve_identity_and_decrypt<P: CryptoProvider>(
&self,
+ allocator: &mut DeserializationArenaAllocator<'a>,
identity_resolution_material: &UnsignedSectionIdentityResolutionMaterial,
- ) -> Option<ArrayView<u8, NP_ADV_MAX_SECTION_LEN>> {
+ ) -> Option<Result<ArrayView<u8, NP_ADV_MAX_SECTION_LEN>, ArenaOutOfSpace>> {
self.contents.try_resolve_identity_and_decrypt::<P>(
+ allocator,
identity_resolution_material.as_raw_resolution_material(),
)
}
@@ -451,6 +471,7 @@ impl<'a> MicEncryptedSection<'a> {
#[cfg(test)]
pub(crate) fn try_resolve_identity_and_deserialize<P: CryptoProvider>(
&self,
+ allocator: &mut DeserializationArenaAllocator<'a>,
identity_resolution_material: &UnsignedSectionIdentityResolutionMaterial,
verification_material: &UnsignedSectionVerificationMaterial,
) -> Result<DecryptedSection, IdentityResolutionOrDeserializationError<MicVerificationError>>
@@ -460,9 +481,9 @@ impl<'a> MicEncryptedSection<'a> {
match section_identity_resolution_contents
.try_match::<P>(identity_resolution_material.as_raw_resolution_material())
{
- Some(identity_match) => {
- self.try_deserialize(identity_match, verification_material).map_err(|e| e.into())
- }
+ Some(identity_match) => self
+ .try_deserialize(allocator, identity_match, verification_material)
+ .map_err(|e| e.into()),
None => Err(IdentityResolutionOrDeserializationError::IdentityMatchingError),
}
}
diff --git a/nearby/presence/np_adv/src/extended/deserialize/encrypted_section/signature_decrypt_tests.rs b/nearby/presence/np_adv/src/extended/deserialize/encrypted_section/signature_decrypt_tests.rs
index f8d622c..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,10 +12,15 @@
// 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;
use crate::extended::data_elements::TxPowerDataElement;
-use crate::extended::deserialize::{DecryptedSection, EncryptionInfo, RawV1Salt};
+use crate::extended::deserialize::{
+ DataElementParseError, DecryptedSection, EncryptionInfo, RawV1Salt, SectionContents,
+};
use crate::extended::serialize::AdvertisementType;
use crate::shared_data::TxPower;
use crate::{
@@ -24,14 +29,12 @@ use crate::{
extended::{
deserialize::{
encrypted_section::{
- DeserializationError, EncryptedSectionContents,
- IdentityResolutionOrDeserializationError, SignatureEncryptedSection,
- SignatureVerificationError,
+ EncryptedSectionContents, IdentityResolutionOrDeserializationError,
+ SignatureEncryptedSection, SignatureVerificationError,
},
parse_sections,
test_stubs::IntermediateSectionExt,
- CiphertextSection, EncryptedIdentityMetadata, OffsetDataElement, SectionContents,
- VerificationMode,
+ CiphertextSection, DataElement, EncryptedIdentityMetadata, VerificationMode,
},
section_signature_payload::*,
serialize::{
@@ -40,9 +43,8 @@ use crate::{
},
NP_ADV_MAX_SECTION_LEN,
},
- parse_adv_header, AdvHeader, Section,
+ parse_adv_header, AdvHeader, MetadataKey, Section,
};
-use array_view::ArrayView;
use crypto_provider::{aes::ctr::AesCtrNonce, CryptoProvider};
use crypto_provider_default::CryptoProviderImpl;
use np_hkdf::v1_salt;
@@ -54,7 +56,7 @@ type KeyPair = np_ed25519::KeyPair<CryptoProviderImpl>;
#[test]
fn deserialize_signature_encrypted_correct_keys() {
- let metadata_key = [1; 16];
+ let metadata_key = MetadataKey([1; 16]);
let key_seed = [2; 32];
let raw_salt = RawV1Salt([3; 16]);
let section_salt = V1Salt::<CryptoProviderImpl>::from(raw_salt);
@@ -64,13 +66,14 @@ fn deserialize_signature_encrypted_correct_keys() {
let mut adv_builder = AdvBuilder::new(AdvertisementType::Encrypted);
+ let broadcast_cm =
+ SimpleSignedBroadcastCryptoMaterial::new(key_seed, metadata_key, key_pair.private_key());
+
let mut section_builder = adv_builder
- .section_builder(SignedEncryptedSectionEncoder::new(
+ .section_builder(SignedEncryptedSectionEncoder::<CryptoProviderImpl>::new(
identity_type,
V1Salt::from(*section_salt.as_array_ref()),
- &metadata_key,
- &key_pair,
- &key_seed_hkdf,
+ &broadcast_cm,
))
.unwrap();
@@ -126,11 +129,11 @@ fn deserialize_signature_encrypted_correct_keys() {
// plaintext is correct
{
- let crypto_material = MinimumFootprintV1CryptoMaterial::new::<CryptoProviderImpl>(
+ let crypto_material = V1DiscoveryCredential::new(
key_seed,
- key_seed_hkdf.extended_unsigned_metadata_key_hmac_key().calculate_hmac(&metadata_key),
- key_seed_hkdf.extended_signed_metadata_key_hmac_key().calculate_hmac(&metadata_key),
- key_pair.public(),
+ 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().to_bytes(),
);
let signed_identity_resolution_material =
crypto_material.signed_identity_resolution_material::<CryptoProviderImpl>();
@@ -138,16 +141,18 @@ fn deserialize_signature_encrypted_correct_keys() {
contents.contents.compute_identity_resolution_contents::<CryptoProviderImpl>();
let identity_match = identity_resolution_contents
.try_match::<CryptoProviderImpl>(
- signed_identity_resolution_material.as_raw_resolution_material(),
+ &signed_identity_resolution_material.into_raw_resolution_material(),
)
.unwrap();
- let decrypted = contents.contents.decrypt_ciphertext(identity_match);
+ let arena = deserialization_arena!();
+ let mut allocator = arena.into_allocator();
+ let decrypted =
+ contents.contents.decrypt_ciphertext(&mut allocator, identity_match).unwrap();
let mut expected = Vec::new();
- // battery de
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();
@@ -169,24 +174,27 @@ fn deserialize_signature_encrypted_correct_keys() {
expected.extend_from_slice(&sig_payload.sign(&key_pair).to_bytes());
assert_eq!(metadata_key, decrypted.metadata_key_plaintext);
assert_eq!(nonce, decrypted.nonce);
- assert_eq!(&expected, decrypted.plaintext_contents.as_slice());
+ assert_eq!(&expected, decrypted.plaintext_contents);
}
// deserialization to Section works
{
- let crypto_material = MinimumFootprintV1CryptoMaterial::new::<CryptoProviderImpl>(
+ let crypto_material = V1DiscoveryCredential::new(
key_seed,
- key_seed_hkdf.extended_unsigned_metadata_key_hmac_key().calculate_hmac(&metadata_key),
- key_seed_hkdf.extended_signed_metadata_key_hmac_key().calculate_hmac(&metadata_key),
- key_pair.public(),
+ 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().to_bytes(),
);
let signed_identity_resolution_material =
crypto_material.signed_identity_resolution_material::<CryptoProviderImpl>();
let signed_verification_material =
crypto_material.signed_verification_material::<CryptoProviderImpl>();
+ let arena = deserialization_arena!();
+ let mut allocator = arena.into_allocator();
let section = contents
.try_resolve_identity_and_deserialize::<CryptoProviderImpl>(
+ &mut allocator,
&signed_identity_resolution_material,
&signed_verification_material,
)
@@ -200,27 +208,23 @@ fn deserialize_signature_encrypted_correct_keys() {
raw_salt,
SectionContents {
section_header: 19 + 2 + 16 + 1 + 1 + 64,
- // battery DE
- de_data: ArrayView::try_from_slice(&[7]).unwrap(),
- data_elements: vec![OffsetDataElement {
- offset: 2.into(),
- de_type: 0x05_u8.into(),
- start_of_contents: 0,
- contents_len: 1,
- }],
+ data_element_start_offset: 2,
+ de_region_excl_identity:
+ &[txpower_de.de_header().serialize().as_slice(), &[7],].concat(),
},
),
section
);
+ let data_elements = section.collect_data_elements().unwrap();
+ assert_eq!(
+ data_elements,
+ &[DataElement { offset: 2.into(), de_type: 0x05_u8.into(), contents: &[7] }]
+ );
assert_eq!(
- vec![(
- v1_salt::DataElementOffset::from(2_usize),
- TxPowerDataElement::DE_TYPE,
- vec![7u8]
- )],
- section
- .data_elements()
+ vec![(v1_salt::DataElementOffset::from(2_u8), TxPowerDataElement::DE_TYPE, vec![7u8])],
+ data_elements
+ .into_iter()
.map(|de| (de.offset(), de.de_type(), de.contents().to_vec()))
.collect::<Vec<_>>()
);
@@ -279,7 +283,9 @@ fn deserialize_signature_encrypted_incorrect_pub_key_error() {
fn deserialize_signature_encrypted_incorrect_salt_error() {
// bad salt -> bad iv -> bad metadata key plaintext
do_bad_deserialize_tampered(
- IdentityResolutionOrDeserializationError::IdentityMatchingError,
+ DeserializeError::IdentityResolutionOrDeserializationError(
+ IdentityResolutionOrDeserializationError::IdentityMatchingError,
+ ),
None,
|_| {},
|adv_mut| adv_mut[5..21].copy_from_slice(&[0xFF; 16]),
@@ -289,7 +295,9 @@ fn deserialize_signature_encrypted_incorrect_salt_error() {
#[test]
fn deserialize_signature_encrypted_tampered_signature_error() {
do_bad_deserialize_tampered(
- SignatureVerificationError::SignatureMismatch.into(),
+ DeserializeError::IdentityResolutionOrDeserializationError(
+ SignatureVerificationError::SignatureMismatch.into(),
+ ),
None,
|_| {},
// flip a bit in the middle of the signature
@@ -303,7 +311,9 @@ fn deserialize_signature_encrypted_tampered_signature_error() {
#[test]
fn deserialize_signature_encrypted_tampered_ciphertext_error() {
do_bad_deserialize_tampered(
- SignatureVerificationError::SignatureMismatch.into(),
+ DeserializeError::IdentityResolutionOrDeserializationError(
+ SignatureVerificationError::SignatureMismatch.into(),
+ ),
None,
|_| {},
// flip a bit outside of the signature
@@ -318,7 +328,9 @@ fn deserialize_signature_encrypted_tampered_ciphertext_error() {
fn deserialize_signature_encrypted_missing_signature_de_error() {
let section_len = 19 + 2 + 16 + 1 + 1;
do_bad_deserialize_tampered(
- SignatureVerificationError::SignatureMissing.into(),
+ DeserializeError::IdentityResolutionOrDeserializationError(
+ SignatureVerificationError::SignatureMissing.into(),
+ ),
Some(section_len),
|_| {},
|adv_mut| {
@@ -333,7 +345,7 @@ fn deserialize_signature_encrypted_missing_signature_de_error() {
#[test]
fn deserialize_signature_encrypted_des_wont_parse() {
do_bad_deserialize_tampered(
- DeserializationError::DeParseError.into(),
+ DeserializeError::DataElementParseError(DataElementParseError::UnexpectedDataAfterEnd),
Some(19 + 2 + 16 + 1 + 1 + 64 + 1),
// add an impossible DE
|section| section.try_push(0xFF).unwrap(),
@@ -350,7 +362,7 @@ fn do_bad_deserialize_params<C: CryptoProvider>(
expected_metadata_key_hmac: Option<[u8; 32]>,
pub_key: Option<np_ed25519::PublicKey<C>>,
) {
- let metadata_key = [1; 16];
+ let metadata_key = MetadataKey([1; 16]);
let key_seed = [2; 32];
let section_salt: v1_salt::V1Salt<C> = [3; 16].into();
let identity_type = EncryptedIdentityDataElementType::Private;
@@ -359,13 +371,14 @@ fn do_bad_deserialize_params<C: CryptoProvider>(
let mut adv_builder = AdvBuilder::new(AdvertisementType::Encrypted);
+ let broadcast_cm =
+ SimpleSignedBroadcastCryptoMaterial::new(key_seed, metadata_key, key_pair.private_key());
+
let mut section_builder = adv_builder
.section_builder(SignedEncryptedSectionEncoder::new(
identity_type,
section_salt,
- &metadata_key,
- &key_pair,
- &key_seed_hkdf,
+ &broadcast_cm,
))
.unwrap();
@@ -402,7 +415,9 @@ fn do_bad_deserialize_params<C: CryptoProvider>(
.unwrap_or_else(|| key_seed_hkdf.extended_signed_metadata_key_hmac_key())
.as_bytes(),
expected_metadata_key_hmac: expected_metadata_key_hmac.unwrap_or_else(|| {
- key_seed_hkdf.extended_signed_metadata_key_hmac_key().calculate_hmac(&metadata_key)
+ key_seed_hkdf
+ .extended_signed_metadata_key_hmac_key()
+ .calculate_hmac(&metadata_key.0)
}),
});
@@ -414,6 +429,7 @@ fn do_bad_deserialize_params<C: CryptoProvider>(
error,
contents
.try_resolve_identity_and_deserialize::<C>(
+ &mut deserialization_arena!().into_allocator(),
&signed_identity_resolution_material,
&signed_verification_material,
)
@@ -421,34 +437,40 @@ fn do_bad_deserialize_params<C: CryptoProvider>(
);
}
+#[derive(Debug, PartialEq)]
+enum DeserializeError {
+ IdentityResolutionOrDeserializationError(
+ IdentityResolutionOrDeserializationError<SignatureVerificationError>,
+ ),
+ DataElementParseError(DataElementParseError),
+}
+
/// Run a test that mangles the advertisement contents before attempting to deserialize.
///
/// Since the advertisement is ciphertext, only changes outside
-fn do_bad_deserialize_tampered<
- A: Fn(&mut Vec<u8>),
- S: Fn(&mut CapacityLimitedVec<u8, NP_ADV_MAX_SECTION_LEN>),
->(
- expected_error: IdentityResolutionOrDeserializationError<SignatureVerificationError>,
+fn do_bad_deserialize_tampered(
+ expected_error: DeserializeError,
expected_section_len: Option<u8>,
- mangle_section: S,
- mangle_adv_contents: A,
+ mangle_section: impl Fn(&mut CapacityLimitedVec<u8, NP_ADV_MAX_SECTION_LEN>),
+ mangle_adv_contents: impl Fn(&mut Vec<u8>),
) {
- let metadata_key = [1; 16];
+ let metadata_key = MetadataKey([1; 16]);
let key_seed = [2; 32];
let section_salt: v1_salt::V1Salt<CryptoProviderImpl> = [3; 16].into();
let identity_type = EncryptedIdentityDataElementType::Private;
- let key_seed_hkdf = np_hkdf::NpKeySeedHkdf::new(&key_seed);
+ let key_seed_hkdf = np_hkdf::NpKeySeedHkdf::<CryptoProviderImpl>::new(&key_seed);
let key_pair = KeyPair::generate();
let mut adv_builder = AdvBuilder::new(AdvertisementType::Encrypted);
+ let broadcast_cm =
+ SimpleSignedBroadcastCryptoMaterial::new(key_seed, metadata_key, key_pair.private_key());
+
let mut section_builder = adv_builder
.section_builder(SignedEncryptedSectionEncoder::new(
identity_type,
section_salt,
- &metadata_key,
- &key_pair,
- &key_seed_hkdf,
+ &broadcast_cm,
))
.unwrap();
@@ -504,24 +526,33 @@ fn do_bad_deserialize_tampered<
contents
);
- let crypto_material = MinimumFootprintV1CryptoMaterial::new::<CryptoProviderImpl>(
+ let crypto_material = V1DiscoveryCredential::new(
key_seed,
- key_seed_hkdf.extended_unsigned_metadata_key_hmac_key().calculate_hmac(&metadata_key),
- key_seed_hkdf.extended_signed_metadata_key_hmac_key().calculate_hmac(&metadata_key),
- key_pair.public(),
+ 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().to_bytes(),
);
let identity_resolution_material =
crypto_material.signed_identity_resolution_material::<CryptoProviderImpl>();
let verification_material =
crypto_material.signed_verification_material::<CryptoProviderImpl>();
- assert_eq!(
- expected_error,
- contents
- .try_resolve_identity_and_deserialize::<CryptoProviderImpl>(
- &identity_resolution_material,
- &verification_material,
- )
- .unwrap_err()
- );
+ match contents.try_resolve_identity_and_deserialize::<CryptoProviderImpl>(
+ &mut deserialization_arena!().into_allocator(),
+ &identity_resolution_material,
+ &verification_material,
+ ) {
+ Ok(section) => {
+ assert_eq!(
+ expected_error,
+ DeserializeError::DataElementParseError(
+ section.collect_data_elements().unwrap_err()
+ ),
+ );
+ }
+ Err(e) => assert_eq!(
+ expected_error,
+ DeserializeError::IdentityResolutionOrDeserializationError(e),
+ ),
+ };
}
diff --git a/nearby/presence/np_adv/src/extended/deserialize/mod.rs b/nearby/presence/np_adv/src/extended/deserialize/mod.rs
index beb205f..6ec7881 100644
--- a/nearby/presence/np_adv/src/extended/deserialize/mod.rs
+++ b/nearby/presence/np_adv/src/extended/deserialize/mod.rs
@@ -14,12 +14,14 @@
//! Deserialization for V1 advertisement contents
+#[cfg(any(test, feature = "alloc"))]
extern crate alloc;
+#[cfg(any(test, feature = "alloc", feature = "devtools"))]
use alloc::vec::Vec;
use core::array::TryFromSliceError;
-use core::slice;
+use core::fmt::Debug;
use nom::{branch, bytes, combinator, error, multi, number, sequence};
use strum::IntoEnumIterator as _;
@@ -27,8 +29,13 @@ use array_view::ArrayView;
use crypto_provider::CryptoProvider;
use np_hkdf::v1_salt::{self, V1Salt};
+use crate::array_vec::ArrayVecOption;
#[cfg(any(feature = "devtools", test))]
-use crate::credential::v1::V1CryptoMaterial;
+use crate::credential::v1::V1DiscoveryCryptoMaterial;
+use crate::credential::v1::V1;
+use crate::deserialization_arena::ArenaOutOfSpace;
+#[cfg(any(feature = "devtools", test))]
+use crate::deserialization_arena::DeserializationArenaAllocator;
#[cfg(test)]
use crate::extended::deserialize::encrypted_section::IdentityResolutionOrDeserializationError;
use crate::extended::{NP_V1_ADV_MAX_ENCRYPTED_SECTION_COUNT, NP_V1_ADV_MAX_PUBLIC_SECTION_COUNT};
@@ -41,9 +48,9 @@ use crate::{
DeserializationError, EncryptedSectionContents, MicEncryptedSection,
MicVerificationError, SignatureEncryptedSection, SignatureVerificationError,
},
- to_array_view, DeLength, ENCRYPTION_INFO_DE_TYPE, METADATA_KEY_LEN, NP_ADV_MAX_SECTION_LEN,
+ DeLength, ENCRYPTION_INFO_DE_TYPE, METADATA_KEY_LEN, NP_ADV_MAX_SECTION_LEN,
},
- PlaintextIdentityMode, V1Header,
+ HasIdentityMatch, MetadataKey, PlaintextIdentityMode, V1Header,
};
pub(crate) mod encrypted_section;
@@ -62,19 +69,32 @@ mod test_stubs;
pub(crate) fn parse_sections(
adv_header: V1Header,
adv_body: &[u8],
-) -> Result<Vec<IntermediateSection>, nom::Err<error::Error<&[u8]>>> {
+) -> Result<
+ ArrayVecOption<IntermediateSection, NP_V1_ADV_MAX_ENCRYPTED_SECTION_COUNT>,
+ nom::Err<error::Error<&[u8]>>,
+> {
combinator::all_consuming(branch::alt((
// Public advertisement
- multi::many_m_n(
+ multi::fold_many_m_n(
1,
NP_V1_ADV_MAX_PUBLIC_SECTION_COUNT,
IntermediateSection::parser_public_section(),
+ ArrayVecOption::default,
+ |mut acc, item| {
+ acc.push(item);
+ acc
+ },
),
// Encrypted advertisement
- multi::many_m_n(
+ multi::fold_many_m_n(
1,
NP_V1_ADV_MAX_ENCRYPTED_SECTION_COUNT,
IntermediateSection::parser_encrypted_with_header(adv_header),
+ ArrayVecOption::default,
+ |mut acc, item| {
+ acc.push(item);
+ acc
+ },
),
)))(adv_body)
.map(|(_rem, sections)| sections)
@@ -82,17 +102,16 @@ pub(crate) fn parse_sections(
/// A partially processed section that hasn't been decrypted (if applicable) yet.
#[derive(PartialEq, Eq, Debug)]
-// sections are large because they have a stack allocated buffer for the whole section
-#[allow(clippy::large_enum_variant)]
pub(crate) enum IntermediateSection<'a> {
/// A section that was not encrypted, e.g. a public identity or no-identity section.
- Plaintext(PlaintextSection),
+ Plaintext(PlaintextSection<'a>),
/// A section whose contents were encrypted, e.g. a private identity section.
Ciphertext(CiphertextSection<'a>),
}
impl<'a> IntermediateSection<'a> {
- fn parser_public_section() -> impl Fn(&'a [u8]) -> nom::IResult<&[u8], IntermediateSection> {
+ fn parser_public_section(
+ ) -> impl Fn(&'a [u8]) -> nom::IResult<&'a [u8], IntermediateSection<'a>> {
move |adv_body| {
let (remaining, section_contents_len) =
combinator::verify(number::complete::u8, |sec_len| {
@@ -103,18 +122,23 @@ impl<'a> IntermediateSection<'a> {
// - Public Identity DE, all other DEs
let parse_public_identity = combinator::map(
// 1 starting offset because of public identity before it
- sequence::tuple((PublicIdentity::parse, parse_non_identity_des(1))),
+ sequence::tuple((PublicIdentity::parse, nom::combinator::rest)),
// move closure to copy section_len into scope
- move |(_identity, des)| {
+ move |(_identity, rest)| {
IntermediateSection::Plaintext(PlaintextSection::new(
PlaintextIdentityMode::Public,
- SectionContents::new(/* section_header */ section_contents_len, &des),
+ SectionContents::new(
+ /* section_header */ section_contents_len,
+ rest,
+ 1,
+ ),
))
},
);
combinator::map_parser(
bytes::complete::take(section_contents_len),
- // guarantee we consume all of the section (not all of the adv body)
+ // Guarantee we consume all of the section (not all of the adv body)
+ // Note: `all_consuming` should never fail, since we used the `rest` parser above.
combinator::all_consuming(parse_public_identity),
)(remaining)
}
@@ -226,61 +250,59 @@ impl<'a> IntermediateSection<'a> {
}
#[derive(PartialEq, Eq, Debug)]
-struct SectionContents {
+struct SectionContents<'adv> {
section_header: u8,
- de_data: ArrayView<u8, NP_ADV_MAX_SECTION_LEN>,
- data_elements: Vec<OffsetDataElement>,
+ de_region_excl_identity: &'adv [u8],
+ data_element_start_offset: u8,
}
-impl SectionContents {
- fn new(section_header: u8, data_elements: &[RefDataElement]) -> Self {
- let (data_elements, de_data) = convert_data_elements(data_elements);
-
- Self { section_header, de_data, data_elements }
- }
-
- fn data_elements(&'_ self) -> DataElements<'_> {
- DataElements { de_iter: self.data_elements.iter(), de_data: &self.de_data }
+impl<'adv> SectionContents<'adv> {
+ fn new(
+ section_header: u8,
+ de_region_excl_identity: &'adv [u8],
+ data_element_start_offset: u8,
+ ) -> Self {
+ Self { section_header, de_region_excl_identity, data_element_start_offset }
}
-}
-
-/// An iterator over data elements in a V1 section
-// A concrete type to make it easy to refer to in return types where opaque types aren't available.
-pub struct DataElements<'a> {
- de_iter: slice::Iter<'a, OffsetDataElement>,
- de_data: &'a ArrayView<u8, NP_ADV_MAX_SECTION_LEN>,
-}
-
-impl<'a> Iterator for DataElements<'a> {
- type Item = DataElement<'a>;
- fn next(&mut self) -> Option<Self::Item> {
- self.de_iter.next().map(|de| DataElement { de_data: self.de_data, de })
+ fn iter_data_elements(&self) -> DataElementParsingIterator<'adv> {
+ DataElementParsingIterator {
+ input: self.de_region_excl_identity,
+ offset: self.data_element_start_offset,
+ }
}
}
/// A section deserialized from a V1 advertisement.
-pub trait Section {
+pub trait Section<'adv, E: Debug> {
/// The iterator type used to iterate over data elements
- type Iterator<'a>: Iterator<Item = DataElement<'a>>
- where
- Self: 'a;
+ type Iterator: Iterator<Item = Result<DataElement<'adv>, E>>;
/// Iterator over the data elements in a section, except for any DEs related to resolving the
/// identity or otherwise validating the payload (e.g. MIC, Signature, any identity DEs like
/// Private Identity).
- fn data_elements(&'_ self) -> Self::Iterator<'_>;
+ fn iter_data_elements(&self) -> Self::Iterator;
+
+ /// Collects the data elements into a vector, eagerly catching and resolving any errors during
+ /// parsing.
+ #[cfg(any(test, feature = "alloc"))]
+ fn collect_data_elements(&self) -> Result<Vec<DataElement<'adv>>, E>
+ where
+ Self: Sized,
+ {
+ self.iter_data_elements().collect()
+ }
}
/// A plaintext section deserialized from a V1 advertisement.
#[derive(PartialEq, Eq, Debug)]
-pub struct PlaintextSection {
+pub struct PlaintextSection<'adv> {
identity: PlaintextIdentityMode,
- contents: SectionContents,
+ contents: SectionContents<'adv>,
}
-impl PlaintextSection {
- fn new(identity: PlaintextIdentityMode, contents: SectionContents) -> Self {
+impl<'adv> PlaintextSection<'adv> {
+ fn new(identity: PlaintextIdentityMode, contents: SectionContents<'adv>) -> Self {
Self { identity, contents }
}
@@ -293,11 +315,11 @@ impl PlaintextSection {
}
}
-impl Section for PlaintextSection {
- type Iterator<'a> = DataElements<'a> where Self: 'a;
+impl<'adv> Section<'adv, DataElementParseError> for PlaintextSection<'adv> {
+ type Iterator = DataElementParsingIterator<'adv>;
- fn data_elements(&'_ self) -> Self::Iterator<'_> {
- self.contents.data_elements()
+ fn iter_data_elements(&self) -> Self::Iterator {
+ self.contents.iter_data_elements()
}
}
@@ -327,21 +349,21 @@ impl<C: CryptoProvider> From<V1Salt<C>> for RawV1Salt {
/// Fully-parsed and verified decrypted contents from an encrypted section.
#[derive(Debug, PartialEq, Eq)]
-pub struct DecryptedSection {
+pub struct DecryptedSection<'adv> {
identity_type: EncryptedIdentityDataElementType,
verification_mode: VerificationMode,
- metadata_key: [u8; METADATA_KEY_LEN],
+ metadata_key: MetadataKey,
salt: RawV1Salt,
- contents: SectionContents,
+ contents: SectionContents<'adv>,
}
-impl DecryptedSection {
+impl<'adv> DecryptedSection<'adv> {
fn new(
identity_type: EncryptedIdentityDataElementType,
verification_mode: VerificationMode,
- metadata_key: [u8; 16],
+ metadata_key: MetadataKey,
salt: RawV1Salt,
- contents: SectionContents,
+ contents: SectionContents<'adv>,
) -> Self {
Self { identity_type, verification_mode, metadata_key, salt, contents }
}
@@ -356,22 +378,24 @@ impl DecryptedSection {
self.verification_mode
}
- /// The decrypted metadata key from the identity DE.
- pub fn metadata_key(&self) -> &[u8; 16] {
- &self.metadata_key
- }
-
/// The salt used for decryption of this advertisement.
pub fn salt(&self) -> RawV1Salt {
self.salt
}
}
-impl Section for DecryptedSection {
- type Iterator<'a> = DataElements<'a> where Self: 'a;
+impl<'adv> HasIdentityMatch for DecryptedSection<'adv> {
+ type Version = V1;
+ fn metadata_key(&self) -> MetadataKey {
+ self.metadata_key
+ }
+}
+
+impl<'adv> Section<'adv, DataElementParseError> for DecryptedSection<'adv> {
+ type Iterator = DataElementParsingIterator<'adv>;
- fn data_elements(&'_ self) -> Self::Iterator<'_> {
- self.contents.data_elements()
+ fn iter_data_elements(&self) -> Self::Iterator {
+ self.contents.iter_data_elements()
}
}
@@ -386,14 +410,14 @@ impl<'a> CiphertextSection<'a> {
/// and if successful, tries to decrypt this section.
#[cfg(test)]
pub(crate) fn try_resolve_identity_and_deserialize<C, P>(
- &self,
+ &'a self,
+ allocator: &mut DeserializationArenaAllocator<'a>,
crypto_material: &C,
- ) -> Result<DecryptedSection, SectionDeserializeError>
+ ) -> Result<DecryptedSection<'a>, SectionDeserializeError>
where
- C: V1CryptoMaterial,
+ C: V1DiscoveryCryptoMaterial,
P: CryptoProvider,
{
- use core::borrow::Borrow;
match self {
CiphertextSection::SignatureEncryptedIdentity(contents) => {
let identity_resolution_material =
@@ -402,7 +426,8 @@ impl<'a> CiphertextSection<'a> {
contents
.try_resolve_identity_and_deserialize::<P>(
- identity_resolution_material.borrow(),
+ allocator,
+ &identity_resolution_material,
&verification_material,
)
.map_err(|e| e.into())
@@ -414,7 +439,8 @@ impl<'a> CiphertextSection<'a> {
contents
.try_resolve_identity_and_deserialize::<P>(
- identity_resolution_material.borrow(),
+ allocator,
+ &identity_resolution_material,
&verification_material,
)
.map_err(|e| e.into())
@@ -424,21 +450,24 @@ impl<'a> CiphertextSection<'a> {
/// Try decrypting into some raw bytes given some raw unsigned crypto-material.
#[cfg(feature = "devtools")]
- pub(crate) fn try_resolve_identity_and_decrypt<C: V1CryptoMaterial, P: CryptoProvider>(
+ pub(crate) fn try_resolve_identity_and_decrypt<
+ C: V1DiscoveryCryptoMaterial,
+ P: CryptoProvider,
+ >(
&self,
+ allocator: &mut DeserializationArenaAllocator<'a>,
crypto_material: &C,
- ) -> Option<ArrayView<u8, NP_ADV_MAX_SECTION_LEN>> {
- use core::borrow::Borrow;
+ ) -> Option<Result<ArrayView<u8, NP_ADV_MAX_SECTION_LEN>, ArenaOutOfSpace>> {
match self {
CiphertextSection::SignatureEncryptedIdentity(x) => {
let identity_resolution_material =
crypto_material.signed_identity_resolution_material::<P>();
- x.try_resolve_identity_and_decrypt::<P>(identity_resolution_material.borrow())
+ x.try_resolve_identity_and_decrypt::<P>(allocator, &identity_resolution_material)
}
CiphertextSection::MicEncryptedIdentity(x) => {
let identity_resolution_material =
crypto_material.unsigned_identity_resolution_material::<P>();
- x.try_resolve_identity_and_decrypt::<P>(identity_resolution_material.borrow())
+ x.try_resolve_identity_and_decrypt::<P>(allocator, &identity_resolution_material)
}
}
}
@@ -459,6 +488,8 @@ pub enum SectionDeserializeError {
IncorrectCredential,
/// Section data is malformed
ParseError,
+ /// The given arena is not large enough to hold the decrypted contents
+ ArenaOutOfSpace,
}
#[cfg(test)]
@@ -492,10 +523,10 @@ impl From<IdentityResolutionOrDeserializationError<SignatureVerificationError>>
impl From<DeserializationError<MicVerificationError>> for SectionDeserializeError {
fn from(mic_deserialization_error: DeserializationError<MicVerificationError>) -> Self {
match mic_deserialization_error {
- DeserializationError::DeParseError => Self::ParseError,
DeserializationError::VerificationError(MicVerificationError::MicMismatch) => {
Self::IncorrectCredential
}
+ DeserializationError::ArenaOutOfSpace => Self::ArenaOutOfSpace,
}
}
}
@@ -505,43 +536,78 @@ impl From<DeserializationError<SignatureVerificationError>> for SectionDeseriali
signature_deserialization_error: DeserializationError<SignatureVerificationError>,
) -> Self {
match signature_deserialization_error {
- DeserializationError::DeParseError
- | DeserializationError::VerificationError(
+ DeserializationError::VerificationError(
SignatureVerificationError::SignatureMissing,
) => Self::ParseError,
DeserializationError::VerificationError(
SignatureVerificationError::SignatureMismatch,
) => Self::IncorrectCredential,
+ DeserializationError::ArenaOutOfSpace => Self::ArenaOutOfSpace,
}
}
}
-/// Returns a parser function that parses data elements whose type is not one of the known identity
-/// DE types, and whose offsets start from the provided `starting_offset`.
-///
-/// Consumes all input.
-fn parse_non_identity_des(
- starting_offset: usize,
-) -> impl Fn(&[u8]) -> nom::IResult<&[u8], Vec<RefDataElement>> {
- move |input: &[u8]| {
- combinator::map_opt(
- combinator::all_consuming(multi::many0(combinator::verify(
- ProtoDataElement::parse,
- |de| IdentityDataElementType::iter().all(|t| t.type_code() != de.header.de_type),
- ))),
- |proto_des| {
- proto_des
- .into_iter()
- .enumerate()
- .map(|(offset, pde)| starting_offset.checked_add(offset).map(|sum| (sum, pde)))
- .map(|res| {
- res.map(|(offset, pde)| {
- pde.into_data_element(v1_salt::DataElementOffset::from(offset))
- })
- })
- .collect::<Option<Vec<_>>>()
- },
- )(input)
+/// An iterator that parses the given data elements iteratively. In environments where memory is
+/// not severely constrained, it is usually safer to collect this into `Result<Vec<DataElement>>`
+/// so the validity of the whole advertisement can be checked before proceeding with further
+/// processing.
+#[derive(Debug)]
+pub struct DataElementParsingIterator<'adv> {
+ input: &'adv [u8],
+ // The index of the data element this is currently at
+ offset: u8,
+}
+
+/// The error that may arise while parsing data elements.
+#[derive(Debug, PartialEq, Eq)]
+pub enum DataElementParseError {
+ /// Only one identity data element is allowed in an advertisement, but a duplicate is found
+ /// while parsing.
+ DuplicateIdentityDataElement,
+ /// Unexpected data found after the end of the data elements portion. This means either the
+ /// parser was fed with additional data (it should only be given the bytes within a section,
+ /// not the whole advertisement), or the length field in the header of the data element is
+ /// malformed.
+ UnexpectedDataAfterEnd,
+ /// There are too many data elements in the advertisement. The maximum number supported by the
+ /// current parsing logic is 255.
+ TooManyDataElements,
+ /// A parse error is returned during nom.
+ NomError(nom::error::ErrorKind),
+}
+
+impl<'adv> Iterator for DataElementParsingIterator<'adv> {
+ type Item = Result<DataElement<'adv>, DataElementParseError>;
+
+ fn next(&mut self) -> Option<Self::Item> {
+ match ProtoDataElement::parse(self.input) {
+ Ok((rem, pde)) => {
+ if !IdentityDataElementType::iter().all(|t| t.type_code() != pde.header.de_type) {
+ return Some(Err(DataElementParseError::DuplicateIdentityDataElement));
+ }
+ self.input = rem;
+ let current_offset = self.offset;
+ self.offset = if let Some(offset) = self.offset.checked_add(1) {
+ offset
+ } else {
+ return Some(Err(DataElementParseError::TooManyDataElements));
+ };
+ Some(Ok(pde.into_data_element(v1_salt::DataElementOffset::from(current_offset))))
+ }
+ Err(nom::Err::Failure(e)) => Some(Err(DataElementParseError::NomError(e.code))),
+ Err(nom::Err::Incomplete(_)) => {
+ panic!("Should always complete since we are parsing using the `nom::complete` APIs")
+ }
+ Err(nom::Err::Error(_)) => {
+ // nom `Error` is recoverable, it usually means we should move on the parsing the
+ // next section. There is nothing after data elements within a section, so we just
+ // check that there is no remaining data.
+ if !self.input.is_empty() {
+ return Some(Err(DataElementParseError::UnexpectedDataAfterEnd));
+ }
+ None
+ }
+ }
}
}
@@ -657,56 +723,14 @@ impl<'d> ProtoDataElement<'d> {
fn parse(input: &[u8]) -> nom::IResult<&[u8], ProtoDataElement> {
let (remaining, header) = DeHeader::parse(input)?;
let len = header.contents_len;
- combinator::map(bytes::complete::take(len.as_usize()), move |slice| {
+ combinator::map(bytes::complete::take(len.as_u8()), move |slice| {
let header_clone = header.clone();
ProtoDataElement { header: header_clone, contents: slice }
})(remaining)
}
- fn into_data_element(self, offset: v1_salt::DataElementOffset) -> RefDataElement<'d> {
- RefDataElement {
- offset,
- header_len: self.header.header_bytes.len().try_into().expect("header is <= 6 bytes"),
- de_type: self.header.de_type,
- contents: self.contents,
- }
- }
-}
-
-/// A data element that holds a slice reference for its contents
-#[derive(Debug, PartialEq, Eq)]
-pub(crate) struct RefDataElement<'d> {
- pub(crate) offset: v1_salt::DataElementOffset,
- pub(crate) header_len: u8,
- pub(crate) de_type: DeType,
- pub(crate) contents: &'d [u8],
-}
-
-/// A deserialized data element in a section.
-///
-/// The DE has been processed to the point of exposing a DE type and its contents as a `&[u8]`, but
-/// no DE-type-specific processing has been performed.
-#[derive(Debug)]
-pub struct DataElement<'a> {
- de_data: &'a ArrayView<u8, NP_ADV_MAX_SECTION_LEN>,
- de: &'a OffsetDataElement,
-}
-
-impl<'a> DataElement<'a> {
- /// The offset of the DE in its containing Section.
- ///
- /// Used with the section salt to derive per-DE salt.
- pub fn offset(&self) -> v1_salt::DataElementOffset {
- self.de.offset
- }
- /// The type of the DE
- pub fn de_type(&self) -> DeType {
- self.de.de_type
- }
- /// The contents of the DE
- pub fn contents(&self) -> &[u8] {
- &self.de_data.as_slice()
- [self.de.start_of_contents..self.de.start_of_contents + self.de.contents_len]
+ fn into_data_element(self, offset: v1_salt::DataElementOffset) -> DataElement<'d> {
+ DataElement { offset, de_type: self.header.de_type, contents: self.contents }
}
}
@@ -730,7 +754,7 @@ pub enum VerificationMode {
pub struct EncryptedSectionIdentity {
identity_type: EncryptedIdentityDataElementType,
validation_mode: VerificationMode,
- metadata_key: [u8; METADATA_KEY_LEN],
+ metadata_key: MetadataKey,
}
impl EncryptedSectionIdentity {
@@ -743,52 +767,37 @@ impl EncryptedSectionIdentity {
self.validation_mode
}
/// The decrypted metadata key from the section's identity DE
- pub fn metadata_key(&self) -> &[u8; METADATA_KEY_LEN] {
- &self.metadata_key
+ pub fn metadata_key(&self) -> MetadataKey {
+ self.metadata_key
}
}
-/// A DE that designates its contents via offset and length to avoid self-referential slices.
-#[derive(Debug, PartialEq, Eq)]
-struct OffsetDataElement {
+/// A deserialized data element in a section.
+///
+/// The DE has been processed to the point of exposing a DE type and its contents as a `&[u8]`, but
+/// no DE-type-specific processing has been performed.
+#[derive(Clone, Copy, Debug, PartialEq, Eq)]
+pub struct DataElement<'adv> {
offset: v1_salt::DataElementOffset,
de_type: DeType,
- start_of_contents: usize,
- contents_len: usize,
+ contents: &'adv [u8],
}
-/// Convert data elements from holding slices to holding offsets and lengths to avoid
-/// lifetime woes.
-/// This entails some data copying, so if it causes noticeable perf issues we can revisit
-/// it, but it is likely to be much cheaper than decryption.
-///
-/// # Panics
-///
-/// Will panic if handed more data elements than fit in one section. This is only possible if
-/// generating data elements from a source other than parsing a section.
-fn convert_data_elements(
- elements: &[RefDataElement],
-) -> (Vec<OffsetDataElement>, ArrayView<u8, NP_ADV_MAX_SECTION_LEN>) {
- let mut buf = tinyvec::ArrayVec::new();
-
- (
- elements
- .iter()
- .map(|de| {
- let current_len = buf.len();
- // won't overflow because these DEs originally came from a section, and now
- // we're packing only their contents without DE headers
- buf.extend_from_slice(de.contents);
- OffsetDataElement {
- offset: de.offset,
- de_type: de.de_type,
- start_of_contents: current_len,
- contents_len: de.contents.len(),
- }
- })
- .collect(),
- to_array_view(buf),
- )
+impl<'adv> DataElement<'adv> {
+ /// The offset of the DE in its containing Section.
+ ///
+ /// Used with the section salt to derive per-DE salt.
+ pub fn offset(&self) -> v1_salt::DataElementOffset {
+ self.offset
+ }
+ /// The type of the DE
+ pub fn de_type(&self) -> DeType {
+ self.de_type
+ }
+ /// The contents of the DE
+ pub fn contents(&self) -> &'adv [u8] {
+ self.contents
+ }
}
#[derive(PartialEq, Eq, Debug, Clone)]
@@ -836,8 +845,11 @@ impl EncryptionInfo {
}
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"),
+ )
}
}
@@ -882,7 +894,7 @@ impl PublicIdentity {
combinator::map(
combinator::verify(DeHeader::parse, |deh| {
deh.de_type == IdentityDataElementType::Public.type_code()
- && deh.contents_len.as_usize() == 0
+ && deh.contents_len.as_u8() == 0
}),
|_| PublicIdentity,
)(input)
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 dd88f74..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,11 +12,14 @@
// 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::{
MicEncryptedSection, SignatureEncryptedSection,
};
+use crate::extended::deserialize::test_stubs::IntermediateSectionExt;
use rand::rngs::StdRng;
use rand::seq::SliceRandom;
use rand::{Rng, SeedableRng};
@@ -35,31 +38,29 @@ fn parse_adv_ext_public_identity() {
// de 2 byte header, type 6, len 1
adv_body.extend_from_slice(&[0x81, 0x06, 0x01]);
+ let parsed_sections =
+ parse_sections(V1Header { header_byte: 0x20 }, &adv_body).unwrap().into_vec();
assert_eq!(
- Ok(vec![PlaintextSection::new(
+ vec![IntermediateSection::from(PlaintextSection::new(
PlaintextIdentityMode::Public,
- SectionContents::new(
- 10,
- &[
- // 1 byte header, len 5
- RefDataElement {
- offset: 1_usize.into(),
- header_len: 1,
- de_type: 5_u8.into(),
- contents: &[0x01, 0x02, 0x03, 0x04, 0x05],
- },
- // 2 byte header, len 1
- RefDataElement {
- offset: 2_usize.into(),
- header_len: 2,
- de_type: 6_u8.into(),
- contents: &[0x01],
- },
- ],
- )
- )
- .into(),]),
- parse_sections(V1Header { header_byte: 0x20 }, &adv_body)
+ SectionContents::new(10, &adv_body[2..], 1)
+ ))],
+ parsed_sections,
+ );
+ let expected_des = [
+ // 1 byte header, len 5
+ DataElement {
+ offset: 1_u8.into(),
+ de_type: 5_u8.into(),
+ contents: &[0x01, 0x02, 0x03, 0x04, 0x05],
+ },
+ // 2 byte header, len 1
+ DataElement { offset: 2_u8.into(), de_type: 6_u8.into(), contents: &[0x01] },
+ ];
+
+ assert_eq!(
+ &expected_des[..],
+ &parsed_sections[0].as_plaintext().unwrap().collect_data_elements().unwrap()
);
}
@@ -127,54 +128,50 @@ fn parse_adv_ext_identity() {
0x11, 0x11,
],
};
- assert_eq!(
- Ok(vec![
- SignatureEncryptedSection {
- contents: EncryptedSectionContents {
- section_header: 47,
- adv_header,
- encryption_info: encryption_info.clone(),
- identity: EncryptedIdentityMetadata {
- header_bytes: [0x90, 0x01],
- offset: 1_usize.into(),
- identity_type: EncryptedIdentityDataElementType::Private,
- },
- // skip section header + encryption info + identity header -> end of section
- all_ciphertext: &adv_body[1 + 19 + 2..48],
+ let expected_sections = [
+ SignatureEncryptedSection {
+ contents: EncryptedSectionContents {
+ section_header: 47,
+ adv_header,
+ encryption_info: encryption_info.clone(),
+ identity: EncryptedIdentityMetadata {
+ header_bytes: [0x90, 0x01],
+ offset: 1_u8.into(),
+ identity_type: EncryptedIdentityDataElementType::Private,
},
- }
- .into(),
- SignatureEncryptedSection {
- contents: EncryptedSectionContents {
- section_header: 48,
- adv_header,
- encryption_info: encryption_info.clone(),
- identity: EncryptedIdentityMetadata {
- header_bytes: [0x90, 0x02],
- offset: 1_usize.into(),
- identity_type: EncryptedIdentityDataElementType::Trusted,
- },
- all_ciphertext: &adv_body[48 + 1 + 19 + 2..97],
+ // skip section header + encryption info + identity header -> end of section
+ all_ciphertext: &adv_body[1 + 19 + 2..48],
+ },
+ },
+ SignatureEncryptedSection {
+ contents: EncryptedSectionContents {
+ section_header: 48,
+ adv_header,
+ encryption_info: encryption_info.clone(),
+ identity: EncryptedIdentityMetadata {
+ header_bytes: [0x90, 0x02],
+ offset: 1_u8.into(),
+ identity_type: EncryptedIdentityDataElementType::Trusted,
},
- }
- .into(),
- SignatureEncryptedSection {
- contents: EncryptedSectionContents {
- section_header: 49,
- adv_header,
- encryption_info,
- identity: EncryptedIdentityMetadata {
- header_bytes: [0x90, 0x04],
- offset: 1_usize.into(),
- identity_type: EncryptedIdentityDataElementType::Provisioned,
- },
- all_ciphertext: &adv_body[97 + 1 + 19 + 2..],
+ all_ciphertext: &adv_body[48 + 1 + 19 + 2..97],
+ },
+ },
+ SignatureEncryptedSection {
+ contents: EncryptedSectionContents {
+ section_header: 49,
+ adv_header,
+ encryption_info,
+ identity: EncryptedIdentityMetadata {
+ header_bytes: [0x90, 0x04],
+ offset: 1_u8.into(),
+ identity_type: EncryptedIdentityDataElementType::Provisioned,
},
- }
- .into()
- ]),
- parse_sections(adv_header, &adv_body)
- );
+ all_ciphertext: &adv_body[97 + 1 + 19 + 2..],
+ },
+ },
+ ];
+ let parsed_sections = parse_sections(adv_header, &adv_body).unwrap();
+ assert_eq!(parsed_sections.into_vec(), expected_sections.map(IntermediateSection::from));
}
#[test]
@@ -256,57 +253,53 @@ fn parse_adv_ext_mic_identity() {
0x11, 0x11,
],
};
- assert_eq!(
- Ok(vec![
- MicEncryptedSection {
- contents: EncryptedSectionContents {
- section_header: 63,
- adv_header,
- encryption_info: encryption_info.clone(),
- identity: EncryptedIdentityMetadata {
- header_bytes: [0x90, 0x01],
- offset: 1_usize.into(),
- identity_type: EncryptedIdentityDataElementType::Private,
- },
- // skip section header + encryption info + identity header -> end of ciphertext
- all_ciphertext: &adv_body[1 + 19 + 2..64 - 16],
+ let expected_sections = [
+ MicEncryptedSection {
+ contents: EncryptedSectionContents {
+ section_header: 63,
+ adv_header,
+ encryption_info: encryption_info.clone(),
+ identity: EncryptedIdentityMetadata {
+ header_bytes: [0x90, 0x01],
+ offset: 1_u8.into(),
+ identity_type: EncryptedIdentityDataElementType::Private,
},
- mic: SectionMic::from([0x33; 16]),
- }
- .into(),
- MicEncryptedSection {
- contents: EncryptedSectionContents {
- section_header: 64,
- adv_header,
- encryption_info: encryption_info.clone(),
- identity: EncryptedIdentityMetadata {
- header_bytes: [0x90, 0x02],
- offset: 1_usize.into(),
- identity_type: EncryptedIdentityDataElementType::Trusted,
- },
- all_ciphertext: &adv_body[64 + 1 + 19 + 2..129 - 16],
+ // skip section header + encryption info + identity header -> end of ciphertext
+ all_ciphertext: &adv_body[1 + 19 + 2..64 - 16],
+ },
+ mic: SectionMic::from([0x33; 16]),
+ },
+ MicEncryptedSection {
+ contents: EncryptedSectionContents {
+ section_header: 64,
+ adv_header,
+ encryption_info: encryption_info.clone(),
+ identity: EncryptedIdentityMetadata {
+ header_bytes: [0x90, 0x02],
+ offset: 1_u8.into(),
+ identity_type: EncryptedIdentityDataElementType::Trusted,
},
- mic: SectionMic::from([0x66; 16]),
- }
- .into(),
- MicEncryptedSection {
- contents: EncryptedSectionContents {
- section_header: 65,
- adv_header,
- encryption_info,
- identity: EncryptedIdentityMetadata {
- header_bytes: [0x90, 0x04],
- offset: 1_usize.into(),
- identity_type: EncryptedIdentityDataElementType::Provisioned,
- },
- all_ciphertext: &adv_body[129 + 1 + 19 + 2..195 - 16],
+ all_ciphertext: &adv_body[64 + 1 + 19 + 2..129 - 16],
+ },
+ mic: SectionMic::from([0x66; 16]),
+ },
+ MicEncryptedSection {
+ contents: EncryptedSectionContents {
+ section_header: 65,
+ adv_header,
+ encryption_info,
+ identity: EncryptedIdentityMetadata {
+ header_bytes: [0x90, 0x04],
+ offset: 1_u8.into(),
+ identity_type: EncryptedIdentityDataElementType::Provisioned,
},
- mic: SectionMic::from([0x99; 16]),
- }
- .into(),
- ]),
- parse_sections(adv_header, &adv_body)
- );
+ all_ciphertext: &adv_body[129 + 1 + 19 + 2..195 - 16],
+ },
+ mic: SectionMic::from([0x99; 16]),
+ },
+ ];
+ let parsed_sections = parse_sections(adv_header, &adv_body).unwrap();
+ assert_eq!(parsed_sections.into_vec(), &expected_sections.map(IntermediateSection::from));
}
#[test]
@@ -441,11 +434,8 @@ fn public_identity_not_first_de_error() {
adv_body.push(0x03);
assert_eq!(
- Err(nom::Err::Error(error::Error {
- input: &adv_body[1..],
- code: error::ErrorKind::Verify
- })),
- parse_sections(V1Header { header_byte: 0x20 }, &adv_body)
+ nom::Err::Error(error::Error { input: &adv_body[1..], code: error::ErrorKind::Verify }),
+ parse_sections(V1Header { header_byte: 0x20 }, &adv_body).unwrap_err()
);
}
@@ -469,12 +459,15 @@ fn invalid_public_section() {
adv_body.push(0x03);
}
if add_des {
- adv_body[0] += 3;
- adv_body.extend_from_slice(&[0x81, 0x70, 0xFF]);
+ adv_body[0] += 1;
+ 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();
+
if shuffle {
adv_body.shuffle(&mut rng);
}
@@ -484,8 +477,8 @@ fn invalid_public_section() {
// * the section identity is missing
// * the identity does not follow the section length
// * the section length is 0
- if remove_section_len || !add_public_identity || (shuffle && adv_body.len() > 2) {
- parse_sections(V1Header { header_byte: 0x20 }, &adv_body)
+ if remove_section_len || !add_public_identity || (ordered_adv != adv_body) {
+ let _ = parse_sections(V1Header { header_byte: 0x20 }, &adv_body)
.expect_err("Expected to fail because of missing section length or identity");
}
}
@@ -504,12 +497,11 @@ fn public_identity_after_public_identity_error() {
// public identity after another DE
adv_body.push(0x03);
+ let sections = parse_sections(V1Header { header_byte: 0x20 }, &adv_body).unwrap();
+ assert_eq!(sections.len(), 1);
assert_eq!(
- Err(nom::Err::Error(error::Error {
- input: &adv_body[1..],
- code: error::ErrorKind::Verify
- })),
- parse_sections(V1Header { header_byte: 0x20 }, &adv_body)
+ DataElementParseError::DuplicateIdentityDataElement,
+ sections[0].as_plaintext().unwrap().collect_data_elements().unwrap_err()
);
}
@@ -526,12 +518,12 @@ fn salt_public_identity_error() {
adv_body.extend_from_slice(&[0x81, 0x70, 0xFF]);
assert_eq!(
- Err(nom::Err::Error(error::Error {
+ nom::Err::Error(error::Error {
input: &adv_body[1..],
// Eof because all_consuming is used to ensure complete section is parsed
code: error::ErrorKind::Verify
- })),
- parse_sections(V1Header { header_byte: 0x20 }, &adv_body)
+ }),
+ parse_sections(V1Header { header_byte: 0x20 }, &adv_body).unwrap_err()
);
}
@@ -553,12 +545,12 @@ fn salt_mic_public_identity_error() {
adv_body.extend_from_slice(&[0x81, 0x70, 0xFF]);
assert_eq!(
- Err(nom::Err::Error(error::Error {
+ nom::Err::Error(error::Error {
input: &adv_body[1..],
// Eof because all_consuming is used to ensure complete section is parsed
code: error::ErrorKind::Verify
- })),
- parse_sections(V1Header { header_byte: 0x20 }, &adv_body)
+ }),
+ parse_sections(V1Header { header_byte: 0x20 }, &adv_body).unwrap_err()
);
}
@@ -566,8 +558,8 @@ fn salt_mic_public_identity_error() {
fn parse_adv_no_identity() {
let adv_body = vec![0x55, 0x01, 0x02, 0x03, 0x04, 0x05];
assert_eq!(
- Err(nom::Err::Error(error::Error { input: &adv_body[1..], code: error::ErrorKind::Eof })),
- parse_sections(V1Header { header_byte: 0x20 }, &adv_body)
+ nom::Err::Error(error::Error { input: &adv_body[1..], code: error::ErrorKind::Eof }),
+ parse_sections(V1Header { header_byte: 0x20 }, &adv_body).unwrap_err()
);
}
@@ -685,8 +677,8 @@ fn parse_adv_signature_encrypted_plaintext_mix() {
let adv_header = V1Header { header_byte: 0x20 };
assert_eq!(
- Err(nom::Err::Error(error::Error { input: &adv_body[11..], code: error::ErrorKind::Eof })),
- parse_sections(adv_header, &adv_body)
+ nom::Err::Error(error::Error { input: &adv_body[11..], code: error::ErrorKind::Eof }),
+ parse_sections(adv_header, &adv_body).unwrap_err()
);
}
@@ -704,8 +696,8 @@ impl<'a> From<MicEncryptedSection<'a>> for IntermediateSection<'a> {
}
}
-impl<'a> From<PlaintextSection> for IntermediateSection<'a> {
- fn from(s: PlaintextSection) -> Self {
+impl<'a> From<PlaintextSection<'a>> for IntermediateSection<'a> {
+ fn from(s: PlaintextSection<'a>) -> Self {
IntermediateSection::Plaintext(s)
}
}
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 15df16a..5fa2dbe 100644
--- a/nearby/presence/np_adv/src/extended/deserialize/section_tests.rs
+++ b/nearby/presence/np_adv/src/extended/deserialize/section_tests.rs
@@ -12,32 +12,35 @@
// 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::deserialization_arena;
+use crate::deserialization_arena::DeserializationArena;
use crate::extended::serialize::AdvertisementType;
use crate::extended::NP_V1_ADV_MAX_PUBLIC_SECTION_COUNT;
use crate::{
credential::{
- simple::SimpleV1Credential,
- source::{CredentialSource, SliceCredentialSource},
- MatchableCredential, V1Credential,
+ source::{DiscoveryCredentialSource, SliceCredentialSource},
+ v1::{SignedBroadcastCryptoMaterial, SimpleSignedBroadcastCryptoMaterial, V1},
+ DiscoveryCryptoMaterial, EmptyMatchedCredential, MatchableCredential,
+ MetadataMatchedCredential, SimpleBroadcastCryptoMaterial,
},
extended::{
data_elements::GenericDataElement,
- deserialize::{
- convert_data_elements,
- test_stubs::{HkdfCryptoMaterial, IntermediateSectionExt},
- OffsetDataElement,
- },
+ deserialize::{test_stubs::IntermediateSectionExt, DataElement},
serialize::{
self, AdvBuilder, MicEncryptedSectionEncoder, PublicSectionEncoder, SectionBuilder,
SignedEncryptedSectionEncoder, WriteDataElement,
},
MAX_DE_LEN,
},
- parse_adv_header, AdvHeader,
+ parse_adv_header, AdvHeader, WithMatchedCredential,
};
+use core::borrow::Borrow;
+use core::convert::Into;
use crypto_provider::{CryptoProvider, CryptoRng};
use crypto_provider_default::CryptoProviderImpl;
use rand::{seq::SliceRandom as _, Rng as _, SeedableRng as _};
@@ -52,7 +55,6 @@ fn deserialize_public_identity_section() {
PublicSectionEncoder::default(),
PlaintextIdentityMode::Public,
1,
- 1,
);
}
@@ -71,17 +73,32 @@ fn deserialize_mic_encrypted_rand_identities_finds_correct_one() {
// share a metadata key to emphasize that we're _only_ using the identity to
// differentiate
let metadata_key: [u8; 16] = rng.gen();
+ let metadata_key = MetadataKey(metadata_key);
let creds = identities
.iter()
- .enumerate()
- .map(|(index, (key_seed, key_pair))| {
- SimpleV1Credential::new(
- HkdfCryptoMaterial::new(key_seed, &metadata_key, key_pair.public()),
- index,
+ .map(|(key_seed, key_pair)| {
+ SimpleSignedBroadcastCryptoMaterial::new(
+ *key_seed,
+ metadata_key,
+ key_pair.private_key(),
)
})
+ .enumerate()
+ .map(|(index, broadcast_cm)| {
+ let match_data = MetadataMatchedCredential::<Vec<u8>>::encrypt_from_plaintext::<
+ _,
+ _,
+ CryptoProviderImpl,
+ >(&broadcast_cm, &[index as u8]);
+
+ let discovery_credential =
+ broadcast_cm.derive_v1_discovery_credential::<CryptoProviderImpl>();
+
+ MatchableCredential { discovery_credential, match_data }
+ })
.collect::<Vec<_>>();
+
let cred_source = SliceCredentialSource::new(&creds);
let identity_type =
@@ -89,18 +106,19 @@ fn deserialize_mic_encrypted_rand_identities_finds_correct_one() {
let mut adv_builder = AdvBuilder::new(AdvertisementType::Encrypted);
- let hkdf = np_hkdf::NpKeySeedHkdf::<CryptoProviderImpl>::new(chosen_key_seed);
+ let broadcast_cm = SimpleBroadcastCryptoMaterial::<V1>::new(*chosen_key_seed, metadata_key);
+
let mut section_builder = adv_builder
- .section_builder(MicEncryptedSectionEncoder::new_random_salt(
+ .section_builder(MicEncryptedSectionEncoder::<CryptoProviderImpl>::new_random_salt(
&mut crypto_rng,
identity_type,
- &metadata_key,
- &hkdf,
+ &broadcast_cm,
))
.unwrap();
- let (expected_de_data, expected_des, orig_des) =
- fill_section_random_des(&mut rng, &mut section_builder, 2);
+ let mut expected_de_data = vec![];
+ let (expected_des, orig_des) =
+ fill_section_random_des(&mut rng, &mut expected_de_data, &mut section_builder, 2);
section_builder.add_to_advertisement();
@@ -116,29 +134,33 @@ fn deserialize_mic_encrypted_rand_identities_finds_correct_one() {
let sections = parse_sections(v1_header, remaining).unwrap();
assert_eq!(1, sections.len());
- let (section, cred) = try_deserialize_all_creds::<_, _, CryptoProviderImpl>(
- sections[0].as_ciphertext().unwrap(),
- &cred_source,
- )
- .unwrap()
- .unwrap();
- assert_eq!(&chosen_index, cred.matched_data());
+ let arena = deserialization_arena!();
+
+ let section = sections[0].as_ciphertext().unwrap();
+ let matched_section =
+ try_deserialize_all_creds::<_, CryptoProviderImpl>(arena, section, &cred_source)
+ .unwrap()
+ .unwrap();
+ // 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_slice(), &decrypted_metadata);
+
+ // Verify that the section contents passed through unaltered
+ let section = matched_section.contents();
assert_eq!(section.identity_type(), identity_type);
assert_eq!(section.verification_mode(), VerificationMode::Mic);
- assert_eq!(section.metadata_key(), &metadata_key);
+ assert_eq!(section.metadata_key(), metadata_key);
assert_eq!(
- section.contents,
- SectionContents {
- section_header: (19 + 2 + 16 + total_de_len(&orig_des) + 16) as u8,
- de_data: ArrayView::try_from_slice(expected_de_data.as_slice()).unwrap(),
- data_elements: expected_des,
- }
+ section.contents.section_header,
+ (19 + 2 + 16 + total_de_len(&orig_des) + 16) as u8
);
+ let data_elements = section.collect_data_elements().unwrap();
+ assert_eq!(data_elements, expected_des);
assert_eq!(
- section
- .data_elements()
+ data_elements
+ .iter()
.map(|de| GenericDataElement::try_from(de.de_type(), de.contents()).unwrap())
.collect::<Vec<_>>(),
orig_des
@@ -159,27 +181,31 @@ fn deserialize_signature_encrypted_rand_identities_finds_correct_one() {
// share a metadata key to emphasize that we're _only_ using the identity to
// differentiate
let metadata_key: [u8; 16] = rng.gen();
+ let metadata_key = MetadataKey(metadata_key);
let creds = identities
.iter()
- .enumerate()
- .map(|(index, (key_seed, key_pair))| {
- let hkdf = np_hkdf::NpKeySeedHkdf::<CryptoProviderImpl>::new(key_seed);
- let unsigned =
- hkdf.extended_unsigned_metadata_key_hmac_key().calculate_hmac(&metadata_key);
- let signed =
- hkdf.extended_signed_metadata_key_hmac_key().calculate_hmac(&metadata_key);
- SimpleV1Credential::new(
- HkdfCryptoMaterial {
- hkdf: *key_seed,
- expected_unsigned_metadata_key_hmac: unsigned,
- expected_signed_metadata_key_hmac: signed,
- pub_key: key_pair.public().to_bytes(),
- },
- index,
+ .map(|(key_seed, key_pair)| {
+ SimpleSignedBroadcastCryptoMaterial::new(
+ *key_seed,
+ metadata_key,
+ key_pair.private_key(),
)
})
+ .enumerate()
+ .map(|(index, broadcast_cm)| {
+ let match_data = MetadataMatchedCredential::<Vec<u8>>::encrypt_from_plaintext::<
+ _,
+ _,
+ CryptoProviderImpl,
+ >(&broadcast_cm, &[index as u8]);
+
+ let discovery_credential =
+ broadcast_cm.derive_v1_discovery_credential::<CryptoProviderImpl>();
+ MatchableCredential { discovery_credential, match_data }
+ })
.collect::<Vec<_>>();
+
let cred_source = SliceCredentialSource::new(&creds);
let identity_type =
@@ -187,19 +213,23 @@ fn deserialize_signature_encrypted_rand_identities_finds_correct_one() {
let mut adv_builder = AdvBuilder::new(AdvertisementType::Encrypted);
- let hkdf = np_hkdf::NpKeySeedHkdf::<CryptoProviderImpl>::new(chosen_key_seed);
+ let broadcast_cm = SimpleSignedBroadcastCryptoMaterial::new(
+ *chosen_key_seed,
+ metadata_key,
+ chosen_key_pair.private_key(),
+ );
+
let mut section_builder = adv_builder
- .section_builder(SignedEncryptedSectionEncoder::new_random_salt(
+ .section_builder(SignedEncryptedSectionEncoder::<CryptoProviderImpl>::new_random_salt(
&mut crypto_rng,
identity_type,
- &metadata_key,
- chosen_key_pair,
- &hkdf,
+ &broadcast_cm,
))
.unwrap();
- let (expected_de_data, expected_des, orig_des) =
- fill_section_random_des(&mut rng, &mut section_builder, 2);
+ let mut expected_de_data = vec![];
+ let (expected_des, orig_des) =
+ fill_section_random_des(&mut rng, &mut expected_de_data, &mut section_builder, 2);
section_builder.add_to_advertisement();
@@ -213,31 +243,35 @@ fn deserialize_signature_encrypted_rand_identities_finds_correct_one() {
panic!("incorrect header");
};
+ let arena = deserialization_arena!();
+
let sections = parse_sections(v1_header, remaining).unwrap();
assert_eq!(1, sections.len());
- let (section, cred) = try_deserialize_all_creds::<_, _, CryptoProviderImpl>(
- sections[0].as_ciphertext().unwrap(),
- &cred_source,
- )
- .unwrap()
- .unwrap();
- assert_eq!(&chosen_index, cred.matched_data());
+ let section = sections[0].as_ciphertext().unwrap();
+ let matched_section =
+ try_deserialize_all_creds::<_, CryptoProviderImpl>(arena, section, &cred_source)
+ .unwrap()
+ .unwrap();
+ // 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_slice(), &decrypted_metadata);
+
+ // Verify that the section contents passed through unaltered
+ let section = matched_section.contents();
assert_eq!(section.identity_type(), identity_type);
assert_eq!(section.verification_mode(), VerificationMode::Signature);
- assert_eq!(section.metadata_key(), &metadata_key);
+ assert_eq!(section.metadata_key(), metadata_key);
assert_eq!(
- section.contents,
- SectionContents {
- section_header: (19 + 2 + 16 + 64 + total_de_len(&orig_des)) as u8,
- de_data: ArrayView::try_from_slice(expected_de_data.as_slice()).unwrap(),
- data_elements: expected_des,
- }
+ section.contents.section_header,
+ (19 + 2 + 16 + 64 + total_de_len(&orig_des)) as u8
);
+ let data_elements = section.collect_data_elements().unwrap();
+ assert_eq!(data_elements, expected_des);
assert_eq!(
- section
- .data_elements()
+ data_elements
+ .iter()
.map(|de| GenericDataElement::try_from(de.de_type(), de.contents()).unwrap())
.collect::<Vec<_>>(),
orig_des
@@ -260,17 +294,24 @@ fn deserialize_encrypted_no_matching_identities_finds_nothing() {
// share a metadata key to emphasize that we're _only_ using the identity to
// differentiate
let metadata_key: [u8; 16] = rng.gen();
+ let metadata_key = MetadataKey(metadata_key);
let credentials = identities
.iter()
- .enumerate()
- .map(|(index, (key_seed, key_pair))| {
- SimpleV1Credential::new(
- HkdfCryptoMaterial::new(key_seed, &metadata_key, key_pair.public()),
- index,
+ .map(|(key_seed, key_pair)| {
+ SimpleSignedBroadcastCryptoMaterial::new(
+ *key_seed,
+ metadata_key,
+ key_pair.private_key(),
)
+ .derive_v1_discovery_credential::<CryptoProviderImpl>()
+ })
+ .map(|discovery_credential| MatchableCredential {
+ discovery_credential,
+ match_data: EmptyMatchedCredential,
})
.collect::<Vec<_>>();
+
let cred_source = SliceCredentialSource::new(&credentials);
let identity_type =
@@ -278,30 +319,35 @@ fn deserialize_encrypted_no_matching_identities_finds_nothing() {
let mut adv_builder = AdvBuilder::new(AdvertisementType::Encrypted);
- let hkdf = np_hkdf::NpKeySeedHkdf::<CryptoProviderImpl>::new(&chosen_key_seed);
+ let broadcast_cm = SimpleSignedBroadcastCryptoMaterial::new(
+ chosen_key_seed,
+ metadata_key,
+ chosen_key_pair.private_key(),
+ );
// awkward split because SectionEncoder isn't object-safe, so we can't just have a
// Box<dyn SectionEncoder> and use that in one code path
if signed {
- let identity = SignedEncryptedSectionEncoder::new_random_salt(
+ let identity = SignedEncryptedSectionEncoder::<CryptoProviderImpl>::new_random_salt(
&mut crypto_rng,
identity_type,
- &metadata_key,
- &chosen_key_pair,
- &hkdf,
+ &broadcast_cm,
);
let mut section_builder = adv_builder.section_builder(identity).unwrap();
- let _ = fill_section_random_des(&mut rng, &mut section_builder, 2);
+ let mut expected_de_data = vec![];
+ let _ =
+ fill_section_random_des(&mut rng, &mut expected_de_data, &mut section_builder, 2);
section_builder.add_to_advertisement();
} else {
- let identity = MicEncryptedSectionEncoder::new_random_salt(
+ let identity = MicEncryptedSectionEncoder::<CryptoProviderImpl>::new_random_salt(
&mut crypto_rng,
identity_type,
- &metadata_key,
- &hkdf,
+ &broadcast_cm,
);
let mut section_builder = adv_builder.section_builder(identity).unwrap();
- let _ = fill_section_random_des(&mut rng, &mut section_builder, 2);
+ let mut expected_de_data = vec![];
+ let _ =
+ fill_section_random_des(&mut rng, &mut expected_de_data, &mut section_builder, 2);
section_builder.add_to_advertisement();
};
@@ -316,9 +362,10 @@ fn deserialize_encrypted_no_matching_identities_finds_nothing() {
let sections = parse_sections(v1_header, remaining).unwrap();
assert_eq!(1, sections.len());
- assert!(try_deserialize_all_creds::<_, _, CryptoProviderImpl>(
+ assert!(try_deserialize_all_creds::<_, CryptoProviderImpl>(
+ deserialization_arena!(),
sections[0].as_ciphertext().unwrap(),
- &cred_source
+ &cred_source,
)
.unwrap()
.is_none());
@@ -326,138 +373,30 @@ fn deserialize_encrypted_no_matching_identities_finds_nothing() {
}
#[test]
-fn convert_data_elements_empty() {
- let orig_des = vec![];
-
- let (des, data) = convert_data_elements(&orig_des);
-
- assert_eq!(Vec::<OffsetDataElement>::new(), des);
- assert_eq!(&Vec::<u8>::new(), data.as_slice());
-}
-
-#[test]
-fn convert_data_elements_just_fits() {
- // this is actually longer than any real section's worth of DEs could be since we aren't putting
- // DE headers in the array
- let orig_data = vec![0x33; 1000];
-
- let orig_des = vec![
- RefDataElement {
- offset: 2.into(),
- header_len: 2,
- de_type: 100_u32.into(),
- contents: &orig_data[0..10],
- },
- RefDataElement {
- offset: 3.into(),
- header_len: 2,
- de_type: 101_u32.into(),
- contents: &orig_data[10..100],
- },
- RefDataElement {
- offset: 4.into(),
- header_len: 2,
- de_type: 102_u32.into(),
- contents: &orig_data[100..NP_ADV_MAX_SECTION_LEN],
- },
- ];
-
- let (des, data) = convert_data_elements(&orig_des);
-
- assert_eq!(
- &[
- OffsetDataElement {
- offset: 2.into(),
- de_type: 100_u32.into(),
- start_of_contents: 0,
- contents_len: 10
- },
- OffsetDataElement {
- offset: 3.into(),
- de_type: 101_u32.into(),
- start_of_contents: 10,
- contents_len: 90
- },
- OffsetDataElement {
- offset: 4.into(),
- de_type: 102_u32.into(),
- start_of_contents: 100,
- contents_len: NP_ADV_MAX_SECTION_LEN - 100,
- },
- ],
- &des[..]
- );
- assert_eq!(&[0x33; NP_ADV_MAX_SECTION_LEN], data.as_slice());
-}
-
-#[test]
-#[should_panic]
-fn convert_data_elements_doesnt_fit_panic() {
- let orig_data = vec![0x33; 1000];
- let orig_des = vec![
- RefDataElement {
- offset: 2.into(),
- header_len: 2,
- de_type: 100_u32.into(),
- contents: &orig_data[0..10],
- },
- // impossibly large DE
- RefDataElement {
- offset: 3.into(),
- header_len: 2,
- de_type: 101_u32.into(),
- contents: &orig_data[10..500],
- },
- ];
-
- let _ = convert_data_elements(&orig_des);
-}
-
-#[test]
fn section_des_expose_correct_data() {
- let mut orig_data = Vec::new();
- orig_data.resize(130, 0);
- for (index, byte) in orig_data.iter_mut().enumerate() {
- *byte = index as u8;
- }
-
- let orig_des = vec![
- OffsetDataElement {
- offset: 2.into(),
- de_type: 100_u32.into(),
- start_of_contents: 0,
- contents_len: 10,
- },
- OffsetDataElement {
- offset: 3.into(),
- de_type: 101_u32.into(),
- start_of_contents: 10,
- contents_len: 90,
- },
- OffsetDataElement {
- offset: 4.into(),
- de_type: 102_u32.into(),
- start_of_contents: 100,
- contents_len: 30,
- },
- ];
+ // 2 sections, 3 DEs each
+ let mut de_data = vec![];
+ // de 1 byte header, type 5, len 5
+ de_data.extend_from_slice(&[0x55, 0x01, 0x02, 0x03, 0x04, 0x05]);
+ // de 2 byte header, type 16, len 1
+ de_data.extend_from_slice(&[0x81, 0x10, 0x01]);
let section = SectionContents {
section_header: 99,
- de_data: ArrayView::try_from_slice(&orig_data).unwrap(),
- data_elements: orig_des,
+ de_region_excl_identity: &de_data,
+ data_element_start_offset: 2,
};
// extract out the parts of the DE we care about
- let des = section
- .data_elements()
- .map(|de| (de.offset(), de.de_type(), de.contents().to_vec()))
- .collect::<Vec<_>>();
+ let des = section.iter_data_elements().collect::<Result<Vec<_>, _>>().unwrap();
assert_eq!(
vec![
- (2.into(), 100_u32.into(), orig_data[0..10].to_vec()),
- (3.into(), 101_u32.into(), orig_data[10..100].to_vec()),
- (4.into(), 102_u32.into(), orig_data[100..].to_vec())
+ DataElement {
+ offset: 2.into(),
+ de_type: 5_u32.into(),
+ contents: &[0x01, 0x02, 0x03, 0x04, 0x05]
+ },
+ DataElement { offset: 3.into(), de_type: 16_u32.into(), contents: &[0x01] },
],
des
);
@@ -474,7 +413,7 @@ fn do_deserialize_zero_section_header() {
} 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]
@@ -487,7 +426,7 @@ fn do_deserialize_empty_section() {
} 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]
@@ -526,7 +465,7 @@ fn try_deserialize_over_max_number_of_public_sections() {
} 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");
}
@@ -542,7 +481,6 @@ pub(crate) fn random_de<R: rand::Rng>(rng: &mut R) -> GenericDataElement {
fn do_deserialize_section_unencrypted<I: serialize::SectionEncoder>(
identity: I,
expected_identity: PlaintextIdentityMode,
- prefix_len: usize,
de_offset: usize,
) {
let mut rng = rand::rngs::StdRng::from_entropy();
@@ -550,8 +488,9 @@ fn do_deserialize_section_unencrypted<I: serialize::SectionEncoder>(
let mut adv_builder = AdvBuilder::new(AdvertisementType::Plaintext);
let mut section_builder = adv_builder.section_builder(identity).unwrap();
- let (expected_de_data, expected_des, orig_des) =
- fill_section_random_des(&mut rng, &mut section_builder, de_offset);
+ let mut expected_de_data = vec![];
+ let (expected_des, orig_des) =
+ fill_section_random_des(&mut rng, &mut expected_de_data, &mut section_builder, de_offset);
section_builder.add_to_advertisement();
@@ -569,37 +508,29 @@ fn do_deserialize_section_unencrypted<I: serialize::SectionEncoder>(
assert_eq!(1, sections.len());
let section = sections[0].as_plaintext().unwrap();
+ assert_eq!(section.identity(), expected_identity);
+ let data_elements = section.collect_data_elements().unwrap();
+ assert_eq!(data_elements, expected_des);
assert_eq!(
- &PlaintextSection {
- identity: expected_identity,
- contents: SectionContents {
- section_header: (prefix_len + total_de_len(&orig_des)) as u8,
- de_data: ArrayView::try_from_slice(expected_de_data.as_slice()).unwrap(),
- data_elements: expected_des,
- }
- },
- section
- );
- assert_eq!(
- section
- .contents
- .data_elements()
+ data_elements
+ .iter()
.map(|de| GenericDataElement::try_from(de.de_type(), de.contents()).unwrap())
.collect::<Vec<_>>(),
orig_des
);
}
-fn fill_section_random_des<R: rand::Rng, I: serialize::SectionEncoder>(
+fn fill_section_random_des<'adv, R: rand::Rng, I: serialize::SectionEncoder>(
mut rng: &mut R,
- section_builder: &mut SectionBuilder<I>,
+ sink: &'adv mut Vec<u8>,
+ section_builder: &mut SectionBuilder<&mut AdvBuilder, I>,
de_offset: usize,
-) -> (Vec<u8>, Vec<OffsetDataElement>, Vec<GenericDataElement>) {
- let mut expected_de_data = vec![];
+) -> (Vec<DataElement<'adv>>, Vec<GenericDataElement>) {
let mut expected_des = vec![];
let mut orig_des = vec![];
+ let mut de_ranges = vec![];
- for index in 0..rng.gen_range(1..10) {
+ for _ in 0..rng.gen_range(1..10) {
let de = random_de(&mut rng);
let de_clone = de.clone();
@@ -607,55 +538,65 @@ fn fill_section_random_des<R: rand::Rng, I: serialize::SectionEncoder>(
break;
}
- let orig_len = expected_de_data.len();
- de.write_de_contents(&mut expected_de_data).unwrap();
- let contents_len = expected_de_data.len() - orig_len;
+ let orig_len = sink.len();
+ de.write_de_contents(sink).unwrap();
+ let contents_len = sink.len() - orig_len;
+ de_ranges.push(orig_len..orig_len + contents_len);
+ orig_des.push(de);
+ }
- expected_des.push(OffsetDataElement {
- offset: (index as usize + de_offset).into(),
+ for (index, (de, range)) in orig_des.iter().zip(de_ranges).enumerate() {
+ expected_des.push(DataElement {
+ offset: u8::try_from(index + de_offset).unwrap().into(),
de_type: de.de_header().de_type,
- contents_len,
- start_of_contents: orig_len,
+ contents: &sink[range],
});
- orig_des.push(de);
}
- (expected_de_data, expected_des, orig_des)
+ (expected_des, orig_des)
}
fn total_de_len(des: &[GenericDataElement]) -> usize {
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()
}
-type TryDeserOutput<'c, C> = Option<(DecryptedSection, <C as MatchableCredential>::Matched<'c>)>;
+type TryDeserOutput<'adv, M> = Option<WithMatchedCredential<M, DecryptedSection<'adv>>>;
/// Returns:
/// - `Ok(Some)` if a matching credential was found
/// - `Ok(None)` if no matching credential was found, or if `cred_source` provides no credentials
/// - `Err` if an error occurred.
-fn try_deserialize_all_creds<'c, C, S, P>(
- section: &CiphertextSection,
- cred_source: &'c S,
-) -> Result<TryDeserOutput<'c, C>, BatchSectionDeserializeError>
+fn try_deserialize_all_creds<'a, S, P>(
+ arena: DeserializationArena<'a>,
+ section: &'a CiphertextSection,
+ cred_source: &'a S,
+) -> Result<TryDeserOutput<'a, S::Matched>, BatchSectionDeserializeError>
where
- C: V1Credential,
- S: CredentialSource<C>,
+ S: DiscoveryCredentialSource<'a, V1>,
P: CryptoProvider,
{
- for c in cred_source.iter() {
- let crypto_material = c.crypto_material();
- match section.try_resolve_identity_and_deserialize::<_, P>(crypto_material) {
- Ok(s) => return Ok(Some((s, c.matched()))),
+ let mut allocator = arena.into_allocator();
+ for (crypto_material, match_data) in cred_source.iter() {
+ match section
+ .try_resolve_identity_and_deserialize::<_, P>(&mut allocator, crypto_material.borrow())
+ {
+ Ok(s) => {
+ let metadata_nonce = crypto_material.metadata_nonce::<P>();
+ return Ok(Some(WithMatchedCredential::new(match_data, metadata_nonce, s)));
+ }
Err(e) => match e {
SectionDeserializeError::IncorrectCredential => continue,
SectionDeserializeError::ParseError => {
return Err(BatchSectionDeserializeError::ParseError)
}
+ SectionDeserializeError::ArenaOutOfSpace => {
+ return Err(BatchSectionDeserializeError::ArenaOutOfSpace)
+ }
},
}
}
@@ -676,4 +617,6 @@ fn build_dummy_advertisement_sections(number_of_sections: usize) -> AdvBuilder {
enum BatchSectionDeserializeError {
/// Advertisement data is malformed
ParseError,
+ /// The given arena is not large enough to hold the decrypted data
+ ArenaOutOfSpace,
}
diff --git a/nearby/presence/np_adv/src/extended/deserialize/test_stubs.rs b/nearby/presence/np_adv/src/extended/deserialize/test_stubs.rs
index 437b997..68b6f5d 100644
--- a/nearby/presence/np_adv/src/extended/deserialize/test_stubs.rs
+++ b/nearby/presence/np_adv/src/extended/deserialize/test_stubs.rs
@@ -14,99 +14,29 @@
extern crate std;
-use crypto_provider::{ed25519, CryptoProvider};
-use np_hkdf::{NpKeySeedHkdf, UnsignedSectionKeys};
use std::prelude::rust_2021::*;
use crate::{
- credential::v1::*,
extended::deserialize::{CiphertextSection, PlaintextSection},
IntermediateSection,
};
-pub(crate) struct HkdfCryptoMaterial {
- pub(crate) hkdf: [u8; 32],
- pub(crate) expected_unsigned_metadata_key_hmac: [u8; 32],
- pub(crate) expected_signed_metadata_key_hmac: [u8; 32],
- pub(crate) pub_key: ed25519::RawPublicKey,
-}
-
-impl HkdfCryptoMaterial {
- pub(crate) fn new<C: CryptoProvider>(
- hkdf_key_seed: &[u8; 32],
- metadata_key: &[u8; 16],
- pub_key: np_ed25519::PublicKey<C>,
- ) -> Self {
- let hkdf = NpKeySeedHkdf::<C>::new(hkdf_key_seed);
- let unsigned =
- hkdf.extended_unsigned_metadata_key_hmac_key().calculate_hmac(metadata_key.as_slice());
- let signed =
- hkdf.extended_signed_metadata_key_hmac_key().calculate_hmac(metadata_key.as_slice());
- Self {
- hkdf: *hkdf_key_seed,
- expected_unsigned_metadata_key_hmac: unsigned,
- expected_signed_metadata_key_hmac: signed,
- pub_key: pub_key.to_bytes(),
- }
- }
-}
-
-impl HkdfCryptoMaterial {
- fn hkdf<C: CryptoProvider>(&self) -> NpKeySeedHkdf<C> {
- NpKeySeedHkdf::<C>::new(&self.hkdf)
- }
-}
-
-impl V1CryptoMaterial for HkdfCryptoMaterial {
- type SignedIdentityResolverReference<'a> = SignedSectionIdentityResolutionMaterial
- where Self: 'a;
- type UnsignedIdentityResolverReference<'a> = UnsignedSectionIdentityResolutionMaterial
- where Self: 'a;
-
- fn signed_identity_resolution_material<C: CryptoProvider>(
- &self,
- ) -> Self::SignedIdentityResolverReference<'_> {
- SignedSectionIdentityResolutionMaterial::from_hkdf_and_expected_metadata_key_hmac::<C>(
- &self.hkdf::<C>(),
- self.expected_signed_metadata_key_hmac,
- )
- }
- fn unsigned_identity_resolution_material<C: CryptoProvider>(
- &self,
- ) -> Self::UnsignedIdentityResolverReference<'_> {
- UnsignedSectionIdentityResolutionMaterial::from_hkdf_and_expected_metadata_key_hmac::<C>(
- &self.hkdf::<C>(),
- self.expected_unsigned_metadata_key_hmac,
- )
- }
- fn signed_verification_material<C: CryptoProvider>(&self) -> SignedSectionVerificationMaterial {
- SignedSectionVerificationMaterial { pub_key: self.pub_key }
- }
-
- fn unsigned_verification_material<C: CryptoProvider>(
- &self,
- ) -> UnsignedSectionVerificationMaterial {
- let mic_hmac_key = *UnsignedSectionKeys::hmac_key(&self.hkdf::<C>()).as_bytes();
- UnsignedSectionVerificationMaterial { mic_hmac_key }
- }
-}
-
-pub(crate) trait IntermediateSectionExt {
+pub(crate) trait IntermediateSectionExt<'adv> {
/// Returns `Some` if `self` is `Plaintext`
- fn as_plaintext(&self) -> Option<&PlaintextSection>;
+ fn as_plaintext(&self) -> Option<&PlaintextSection<'adv>>;
/// Returns `Some` if `self` is `Ciphertext`
- fn as_ciphertext(&self) -> Option<&CiphertextSection>;
+ fn as_ciphertext(&self) -> Option<&CiphertextSection<'adv>>;
}
-impl<'a> IntermediateSectionExt for IntermediateSection<'a> {
- fn as_plaintext(&self) -> Option<&PlaintextSection> {
+impl<'adv> IntermediateSectionExt<'adv> for IntermediateSection<'adv> {
+ fn as_plaintext(&self) -> Option<&PlaintextSection<'adv>> {
match self {
IntermediateSection::Plaintext(s) => Some(s),
IntermediateSection::Ciphertext(_) => None,
}
}
- fn as_ciphertext(&self) -> Option<&CiphertextSection> {
+ fn as_ciphertext(&self) -> Option<&CiphertextSection<'adv>> {
match self {
IntermediateSection::Plaintext(_) => None,
IntermediateSection::Ciphertext(s) => Some(s),
diff --git a/nearby/presence/np_adv/src/extended/mod.rs b/nearby/presence/np_adv/src/extended/mod.rs
index 5d18daa..68b16c3 100644
--- a/nearby/presence/np_adv/src/extended/mod.rs
+++ b/nearby/presence/np_adv/src/extended/mod.rs
@@ -56,8 +56,8 @@ impl DeLength {
/// A convenient constant for zero length.
pub const ZERO: DeLength = DeLength { len: 0 };
- fn as_usize(&self) -> usize {
- self.len as usize
+ fn as_u8(&self) -> u8 {
+ self.len
}
}
@@ -65,7 +65,7 @@ impl TryFrom<u8> for DeLength {
type Error = DeLengthOutOfRange;
fn try_from(value: u8) -> Result<Self, Self::Error> {
- if value as usize <= MAX_DE_LEN {
+ if usize::from(value) <= MAX_DE_LEN {
Ok(Self { len: value })
} else {
Err(DeLengthOutOfRange {})
diff --git a/nearby/presence/np_adv/src/extended/section_signature_payload.rs b/nearby/presence/np_adv/src/extended/section_signature_payload.rs
index a89843b..d8ec93c 100644
--- a/nearby/presence/np_adv/src/extended/section_signature_payload.rs
+++ b/nearby/presence/np_adv/src/extended/section_signature_payload.rs
@@ -16,7 +16,8 @@
//! after the included context bytes, and utilities for
//! performing signatures and signature verification.
-use crate::extended::{deserialize::EncryptionInfo, METADATA_KEY_LEN};
+use crate::extended::deserialize::EncryptionInfo;
+use crate::MetadataKey;
use crate::NP_SVC_UUID;
use crypto_provider::{aes::ctr::AesCtrNonce, CryptoProvider};
use sink::{Sink, SinkWriter};
@@ -48,7 +49,7 @@ enum AfterIVInfo<'a> {
/// Plaintext identity DE header followed by the metadata key,
/// then the rest of the section plaintext (including
/// the plaintext identity DE payload).
- IdentityHeaderMetadataKeyAndRemainder([u8; 2], [u8; METADATA_KEY_LEN], &'a [u8]),
+ IdentityHeaderMetadataKeyAndRemainder([u8; 2], MetadataKey, &'a [u8]),
}
const ADV_SIGNATURE_CONTEXT: np_ed25519::SignatureContext = {
@@ -67,7 +68,7 @@ impl<'a> SectionSignaturePayload<'a> {
encryption_info: &'a [u8; EncryptionInfo::TOTAL_DE_LEN],
nonce_ref: &'a AesCtrNonce,
identity_header: [u8; 2],
- plaintext_metadata_key: [u8; METADATA_KEY_LEN],
+ plaintext_metadata_key: MetadataKey,
raw_plaintext_remainder: &'a [u8],
) -> Self {
Self {
@@ -141,7 +142,7 @@ impl<'a> SinkWriter for SectionSignaturePayload<'a> {
remainder,
) => {
sink.try_extend_from_slice(&identity_header)?;
- sink.try_extend_from_slice(&metadata_key)?;
+ sink.try_extend_from_slice(&metadata_key.0)?;
sink.try_extend_from_slice(remainder)
}
}
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 c3b9ed3..9d82273 100644
--- a/nearby/presence/np_adv/src/extended/serialize/mod.rs
+++ b/nearby/presence/np_adv/src/extended/serialize/mod.rs
@@ -54,8 +54,10 @@
//!
//! ```
//! use np_adv::{
+//! credential::{SimpleBroadcastCryptoMaterial, v1::V1},
//! de_type::EncryptedIdentityDataElementType,
//! extended::{data_elements::*, serialize::*, de_type::DeType },
+//! MetadataKey,
//! };
//! use rand::{Rng as _, SeedableRng as _};
//! use crypto_provider::{CryptoProvider, CryptoRng};
@@ -67,15 +69,20 @@
//! // these would come from the credential//!
//! let mut rng = <CryptoProviderImpl as CryptoProvider>::CryptoRng::new();
//! let metadata_key: [u8; 16] = rng.gen();
+//! let metadata_key = MetadataKey(metadata_key);
//! let key_seed: [u8; 32] = rng.gen();
//! // use your preferred crypto impl
//! let key_seed_hkdf = np_hkdf::NpKeySeedHkdf::<CryptoProviderImpl>::new(&key_seed);
//!
-//! let mut section_builder = adv_builder.section_builder(MicEncryptedSectionEncoder::new_random_salt(
+//! let broadcast_cm = SimpleBroadcastCryptoMaterial::<V1>::new(
+//! key_seed,
+//! metadata_key,
+//! );
+//!
+//! let mut section_builder = adv_builder.section_builder(MicEncryptedSectionEncoder::<CryptoProviderImpl>::new_random_salt(
//! &mut rng,
//! EncryptedIdentityDataElementType::Private,
-//! &metadata_key,
-//! &key_seed_hkdf,
+//! &broadcast_cm,
//! )).unwrap();
//!
//! section_builder.add_de(|_salt| TxPowerDataElement::from(TxPower::try_from(3).unwrap())).unwrap();
@@ -106,6 +113,10 @@
//! ```
use crate::extended::{NP_V1_ADV_MAX_ENCRYPTED_SECTION_COUNT, NP_V1_ADV_MAX_PUBLIC_SECTION_COUNT};
use crate::{
+ credential::{
+ v1::{SignedBroadcastCryptoMaterial, V1},
+ BroadcastCryptoMaterial,
+ },
de_type::{EncryptedIdentityDataElementType, IdentityDataElementType},
extended::{
data_elements::EncryptionInfoDataElement,
@@ -114,11 +125,10 @@ use crate::{
section_signature_payload::*,
to_array_view, DeLength, BLE_ADV_SVC_CONTENT_LEN, NP_ADV_MAX_SECTION_LEN,
},
- DeLengthOutOfRange, NP_SVC_UUID,
+ DeLengthOutOfRange, MetadataKey, NP_SVC_UUID,
};
use array_view::ArrayView;
-use core::fmt;
-use core::marker::PhantomData;
+use core::fmt::{self, Display};
use crypto_provider::{
aes::{
ctr::{AesCtr, AesCtrNonce, NonceAndCounter},
@@ -151,6 +161,12 @@ pub struct AdvBuilder {
advertisement_type: AdvertisementType,
}
+impl AsMut<AdvBuilder> for AdvBuilder {
+ fn as_mut(&mut self) -> &mut AdvBuilder {
+ self
+ }
+}
+
impl AdvBuilder {
/// Build an [AdvBuilder].
pub fn new(advertisement_type: AdvertisementType) -> Self {
@@ -160,17 +176,10 @@ impl AdvBuilder {
Self { adv, section_count: 0, advertisement_type }
}
- /// Create a section builder.
- ///
- /// The builder will not accept more DEs than can fit given the space already used in the
- /// advertisement by previous sections, if any.
- ///
- /// Once the builder is populated, add it to the originating advertisement with
- /// [SectionBuilder.add_to_advertisement].
- pub fn section_builder<SE: SectionEncoder>(
- &mut self,
- section_encoder: SE,
- ) -> Result<SectionBuilder<SE>, AddSectionError> {
+ fn prepare_section_builder_buffer_and_de_offset<SE: SectionEncoder>(
+ &self,
+ ) -> Result<(CapacityLimitedVec<u8, NP_ADV_MAX_SECTION_LEN>, DataElementOffset), AddSectionError>
+ {
// section header and identity prefix
let prefix_len = 1 + SE::PREFIX_LEN;
let minimum_section_len = prefix_len + SE::SUFFIX_LEN;
@@ -189,20 +198,53 @@ impl AdvBuilder {
return Err(AddSectionError::IncompatibleSectionType);
}
- let mut section = tinyvec::ArrayVec::new();
+ let mut section: tinyvec::ArrayVec<[u8; 249]> = tinyvec::ArrayVec::new();
// placeholder for section header and identity prefix
section.resize(prefix_len, 0);
- Ok(SectionBuilder {
- section: CapacityLimitedVec {
- vec: section,
- // won't underflow: checked above
- capacity: available_len - SE::SUFFIX_LEN,
- },
- section_encoder,
- adv_builder: self,
- next_de_offset: SE::INITIAL_DE_OFFSET,
- })
+ let section = CapacityLimitedVec {
+ vec: section,
+ // won't underflow: checked above
+ capacity: available_len - SE::SUFFIX_LEN,
+ };
+ let next_de_offset = SE::INITIAL_DE_OFFSET;
+ Ok((section, next_de_offset))
+ }
+
+ /// Create a section builder whose contents may be added to this advertisement.
+ ///
+ /// The builder will not accept more DEs than can fit given the space already used in the
+ /// advertisement by previous sections, if any.
+ ///
+ /// Once the builder is populated, add it to the originating advertisement with
+ /// [SectionBuilder.add_to_advertisement].
+ pub fn section_builder<SE: SectionEncoder>(
+ &mut self,
+ section_encoder: SE,
+ ) -> Result<SectionBuilder<&mut AdvBuilder, SE>, AddSectionError> {
+ let (section, next_de_offset) =
+ self.prepare_section_builder_buffer_and_de_offset::<SE>()?;
+
+ Ok(SectionBuilder { section, section_encoder, adv_builder: self, next_de_offset })
+ }
+
+ /// Create a section builder which actually takes ownership of this advertisement builder.
+ ///
+ /// This is unlike `AdvertisementBuilder#section_builder` in that the returned section
+ /// builder will take ownership of this advertisement builder, if the operation was
+ /// successful. Otherwise, this advertisement builder will be returned back to the
+ /// caller unaltered as part of the `Err` arm.
+ #[allow(clippy::result_large_err)]
+ pub fn into_section_builder<SE: SectionEncoder>(
+ self,
+ section_encoder: SE,
+ ) -> Result<SectionBuilder<AdvBuilder, SE>, (AdvBuilder, AddSectionError)> {
+ match self.prepare_section_builder_buffer_and_de_offset::<SE>() {
+ Ok((section, next_de_offset)) => {
+ Ok(SectionBuilder { section, section_encoder, adv_builder: self, next_de_offset })
+ }
+ Err(err) => Err((self, err)),
+ }
}
/// Convert the builder into an encoded advertisement.
@@ -210,6 +252,12 @@ impl AdvBuilder {
EncodedAdvertisement { adv: to_array_view(self.adv) }
}
+ /// Gets the current number of sections added to this advertisement
+ /// builder, not counting any outstanding SectionBuilders.
+ pub fn section_count(&self) -> usize {
+ self.section_count
+ }
+
/// Add the section, which must have come from a SectionBuilder generated from this, into this
/// advertisement.
fn add_section(&mut self, section: EncodedSection) {
@@ -235,6 +283,22 @@ pub enum AddSectionError {
IncompatibleSectionType,
}
+impl Display for AddSectionError {
+ fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+ match self {
+ AddSectionError::InsufficientAdvSpace => {
+ write!(f, "The advertisement (max {BLE_ADV_SVC_CONTENT_LEN} bytes) doesn't have enough remaining space to hold the section")
+ }
+ AddSectionError::MaxSectionCountExceeded => {
+ write!(f, "The advertisement can only hold a maximum of {NP_V1_ADV_MAX_ENCRYPTED_SECTION_COUNT} number of sections")
+ }
+ AddSectionError::IncompatibleSectionType => {
+ write!(f, "Public and Encrypted sections cannot be mixed in the same advertisement")
+ }
+ }
+ }
+}
+
/// An encoded NP V1 advertisement, starting with the NP advertisement header byte.
#[derive(Debug, PartialEq, Eq)]
pub struct EncodedAdvertisement {
@@ -253,24 +317,56 @@ type EncodedSection = ArrayView<u8, NP_ADV_MAX_SECTION_LEN>;
/// Accumulates data elements and encodes them into a section.
#[derive(Debug)]
-pub struct SectionBuilder<'a, SE: SectionEncoder> {
+pub struct SectionBuilder<R: AsMut<AdvBuilder>, SE: SectionEncoder> {
/// Contains the section header, the identity-specified overhead, and any DEs added
pub(crate) section: CapacityLimitedVec<u8, NP_ADV_MAX_SECTION_LEN>,
section_encoder: SE,
- /// mut ref to enforce only one active section builder at a time
- adv_builder: &'a mut AdvBuilder,
+ /// mut ref-able to enforce only one active section builder at a time
+ adv_builder: R,
next_de_offset: DataElementOffset,
}
-impl<'a, SE: SectionEncoder> SectionBuilder<'a, SE> {
+impl<'a, SE: SectionEncoder> SectionBuilder<&'a mut AdvBuilder, SE> {
/// Add this builder to the advertisement that created it.
pub fn add_to_advertisement(self) {
- let adv_builder = self.adv_builder;
+ let _ = self.add_to_advertisement_internal();
+ }
+}
+
+impl<SE: SectionEncoder> SectionBuilder<AdvBuilder, SE> {
+ /// Gets the 0-based index of the section currently under construction
+ /// in the context of the containing advertisement.
+ pub fn section_index(&self) -> usize {
+ self.adv_builder.section_count()
+ }
+ /// Add this builder to the advertisement that created it,
+ /// and returns the containing advertisement back to the caller.
+ pub fn add_to_advertisement(self) -> AdvBuilder {
+ self.add_to_advertisement_internal()
+ }
+}
+
+impl<R: AsMut<AdvBuilder>, SE: SectionEncoder> SectionBuilder<R, SE> {
+ /// Add this builder to the advertisement that created it.
+ /// Returns the mut-refable to the advertisement builder
+ /// which the contents of this section builder were added to.
+ fn add_to_advertisement_internal(mut self) -> R {
+ let adv_builder = self.adv_builder.as_mut();
+ let adv_builder_header_byte = adv_builder.header_byte();
adv_builder.add_section(Self::build_section(
+ adv_builder_header_byte,
self.section.into_inner(),
self.section_encoder,
- adv_builder,
- ))
+ ));
+ self.adv_builder
+ }
+
+ /// Gets the derived salt which will be employed for the next DE offset.
+ ///
+ /// Suitable for scenarios (like FFI) where a closure would be inappropriate
+ /// for DE construction, and interaction with the client is preferred.
+ pub fn next_de_salt(&self) -> SE::DerivedSalt {
+ self.section_encoder.de_salt(self.next_de_offset)
}
/// Add a data element to the section with a closure that returns a `Result`.
@@ -280,8 +376,7 @@ impl<'a, SE: SectionEncoder> SectionBuilder<'a, SE> {
&mut self,
build_de: F,
) -> Result<(), AddDataElementError<E>> {
- let writer = build_de(self.section_encoder.de_salt(self.next_de_offset))
- .map_err(AddDataElementError::BuildDeError)?;
+ let writer = build_de(self.next_de_salt()).map_err(AddDataElementError::BuildDeError)?;
let orig_len = self.section.len();
// since we own the writer, and it's immutable, no race risk writing header w/ len then
@@ -304,10 +399,10 @@ impl<'a, SE: SectionEncoder> SectionBuilder<'a, SE> {
e
})?;
- if content_len != de_header.len.as_usize() {
+ if content_len != usize::from(de_header.len.as_u8()) {
panic!(
"Buggy WriteDataElement impl: header len {}, actual written len {}",
- de_header.len.as_usize(),
+ de_header.len.as_u8(),
content_len
);
}
@@ -331,9 +426,9 @@ impl<'a, SE: SectionEncoder> SectionBuilder<'a, SE> {
///
/// Implemented without self to avoid partial-move issues.
fn build_section(
+ adv_builder_header_byte: u8,
mut section_contents: tinyvec::ArrayVec<[u8; NP_ADV_MAX_SECTION_LEN]>,
mut section_encoder: SE,
- adv_builder: &AdvBuilder,
) -> EncodedSection {
// there is space because the capacity for DEs was restricted to allow it
section_contents.resize(section_contents.len() + SE::SUFFIX_LEN, 0);
@@ -346,7 +441,7 @@ impl<'a, SE: SectionEncoder> SectionBuilder<'a, SE> {
.expect("section length is always <=255 and non-negative");
section_encoder.postprocess(
- adv_builder.header_byte(),
+ adv_builder_header_byte,
section_contents[0],
&mut section_contents[1..],
);
@@ -437,48 +532,44 @@ impl SectionEncoder for PublicSectionEncoder {
/// Encrypts the data elements and protects integrity with an np_ed25519 signature
/// using key material derived from an NP identity.
-pub struct SignedEncryptedSectionEncoder<'a, C: CryptoProvider> {
+pub struct SignedEncryptedSectionEncoder<C: CryptoProvider> {
identity_type: EncryptedIdentityDataElementType,
salt: V1Salt<C>,
- metadata_key: &'a [u8; 16],
- key_pair: &'a np_ed25519::KeyPair<C>,
+ metadata_key: MetadataKey,
+ key_pair: np_ed25519::KeyPair<C>,
aes_key: Aes128Key,
- _marker: PhantomData<C>,
}
-impl<'a, C: CryptoProvider> SignedEncryptedSectionEncoder<'a, C> {
- /// Build a [SignedEncryptedSectionEncoder] from the provided identity material with a random salt.
- pub fn new_random_salt(
+impl<C: CryptoProvider> SignedEncryptedSectionEncoder<C> {
+ /// Build a [SignedEncryptedSectionEncoder] from an identity type,
+ /// some broadcast crypto-material, and with a random salt.
+ pub fn new_random_salt<B: SignedBroadcastCryptoMaterial>(
rng: &mut C::CryptoRng,
identity_type: EncryptedIdentityDataElementType,
- metadata_key: &'a [u8; 16],
- key_pair: &'a np_ed25519::KeyPair<C>,
- key_seed_hkdf: &np_hkdf::NpKeySeedHkdf<C>,
+ crypto_material: &B,
) -> Self {
let salt: V1Salt<C> = rng.gen::<[u8; 16]>().into();
- Self::new(identity_type, salt, metadata_key, key_pair, key_seed_hkdf)
+ Self::new(identity_type, salt, crypto_material)
}
- /// Build a [SignedEncryptedSectionEncoder] from the provided identity material.
- pub(crate) fn new(
+ /// Build a [SignedEncryptedSectionEncoder] from an identity type,
+ /// a provided salt, and some broadcast crypto-material.
+ pub(crate) fn new<B: SignedBroadcastCryptoMaterial>(
identity_type: EncryptedIdentityDataElementType,
salt: V1Salt<C>,
- metadata_key: &'a [u8; 16],
- key_pair: &'a np_ed25519::KeyPair<C>,
- key_seed_hkdf: &np_hkdf::NpKeySeedHkdf<C>,
+ crypto_material: &B,
) -> Self {
- Self {
- identity_type,
- salt,
- metadata_key,
- key_pair,
- aes_key: key_seed_hkdf.extended_signed_section_aes_key(),
- _marker: Default::default(),
- }
+ let metadata_key = crypto_material.metadata_key();
+ let key_seed = crypto_material.key_seed();
+ let key_seed_hkdf = np_hkdf::NpKeySeedHkdf::<C>::new(&key_seed);
+ let private_key = crypto_material.signing_key();
+ let key_pair = np_ed25519::KeyPair::<C>::from_private_key(&private_key);
+ let aes_key = key_seed_hkdf.extended_signed_section_aes_key();
+ Self { identity_type, salt, metadata_key, key_pair, aes_key }
}
}
-impl<'a, C: CryptoProvider> SectionEncoder for SignedEncryptedSectionEncoder<'a, C> {
+impl<C: CryptoProvider> SectionEncoder for SignedEncryptedSectionEncoder<C> {
const PREFIX_LEN: usize =
EncryptionInfo::TOTAL_DE_LEN + EncryptedIdentityMetadata::TOTAL_DE_LEN;
/// Ed25519 signature
@@ -502,7 +593,7 @@ impl<'a, C: CryptoProvider> SectionEncoder for SignedEncryptedSectionEncoder<'a,
let identity_header = identity_de_header(self.identity_type, self.metadata_key);
section_contents[19..21].copy_from_slice(identity_header.serialize().as_slice());
- section_contents[21..37].copy_from_slice(self.metadata_key);
+ section_contents[21..37].copy_from_slice(&self.metadata_key.0);
let nonce: AesCtrNonce = self
.de_salt(v1_salt::DataElementOffset::from(1))
@@ -515,7 +606,7 @@ impl<'a, C: CryptoProvider> SectionEncoder for SignedEncryptedSectionEncoder<'a,
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 +
@@ -531,14 +622,14 @@ impl<'a, C: CryptoProvider> SectionEncoder for SignedEncryptedSectionEncoder<'a,
after_encryption_info,
);
- let signature = section_signature_payload.sign(self.key_pair);
+ let signature = section_signature_payload.sign(&self.key_pair);
sig[0..64].copy_from_slice(&signature.to_bytes());
let mut cipher = C::AesCtr128::new(&self.aes_key, NonceAndCounter::from_nonce(nonce));
// encrypt just the part that should be ciphertext: identity DE contents and subsequent DEs
- cipher.encrypt(&mut section_contents[21..]);
+ cipher.apply_keystream(&mut section_contents[21..]);
}
type DerivedSalt = DeSalt<C>;
@@ -550,54 +641,55 @@ impl<'a, C: CryptoProvider> SectionEncoder for SignedEncryptedSectionEncoder<'a,
/// Encrypts the data elements and protects integrity with a MIC using key material derived from
/// an NP identity.
-pub struct MicEncryptedSectionEncoder<'a, C: CryptoProvider> {
+pub struct MicEncryptedSectionEncoder<C: CryptoProvider> {
identity_type: EncryptedIdentityDataElementType,
salt: V1Salt<C>,
- identity_payload: &'a [u8; 16],
+ metadata_key: MetadataKey,
aes_key: Aes128Key,
mic_hmac_key: np_hkdf::NpHmacSha256Key<C>,
}
-impl<'a, C: CryptoProvider> MicEncryptedSectionEncoder<'a, C> {
- /// Build a [MicEncryptedSectionEncoder] from the provided identity info with a random salt.
- pub fn new_random_salt(
+impl<C: CryptoProvider> MicEncryptedSectionEncoder<C> {
+ /// Build a [MicEncryptedSectionEncoder] from the provided identity
+ /// info with a random salt.
+ pub fn new_random_salt<B: BroadcastCryptoMaterial<V1>>(
rng: &mut C::CryptoRng,
identity_type: EncryptedIdentityDataElementType,
- identity_payload: &'a [u8; 16],
- keys: &impl np_hkdf::UnsignedSectionKeys<C>,
+ crypto_material: &B,
) -> Self {
let salt: V1Salt<C> = rng.gen::<[u8; 16]>().into();
- Self::new(identity_type, salt, identity_payload, keys)
+ Self::new(identity_type, salt, crypto_material)
}
+
/// Build a [MicEncryptedSectionEncoder] from the provided identity info.
- pub(crate) fn new(
+ pub(crate) fn new<B: BroadcastCryptoMaterial<V1>>(
identity_type: EncryptedIdentityDataElementType,
salt: V1Salt<C>,
- identity_payload: &'a [u8; 16],
- keys: &impl np_hkdf::UnsignedSectionKeys<C>,
+ crypto_material: &B,
) -> Self {
- MicEncryptedSectionEncoder {
- identity_type,
- salt,
- identity_payload,
- aes_key: keys.aes_key(),
- mic_hmac_key: keys.hmac_key(),
- }
+ let metadata_key = crypto_material.metadata_key();
+ let key_seed = crypto_material.key_seed();
+ let key_seed_hkdf = np_hkdf::NpKeySeedHkdf::<C>::new(&key_seed);
+ let aes_key = np_hkdf::UnsignedSectionKeys::aes_key(&key_seed_hkdf);
+ let mic_hmac_key = np_hkdf::UnsignedSectionKeys::hmac_key(&key_seed_hkdf);
+
+ Self { identity_type, salt, metadata_key, aes_key, mic_hmac_key }
}
- /// Build a [MicEncryptedSectionEncoder] from the provided identity info. Exposed outside of this crate for
- /// testing purposes only.
+
+ /// Build a [MicEncrypedSectionEncoder] from the provided identity info.
+ /// Exposed outside of this crate for testing purposes only, since this
+ /// does not handle the generation of random salts.
#[cfg(any(test, feature = "testing"))]
- pub fn new_for_testing(
+ pub fn new_for_testing<B: BroadcastCryptoMaterial<V1>>(
identity_type: EncryptedIdentityDataElementType,
salt: V1Salt<C>,
- identity_payload: &'a [u8; 16],
- keys: &impl np_hkdf::UnsignedSectionKeys<C>,
+ crypto_material: &B,
) -> Self {
- Self::new(identity_type, salt, identity_payload, keys)
+ Self::new(identity_type, salt, crypto_material)
}
}
-impl<'a, C: CryptoProvider> SectionEncoder for MicEncryptedSectionEncoder<'a, C> {
+impl<C: CryptoProvider> SectionEncoder for MicEncryptedSectionEncoder<C> {
const PREFIX_LEN: usize =
EncryptionInfo::TOTAL_DE_LEN + EncryptedIdentityMetadata::TOTAL_DE_LEN;
/// Length of mic
@@ -625,9 +717,9 @@ impl<'a, C: CryptoProvider> SectionEncoder for MicEncryptedSectionEncoder<'a, C>
.serialize();
section_contents[0..19].copy_from_slice(&encryption_info_bytes);
// Identity DE
- let identity_header = identity_de_header(self.identity_type, self.identity_payload);
+ let identity_header = identity_de_header(self.identity_type, self.metadata_key);
section_contents[19..21].copy_from_slice(identity_header.serialize().as_slice());
- section_contents[21..37].copy_from_slice(self.identity_payload);
+ section_contents[21..37].copy_from_slice(&self.metadata_key.0);
// DE offset for identity is 1: Encryption Info DE, Identity DE, then other DEs
let nonce: AesCtrNonce = self
.de_salt(v1_salt::DataElementOffset::from(1))
@@ -636,7 +728,7 @@ impl<'a, C: CryptoProvider> SectionEncoder for MicEncryptedSectionEncoder<'a, C>
let mut cipher = C::AesCtr128::new(&self.aes_key, NonceAndCounter::from_nonce(nonce));
let ciphertext_end = section_contents.len() - SectionMic::CONTENTS_LEN;
// encrypt just the part that should be ciphertext: identity DE contents and subsequent DEs
- cipher.encrypt(&mut section_contents[21..ciphertext_end]);
+ cipher.apply_keystream(&mut section_contents[21..ciphertext_end]);
// calculate MAC per the spec
let mut section_hmac = self.mic_hmac_key.build_hmac();
// svc uuid
@@ -777,11 +869,12 @@ impl DeHeader {
fn identity_de_header(
id_type: EncryptedIdentityDataElementType,
- metadata_key: &[u8; 16],
+ metadata_key: MetadataKey,
) -> DeHeader {
DeHeader {
de_type: id_type.type_code(),
len: metadata_key
+ .0
.len()
.try_into()
.map_err(|_e| DeLengthOutOfRange)
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 74a7eb0..3a6a5f9 100644
--- a/nearby/presence/np_adv/src/extended/serialize/section_tests.rs
+++ b/nearby/presence/np_adv/src/extended/serialize/section_tests.rs
@@ -12,9 +12,15 @@
// 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::credential::{
+ v1::{SimpleSignedBroadcastCryptoMaterial, V1},
+ SimpleBroadcastCryptoMaterial,
+};
use crate::extended::data_elements::GenericDataElement;
use crate::extended::serialize::AddSectionError::MaxSectionCountExceeded;
use crypto_provider::aes::ctr::AES_CTR_NONCE_LEN;
@@ -59,6 +65,7 @@ fn mic_encrypted_identity_section_random_des() {
.collect::<Vec<_>>();
let metadata_key = rng.gen();
+ let metadata_key = MetadataKey(metadata_key);
let key_seed = rng.gen();
let identity_type =
*EncryptedIdentityDataElementType::iter().collect::<Vec<_>>().choose(&mut rng).unwrap();
@@ -66,12 +73,13 @@ fn mic_encrypted_identity_section_random_des() {
let mut adv_builder = AdvBuilder::new(AdvertisementType::Encrypted);
+ let broadcast_cm = SimpleBroadcastCryptoMaterial::<V1>::new(key_seed, metadata_key);
+
let mut section_builder = adv_builder
- .section_builder(MicEncryptedSectionEncoder::new_random_salt(
+ .section_builder(MicEncryptedSectionEncoder::<CryptoProviderImpl>::new_random_salt(
&mut crypto_rng,
identity_type,
- &metadata_key,
- &key_seed_hkdf,
+ &broadcast_cm,
))
.unwrap();
let section_salt = V1Salt::<CryptoProviderImpl>::from(
@@ -86,9 +94,7 @@ fn mic_encrypted_identity_section_random_des() {
let section_length = 53
+ extra_des
.iter()
- .map(|de| {
- de.de_header().serialize().len() as u8 + de.de_header().len.as_usize() as u8
- })
+ .map(|de| de.de_header().serialize().len() as u8 + de.de_header().len.as_u8())
.sum::<u8>();
let encryption_info = [
@@ -121,14 +127,14 @@ fn mic_encrypted_identity_section_random_des() {
&np_hkdf::UnsignedSectionKeys::aes_key(&key_seed_hkdf),
NonceAndCounter::from_nonce(nonce),
);
- let mut plaintext = metadata_key.as_slice().to_vec();
+ let mut plaintext = metadata_key.0.as_slice().to_vec();
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.encrypt(&mut plaintext);
+ cipher.apply_keystream(&mut plaintext);
let ciphertext = plaintext;
hmac_input.extend_from_slice(&ciphertext);
@@ -160,7 +166,7 @@ fn signature_encrypted_identity_section_random_des() {
for _ in 0..1_000 {
let num_des = rng.gen_range(1..=5);
- let metadata_key = rng.gen();
+ let metadata_key = MetadataKey(rng.gen());
let key_seed = rng.gen();
let identity_type =
*EncryptedIdentityDataElementType::iter().collect::<Vec<_>>().choose(&mut rng).unwrap();
@@ -169,13 +175,17 @@ fn signature_encrypted_identity_section_random_des() {
let mut adv_builder = AdvBuilder::new(AdvertisementType::Encrypted);
let key_pair = KeyPair::generate();
+ let broadcast_cm = SimpleSignedBroadcastCryptoMaterial::new(
+ key_seed,
+ metadata_key,
+ key_pair.private_key(),
+ );
+
let mut section_builder = adv_builder
- .section_builder(SignedEncryptedSectionEncoder::new_random_salt(
+ .section_builder(SignedEncryptedSectionEncoder::<CryptoProviderImpl>::new_random_salt(
&mut crypto_rng,
identity_type,
- &metadata_key,
- &key_pair,
- &key_seed_hkdf,
+ &broadcast_cm,
))
.unwrap();
let section_salt = V1Salt::<CryptoProviderImpl>::from(
@@ -200,9 +210,7 @@ fn signature_encrypted_identity_section_random_des() {
+ 64
+ extra_des
.iter()
- .map(|de| {
- de.de_header().serialize().len() as u8 + de.de_header().len.as_usize() as u8
- })
+ .map(|de| de.de_header().serialize().len() as u8 + de.de_header().len.as_u8())
.sum::<u8>();
let encryption_info = [
@@ -226,7 +234,7 @@ fn signature_encrypted_identity_section_random_des() {
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(
@@ -239,7 +247,7 @@ fn signature_encrypted_identity_section_random_des() {
&section_body,
);
- let mut plaintext = metadata_key.as_slice().to_vec();
+ let mut plaintext = metadata_key.0.as_slice().to_vec();
plaintext.extend_from_slice(section_body.as_slice());
plaintext.extend_from_slice(&sig_payload.sign(&key_pair).to_bytes());
@@ -248,7 +256,7 @@ fn signature_encrypted_identity_section_random_des() {
&key_seed_hkdf.extended_signed_section_aes_key(),
NonceAndCounter::from_nonce(nonce),
)
- .encrypt(&mut plaintext);
+ .apply_keystream(&mut plaintext);
let ciphertext = plaintext;
let mut expected = vec![section_length];
@@ -268,13 +276,15 @@ fn section_builder_too_full_doesnt_advance_de_index() {
let key_seed = [22; 32];
let key_seed_hkdf = np_hkdf::NpKeySeedHkdf::<CryptoProviderImpl>::new(&key_seed);
- let metadata_key = [33; 16];
+ let metadata_key = MetadataKey([33; 16]);
+
+ let broadcast_cm = SimpleBroadcastCryptoMaterial::<V1>::new(key_seed, metadata_key);
+
let mut section_builder = adv_builder
- .section_builder(MicEncryptedSectionEncoder::new_random_salt(
+ .section_builder(MicEncryptedSectionEncoder::<CryptoProviderImpl>::new_random_salt(
&mut crypto_rng,
EncryptedIdentityDataElementType::Trusted,
- &metadata_key,
- &key_seed_hkdf,
+ &broadcast_cm,
))
.unwrap();
let salt =
@@ -309,7 +319,7 @@ fn section_builder_too_full_doesnt_advance_de_index() {
let mut expected = vec![];
// metadata key
- expected.extend_from_slice(&metadata_key);
+ expected.extend_from_slice(&metadata_key.0);
// de header
expected.extend_from_slice(&[0x80 + 100, 100]);
// section 0 de 2
@@ -324,7 +334,7 @@ fn section_builder_too_full_doesnt_advance_de_index() {
NonceAndCounter::from_nonce(salt.derive(Some(1.into())).unwrap()),
);
- cipher.encrypt(&mut expected);
+ cipher.apply_keystream(&mut expected);
let adv_bytes = adv_builder.into_advertisement();
// ignoring the MIC, etc, since that's tested elsewhere
@@ -363,13 +373,15 @@ fn section_builder_build_de_error_doesnt_advance_de_index() {
let key_seed = [22; 32];
let key_seed_hkdf = np_hkdf::NpKeySeedHkdf::<CryptoProviderImpl>::new(&key_seed);
- let metadata_key = [33; 16];
+ let metadata_key = MetadataKey([33; 16]);
+
+ let broadcast_cm = SimpleBroadcastCryptoMaterial::<V1>::new(key_seed, metadata_key);
+
let mut section_builder = adv_builder
- .section_builder(MicEncryptedSectionEncoder::new_random_salt(
+ .section_builder(MicEncryptedSectionEncoder::<CryptoProviderImpl>::new_random_salt(
&mut crypto_rng,
EncryptedIdentityDataElementType::Trusted,
- &metadata_key,
- &key_seed_hkdf,
+ &broadcast_cm,
))
.unwrap();
let salt =
@@ -398,7 +410,7 @@ fn section_builder_build_de_error_doesnt_advance_de_index() {
let mut expected = vec![];
// metadata key
- expected.extend_from_slice(&metadata_key);
+ expected.extend_from_slice(&metadata_key.0);
// de header
expected.extend_from_slice(&[0x80 + 100, 100]);
// section 0 de 2
@@ -413,7 +425,7 @@ fn section_builder_build_de_error_doesnt_advance_de_index() {
NonceAndCounter::from_nonce(salt.derive(Some(1.into())).unwrap()),
);
- cipher.encrypt(&mut expected);
+ cipher.apply_keystream(&mut expected);
let adv_bytes = adv_builder.into_advertisement();
// ignoring the MIC, etc, since that's tested elsewhere
@@ -429,13 +441,15 @@ fn add_multiple_de_correct_de_offsets_mic_encrypted_identity() {
let key_seed = [22; 32];
let key_seed_hkdf = np_hkdf::NpKeySeedHkdf::<CryptoProviderImpl>::new(&key_seed);
- let metadata_key = [33; 16];
+ let metadata_key = MetadataKey([33; 16]);
+
+ let broadcast_cm = SimpleBroadcastCryptoMaterial::<V1>::new(key_seed, metadata_key);
+
let mut section_builder = adv_builder
- .section_builder(MicEncryptedSectionEncoder::new_random_salt(
+ .section_builder(MicEncryptedSectionEncoder::<CryptoProviderImpl>::new_random_salt(
&mut crypto_rng,
EncryptedIdentityDataElementType::Trusted,
- &metadata_key,
- &key_seed_hkdf,
+ &broadcast_cm,
))
.unwrap();
let salt =
@@ -458,7 +472,7 @@ fn add_multiple_de_correct_de_offsets_mic_encrypted_identity() {
let mut expected = vec![];
// metadata key
- expected.extend_from_slice(&metadata_key);
+ expected.extend_from_slice(&metadata_key.0);
// de header
expected.extend_from_slice(&[0x90, 0x40]);
// section 0 de 2
@@ -473,7 +487,7 @@ fn add_multiple_de_correct_de_offsets_mic_encrypted_identity() {
NonceAndCounter::from_nonce(salt.derive(Some(1.into())).unwrap()),
);
- cipher.encrypt(&mut expected);
+ cipher.apply_keystream(&mut expected);
let adv_bytes = adv_builder.into_advertisement();
// ignoring the MIC, etc, since that's tested elsewhere
@@ -489,15 +503,17 @@ fn add_multiple_de_correct_de_offsets_signature_encrypted_identity() {
let key_seed = [22; 32];
let key_seed_hkdf = np_hkdf::NpKeySeedHkdf::<CryptoProviderImpl>::new(&key_seed);
- let metadata_key = [33; 16];
+ let metadata_key = MetadataKey([33; 16]);
let key_pair = KeyPair::generate();
+
+ let broadcast_cm =
+ SimpleSignedBroadcastCryptoMaterial::new(key_seed, metadata_key, key_pair.private_key());
+
let mut section_builder = adv_builder
- .section_builder(SignedEncryptedSectionEncoder::new_random_salt(
+ .section_builder(SignedEncryptedSectionEncoder::<CryptoProviderImpl>::new_random_salt(
&mut crypto_rng,
EncryptedIdentityDataElementType::Trusted,
- &metadata_key,
- &key_pair,
- &key_seed_hkdf,
+ &broadcast_cm,
))
.unwrap();
let salt =
@@ -520,7 +536,7 @@ fn add_multiple_de_correct_de_offsets_signature_encrypted_identity() {
let mut expected = vec![];
// metadata key
- expected.extend_from_slice(&metadata_key);
+ expected.extend_from_slice(&metadata_key.0);
// de header
expected.extend_from_slice(&[0x90, 0x40]);
// section 0 de 2
@@ -534,7 +550,7 @@ fn add_multiple_de_correct_de_offsets_signature_encrypted_identity() {
&key_seed_hkdf.extended_signed_section_aes_key(),
NonceAndCounter::from_nonce(salt.derive(Some(1.into())).unwrap()),
)
- .encrypt(&mut expected);
+ .apply_keystream(&mut expected);
let adv_bytes = adv_builder.into_advertisement();
// ignoring the signature since that's tested elsewhere
@@ -552,16 +568,17 @@ fn signature_encrypted_section_de_lengths_allow_room_for_suffix() {
let mut adv_builder = AdvBuilder::new(AdvertisementType::Encrypted);
let key_seed = [22; 32];
- let key_seed_hkdf = np_hkdf::NpKeySeedHkdf::<CryptoProviderImpl>::new(&key_seed);
- let metadata_key = [33; 16];
+ let metadata_key = MetadataKey([33; 16]);
let key_pair = KeyPair::generate();
+
+ let broadcast_cm =
+ SimpleSignedBroadcastCryptoMaterial::new(key_seed, metadata_key, key_pair.private_key());
+
let mut section_builder = adv_builder
- .section_builder(SignedEncryptedSectionEncoder::new_random_salt(
+ .section_builder(SignedEncryptedSectionEncoder::<CryptoProviderImpl>::new_random_salt(
&mut crypto_rng,
EncryptedIdentityDataElementType::Trusted,
- &metadata_key,
- &key_pair,
- &key_seed_hkdf,
+ &broadcast_cm,
))
.unwrap();
@@ -614,21 +631,22 @@ fn serialize_max_number_of_public_sections() {
}
fn do_mic_encrypted_identity_fixed_key_material_test<W: WriteDataElement>(extra_des: &[W]) {
- let metadata_key = [1; 16];
+ let metadata_key = MetadataKey([1; 16]);
let key_seed = [2; 32];
let adv_header_byte = 0b00100000;
let section_salt: V1Salt<CryptoProviderImpl> = [3; 16].into();
let identity_type = EncryptedIdentityDataElementType::Private;
let key_seed_hkdf = np_hkdf::NpKeySeedHkdf::<CryptoProviderImpl>::new(&key_seed);
+ let broadcast_cm = SimpleBroadcastCryptoMaterial::<V1>::new(key_seed, metadata_key);
+
let mut adv_builder = AdvBuilder::new(AdvertisementType::Encrypted);
let mut section_builder = adv_builder
- .section_builder(MicEncryptedSectionEncoder::new(
+ .section_builder(MicEncryptedSectionEncoder::<CryptoProviderImpl>::new(
identity_type,
V1Salt::from(*section_salt.as_array_ref()),
- &metadata_key,
- &key_seed_hkdf,
+ &broadcast_cm,
))
.unwrap();
@@ -640,7 +658,7 @@ fn do_mic_encrypted_identity_fixed_key_material_test<W: WriteDataElement>(extra_
let section_length = 53
+ extra_des
.iter()
- .map(|de| de.de_header().serialize().len() as u8 + de.de_header().len.as_usize() as u8)
+ .map(|de| de.de_header().serialize().len() as u8 + de.de_header().len.as_u8())
.sum::<u8>();
let encryption_info = [
@@ -672,14 +690,14 @@ fn do_mic_encrypted_identity_fixed_key_material_test<W: WriteDataElement>(extra_
&np_hkdf::UnsignedSectionKeys::aes_key(&key_seed_hkdf),
NonceAndCounter::from_nonce(nonce),
);
- let mut plaintext = metadata_key.as_slice().to_vec();
+ let mut plaintext = metadata_key.0.as_slice().to_vec();
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.encrypt(&mut plaintext);
+ cipher.apply_keystream(&mut plaintext);
let ciphertext = plaintext;
hmac_input.extend_from_slice(&ciphertext);
@@ -698,7 +716,7 @@ fn do_mic_encrypted_identity_fixed_key_material_test<W: WriteDataElement>(extra_
}
fn do_signature_encrypted_identity_fixed_key_material_test<W: WriteDataElement>(extra_des: &[W]) {
- let metadata_key = [1; 16];
+ let metadata_key = MetadataKey([1; 16]);
let key_seed = [2; 32];
let adv_header_byte = 0b00100000;
let section_salt: V1Salt<CryptoProviderImpl> = [3; 16].into();
@@ -706,15 +724,16 @@ fn do_signature_encrypted_identity_fixed_key_material_test<W: WriteDataElement>(
let key_seed_hkdf = np_hkdf::NpKeySeedHkdf::<CryptoProviderImpl>::new(&key_seed);
let key_pair = KeyPair::generate();
+ let broadcast_cm =
+ SimpleSignedBroadcastCryptoMaterial::new(key_seed, metadata_key, key_pair.private_key());
+
let mut adv_builder = AdvBuilder::new(AdvertisementType::Encrypted);
let mut section_builder = adv_builder
- .section_builder(SignedEncryptedSectionEncoder::new(
+ .section_builder(SignedEncryptedSectionEncoder::<CryptoProviderImpl>::new(
identity_type,
V1Salt::from(*section_salt.as_array_ref()),
- &metadata_key,
- &key_pair,
- &key_seed_hkdf,
+ &broadcast_cm,
))
.unwrap();
@@ -728,7 +747,7 @@ fn do_signature_encrypted_identity_fixed_key_material_test<W: WriteDataElement>(
+ 64
+ extra_des
.iter()
- .map(|de| de.de_header().serialize().len() as u8 + de.de_header().len.as_usize() as u8)
+ .map(|de| de.de_header().serialize().len() as u8 + de.de_header().len.as_u8())
.sum::<u8>();
let encryption_info = [
@@ -748,7 +767,7 @@ fn do_signature_encrypted_identity_fixed_key_material_test<W: WriteDataElement>(
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();
@@ -763,7 +782,7 @@ fn do_signature_encrypted_identity_fixed_key_material_test<W: WriteDataElement>(
&section_body,
);
- let mut plaintext = metadata_key.as_slice().to_vec();
+ let mut plaintext = metadata_key.0.as_slice().to_vec();
plaintext.extend_from_slice(section_body.as_slice());
plaintext.extend_from_slice(&sig_payload.sign(&key_pair).to_bytes());
@@ -771,7 +790,7 @@ fn do_signature_encrypted_identity_fixed_key_material_test<W: WriteDataElement>(
&key_seed_hkdf.extended_signed_section_aes_key(),
NonceAndCounter::from_nonce(nonce),
)
- .encrypt(&mut plaintext);
+ .apply_keystream(&mut plaintext);
let ciphertext = plaintext;
let mut expected = vec![section_length];
@@ -786,7 +805,7 @@ fn do_signature_encrypted_identity_fixed_key_material_test<W: WriteDataElement>(
/// Write `section_contents_len` bytes of DE and header into `section_builder`
pub(crate) fn fill_section_builder<I: SectionEncoder>(
section_contents_len: usize,
- section_builder: &mut SectionBuilder<I>,
+ section_builder: &mut SectionBuilder<&mut AdvBuilder, I>,
) {
// DEs can only go up to 127, so we'll need multiple for long sections
for _ in 0..(section_contents_len / 100) {
@@ -844,9 +863,14 @@ pub(crate) trait SectionBuilderExt {
fn into_section(self) -> EncodedSection;
}
-impl<'a, I: SectionEncoder> SectionBuilderExt for SectionBuilder<'a, I> {
+impl<R: AsMut<AdvBuilder>, I: SectionEncoder> SectionBuilderExt for SectionBuilder<R, I> {
/// Convenience method for tests
- fn into_section(self) -> EncodedSection {
- Self::build_section(self.section.into_inner(), self.section_encoder, self.adv_builder)
+ fn into_section(mut self) -> EncodedSection {
+ let adv_builder_header_byte = self.adv_builder.as_mut().header_byte();
+ Self::build_section(
+ adv_builder_header_byte,
+ self.section.into_inner(),
+ self.section_encoder,
+ )
}
}
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 0853df5..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,15 +12,19 @@
// 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;
use crate::{
+ credential::{v1::V1, SimpleBroadcastCryptoMaterial},
de_type::EncryptedIdentityDataElementType,
extended::serialize::{
section_tests::{DummyDataElement, SectionBuilderExt},
AdvBuilder, MicEncryptedSectionEncoder,
},
+ MetadataKey,
};
use anyhow::anyhow;
use crypto_provider::{aes::ctr::AES_CTR_NONCE_LEN, aes::AesKey};
@@ -40,7 +44,7 @@ fn mic_encrypted_test_vectors() -> Result<(), anyhow::Error> {
);
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,
@@ -50,7 +54,7 @@ fn mic_encrypted_test_vectors() -> Result<(), anyhow::Error> {
for tc in test_cases {
{
let key_seed = extract_key_array::<32>(&tc, "key_seed");
- let metadata_key = extract_key_array::<16>(&tc, "metadata_key");
+ let metadata_key = MetadataKey(extract_key_array::<16>(&tc, "metadata_key"));
let adv_header_byte = extract_key_array::<1>(&tc, "adv_header_byte")[0];
let section_salt = v1_salt::V1Salt::<CryptoProviderImpl>::from(
extract_key_array::<16>(&tc, "section_salt"),
@@ -66,7 +70,7 @@ fn mic_encrypted_test_vectors() -> Result<(), anyhow::Error> {
})
.collect::<Vec<_>>();
- let hkdf = np_hkdf::NpKeySeedHkdf::new(&key_seed);
+ let hkdf = np_hkdf::NpKeySeedHkdf::<CryptoProviderImpl>::new(&key_seed);
assert_eq!(
extract_key_array::<16>(&tc, "aes_key").as_slice(),
@@ -81,16 +85,17 @@ fn mic_encrypted_test_vectors() -> Result<(), anyhow::Error> {
section_salt.derive::<{ AES_CTR_NONCE_LEN }>(Some(1.into())).unwrap()
);
+ let broadcast_cm = SimpleBroadcastCryptoMaterial::<V1>::new(key_seed, metadata_key);
+
// make an adv builder in the configuration we need
let mut adv_builder = AdvBuilder::new(AdvertisementType::Encrypted);
assert_eq!(adv_header_byte, adv_builder.header_byte());
let mut section_builder = adv_builder
- .section_builder(MicEncryptedSectionEncoder::new(
+ .section_builder(MicEncryptedSectionEncoder::<CryptoProviderImpl>::new(
identity_type,
section_salt,
- &metadata_key,
- &hkdf,
+ &broadcast_cm,
))
.unwrap();
@@ -128,7 +133,7 @@ fn gen_mic_encrypted_test_vectors() {
})
.collect::<Vec<_>>();
- let metadata_key = rng.gen();
+ let metadata_key = MetadataKey(rng.gen());
let key_seed = rng.gen();
let adv_header_byte = 0b00100000;
let identity_type =
@@ -136,15 +141,16 @@ fn gen_mic_encrypted_test_vectors() {
let key_seed_hkdf = np_hkdf::NpKeySeedHkdf::<CryptoProviderImpl>::new(&key_seed);
+ let broadcast_cm = SimpleBroadcastCryptoMaterial::<V1>::new(key_seed, metadata_key);
+
let mut adv_builder = AdvBuilder::new(AdvertisementType::Encrypted);
let section_salt = v1_salt::V1Salt::<CryptoProviderImpl>::from(rng.gen::<[u8; 16]>());
let mut section_builder = adv_builder
- .section_builder(MicEncryptedSectionEncoder::new(
+ .section_builder(MicEncryptedSectionEncoder::<CryptoProviderImpl>::new(
identity_type,
V1Salt::from(*section_salt.as_array_ref()),
- &metadata_key,
- &key_seed_hkdf,
+ &broadcast_cm,
))
.unwrap();
@@ -157,7 +163,7 @@ fn gen_mic_encrypted_test_vectors() {
array
.push(json!({
"key_seed": hex::encode_upper(key_seed),
- "metadata_key": hex::encode_upper(metadata_key),
+ "metadata_key": hex::encode_upper(metadata_key.0),
"adv_header_byte": hex::encode_upper([adv_header_byte]),
"section_salt": hex::encode_upper(section_salt.as_slice()),
"identity_type": identity_type_label(identity_type),
diff --git a/nearby/presence/np_adv/src/filter/mod.rs b/nearby/presence/np_adv/src/filter/mod.rs
new file mode 100644
index 0000000..a2b2d9c
--- /dev/null
+++ b/nearby/presence/np_adv/src/filter/mod.rs
@@ -0,0 +1,296 @@
+// Copyright 2023 Google LLC
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+//! Provides filtering APIs to determine whether or not a buffer of bytes represents
+//! a valid Nearby Presence advertisement and if so which was its corresponding identity
+//! it matched with. Used as a first pass option to quickly check if a buffer should
+//! further processed.
+use crate::credential::MatchedCredential;
+use crate::legacy::data_elements::DataElementDeserializeError;
+use crate::legacy::deserialize::DecryptedAdvContents;
+use crate::{
+ credential::{book::CredentialBook, v0::V0DiscoveryCryptoMaterial},
+ legacy,
+ legacy::{
+ actions,
+ actions::ActionsDataElement,
+ deserialize::{
+ DecryptError, EncryptedAdvContents, IntermediateAdvContents, PlainDataElement,
+ },
+ PacketFlavor,
+ },
+ parse_adv_header, AdvHeader, V1Header,
+};
+use array_view::ArrayView;
+use core::fmt::Debug;
+use crypto_provider::CryptoProvider;
+
+#[cfg(test)]
+mod tests;
+
+/// The options to filter for in an advertisement payload.
+pub enum FilterOptions {
+ /// Filter criteria for matching against only v0 advertisements
+ V0FilterOptions(V0Filter),
+ /// Filter criteria for matching against only v1 advertisements
+ V1FilterOptions(V1Filter),
+ /// Criteria to filter for in both v0 and v1 advertisements
+ Either(V0Filter, V1Filter),
+}
+
+/// Error returned when no advertisement matching the criteria was found.
+#[derive(PartialEq, Debug)]
+pub struct NoMatch;
+
+/// Error returned if the provided slice is of invalid length
+#[derive(PartialEq, Debug)]
+pub struct InvalidLength;
+
+/// The criteria of what to filter for in a V0 advertisement.
+pub struct V0Filter {
+ identity: IdentityFilterType,
+ data_elements: V0DataElementsFilter,
+}
+
+/// The type of identity to filter for.
+pub enum IdentityFilterType {
+ /// Filter only on public identity advertisements
+ Public,
+ /// Filter only on private identity advertisements
+ Private,
+ /// Don't filter on any specific identity type
+ Any,
+}
+
+/// Returned by the filter API indicating the status and in the case of private identity the key seed
+/// which matched the decrypted advertisement
+#[derive(Debug, PartialEq)]
+pub enum FilterResult<M: MatchedCredential> {
+ /// A public identity advertisement matching the filter criteria was detected.
+ Public,
+ /// A Private identity advertisement matching the filter criteria was detected and decrypted
+ /// with the contained `KeySeed`.
+ Private(M),
+}
+
+/// The criteria of what data elements to filter for in a V0 advertisement.
+pub struct V0DataElementsFilter {
+ contains_tx_power: Option<bool>,
+ actions_filter: Option<V0ActionsFilter>,
+}
+
+/// The total number of unique boolean action types
+const NUM_ACTIONS: usize = 7;
+
+/// Specify which specific actions bits to filter on, will filter on if any of the specified
+/// actions are matched
+//TODO: do we need a more specific filter criteria, eg: only return if `(A AND B) || C` are found?
+// Potentially this could be pulled out more generally into a V0Filter trait which has many different
+// implementations.
+pub struct V0ActionsFilter {
+ actions: array_view::ArrayView<Option<actions::ActionType>, NUM_ACTIONS>,
+}
+
+impl FilterOptions {
+ /// WARNING: This does not perform signature verification on the advertisement, even if it detects
+ /// a valid advertisement it does not verify it. Verification must occur before further processing
+ /// of the adv.
+ ///
+ /// Returns `Ok` if a advertisement was detected which matched the filter criteria, and `Err` otherwise.
+ pub fn match_advertisement<'a, B, P>(
+ &self,
+ cred_book: &'a B,
+ adv: &[u8],
+ ) -> Result<FilterResult<B::Matched>, NoMatch>
+ where
+ B: CredentialBook<'a>,
+ P: CryptoProvider,
+ {
+ parse_adv_header(adv)
+ .map(|(remaining, header)| match header {
+ AdvHeader::V0 => {
+ let filter = match self {
+ FilterOptions::V0FilterOptions(filter) => filter,
+ FilterOptions::Either(filter, _) => filter,
+ _ => return Err(NoMatch),
+ };
+ filter.match_v0_adv::<B, P>(cred_book, remaining)
+ }
+ AdvHeader::V1(header) => {
+ let filter = match self {
+ FilterOptions::V1FilterOptions(filter) => filter,
+ FilterOptions::Either(_, filter) => filter,
+ _ => return Err(NoMatch),
+ };
+ filter.match_v1_adv::<B, P>(cred_book, remaining, header)
+ }
+ })
+ .map_err(|_| NoMatch)?
+ }
+}
+
+impl V0Filter {
+ /// Filter for the provided criteria returning success if the advertisement bytes successfully
+ /// match the filter criteria
+ fn match_v0_adv<'a, B, P>(
+ &self,
+ cred_book: &'a B,
+ remaining: &[u8],
+ ) -> Result<FilterResult<B::Matched>, NoMatch>
+ where
+ B: CredentialBook<'a>,
+ P: CryptoProvider,
+ {
+ let contents =
+ legacy::deserialize::deserialize_adv_contents::<P>(remaining).map_err(|_| NoMatch)?;
+ match contents {
+ IntermediateAdvContents::Plaintext(p) => match self.identity {
+ IdentityFilterType::Public | IdentityFilterType::Any => self
+ .data_elements
+ .match_v0_legible_adv(|| p.data_elements())
+ .map(|()| FilterResult::Public),
+ _ => Err(NoMatch),
+ },
+ IntermediateAdvContents::Ciphertext(c) => match self.identity {
+ IdentityFilterType::Private | IdentityFilterType::Any => {
+ let (legible_adv, m) = try_decrypt_and_match::<B, P>(cred_book.v0_iter(), &c)?;
+ self.data_elements
+ .match_v0_legible_adv(|| legible_adv.data_elements())
+ .map(|()| FilterResult::Private(m))
+ }
+ _ => Err(NoMatch),
+ },
+ }
+ }
+}
+
+fn try_decrypt_and_match<'cred, B, P>(
+ v0_creds: B::V0Iterator,
+ adv: &EncryptedAdvContents,
+) -> Result<(DecryptedAdvContents, B::Matched), NoMatch>
+where
+ B: CredentialBook<'cred>,
+ P: CryptoProvider,
+{
+ for (crypto_material, m) in v0_creds {
+ let ldt = crypto_material.ldt_adv_cipher::<P>();
+ match adv.try_decrypt(&ldt) {
+ Ok(c) => return Ok((c, m)),
+ Err(e) => match e {
+ DecryptError::DecryptOrVerifyError => continue,
+ DecryptError::DeserializeError(_) => {
+ return Err(NoMatch);
+ }
+ },
+ }
+ }
+ Err(NoMatch)
+}
+
+impl V0DataElementsFilter {
+ /// A legible adv is either plaintext to begin with, or decrypted contents from an encrypted adv
+ fn match_v0_legible_adv<F, I>(&self, data_elements: impl Fn() -> I) -> Result<(), NoMatch>
+ where
+ F: PacketFlavor,
+ I: Iterator<Item = Result<PlainDataElement<F>, DataElementDeserializeError>>,
+ {
+ match &self.contains_tx_power {
+ None => Ok(()),
+ Some(c) => {
+ if c == &data_elements().any(|de| matches!(de, Ok(PlainDataElement::TxPower(_)))) {
+ Ok(())
+ } else {
+ Err(NoMatch)
+ }
+ }
+ }?;
+
+ match &self.actions_filter {
+ None => Ok(()),
+ Some(filter) => {
+ if let Some(_err) = data_elements().find_map(|result| result.err()) {
+ return Err(NoMatch);
+ }
+ // find if an actions DE exists, if so match on the provided action filter
+ let actions = data_elements().find_map(|de| match de {
+ Ok(PlainDataElement::Actions(actions)) => Some(actions),
+ _ => None,
+ });
+ if let Some(actions) = actions {
+ filter.match_v0_actions(&actions)
+ } else {
+ return Err(NoMatch);
+ }
+ }
+ }?;
+
+ Ok(())
+ }
+}
+
+impl V0ActionsFilter {
+ /// Creates a new filter from a slice of Actions to look for. Will succeed if any of the provided
+ /// actions are found. Maximum length of `actions` is 7, as there are 7 unique actions possible to
+ /// filter on for V0.
+ pub fn new_from_slice(actions: &[actions::ActionType]) -> Result<Self, InvalidLength> {
+ if actions.len() > NUM_ACTIONS {
+ return Err(InvalidLength);
+ }
+ let mut filter_actions = [None; NUM_ACTIONS];
+ for (i, action) in actions.iter().map(|action| Some(*action)).enumerate() {
+ // i will always be in bounds because the length is checked above
+ filter_actions[i] = action;
+ }
+ Ok(ArrayView::try_from_array(filter_actions, actions.len())
+ .map(|actions| Self { actions })
+ .expect("Length checked above, so this can't happen"))
+ }
+
+ fn match_v0_actions<F: PacketFlavor>(
+ &self,
+ actions: &ActionsDataElement<F>,
+ ) -> Result<(), NoMatch> {
+ for action in self.actions.as_slice().iter() {
+ if actions
+ .action
+ .has_action(&action.expect("This will always contain Some"))
+ .unwrap_or(false)
+ {
+ return Ok(());
+ }
+ }
+ Err(NoMatch)
+ }
+}
+
+/// The criteria of what to filter for in a V1 advertisement.
+pub struct V1Filter;
+
+impl V1Filter {
+ /// Filter for the provided criteria returning success if the advertisement bytes successfully
+ /// match the filter criteria
+ #[allow(clippy::extra_unused_type_parameters)]
+ fn match_v1_adv<'a, B, P>(
+ &self,
+ _cred_book: &'a B,
+ _remaining: &[u8],
+ _header: V1Header,
+ ) -> Result<FilterResult<B::Matched>, NoMatch>
+ where
+ B: CredentialBook<'a>,
+ P: CryptoProvider,
+ {
+ todo!()
+ }
+}
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
new file mode 100644
index 0000000..ffdeeb9
--- /dev/null
+++ b/nearby/presence/np_adv/src/filter/tests/actions_filter_tests.rs
@@ -0,0 +1,138 @@
+// 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.
+
+#![allow(clippy::unwrap_used)]
+
+use super::super::*;
+use crate::legacy::actions::{ActionBits, InstantTethering, NearbyShare};
+use crate::legacy::{Ciphertext, Plaintext};
+
+#[test]
+fn new_v0_actions_invalid_length() {
+ let actions = [actions::ActionType::ActiveUnlock; 8];
+ let result = V0ActionsFilter::new_from_slice(&actions);
+ assert!(result.is_err());
+ assert_eq!(result.err().unwrap(), InvalidLength)
+}
+
+#[test]
+fn new_v0_actions() {
+ let actions = [actions::ActionType::ActiveUnlock; 7];
+ let result = V0ActionsFilter::new_from_slice(&actions);
+ assert!(result.is_ok());
+}
+
+#[test]
+fn new_v0_actions_empty_slice() {
+ let result = V0ActionsFilter::new_from_slice(&[]);
+ assert!(result.is_ok());
+}
+
+#[test]
+fn test_actions_filter_single_action_not_present() {
+ // default is all 0 bits
+ let action_bits = ActionBits::<Plaintext>::default();
+
+ let filter = V0ActionsFilter::new_from_slice(&[actions::ActionType::ActiveUnlock; 1])
+ .expect("1 is a valid length");
+
+ assert_eq!(filter.match_v0_actions(&action_bits.into()), Err(NoMatch))
+}
+
+#[test]
+fn test_actions_filter_all_actions_not_present() {
+ // default is all 0 bits
+ let action_bits = ActionBits::<Plaintext>::default();
+
+ let filter = V0ActionsFilter::new_from_slice(&[
+ actions::ActionType::ActiveUnlock,
+ actions::ActionType::NearbyShare,
+ actions::ActionType::InstantTethering,
+ actions::ActionType::PhoneHub,
+ actions::ActionType::Finder,
+ actions::ActionType::FastPairSass,
+ actions::ActionType::PresenceManager,
+ ])
+ .expect("7 is a valid length");
+
+ assert_eq!(filter.match_v0_actions(&action_bits.into()), Err(NoMatch))
+}
+
+#[test]
+fn test_actions_filter_single_action_present() {
+ // default is all 0 bits
+ let mut action_bits = ActionBits::<Plaintext>::default();
+ action_bits.set_action(NearbyShare::from(true));
+
+ let filter = V0ActionsFilter::new_from_slice(&[
+ actions::ActionType::ActiveUnlock,
+ actions::ActionType::NearbyShare,
+ actions::ActionType::InstantTethering,
+ actions::ActionType::PhoneHub,
+ actions::ActionType::Finder,
+ actions::ActionType::FastPairSass,
+ actions::ActionType::PresenceManager,
+ ])
+ .expect("7 is a valid length");
+
+ assert_eq!(filter.match_v0_actions(&action_bits.into()), Ok(()))
+}
+
+#[test]
+fn test_actions_filter_desired_action_not_present() {
+ // default is all 0 bits
+ let mut action_bits = ActionBits::<Plaintext>::default();
+ action_bits.set_action(NearbyShare::from(true));
+
+ let filter = V0ActionsFilter::new_from_slice(&[
+ actions::ActionType::ActiveUnlock,
+ actions::ActionType::InstantTethering,
+ actions::ActionType::PhoneHub,
+ actions::ActionType::Finder,
+ actions::ActionType::FastPairSass,
+ actions::ActionType::PresenceManager,
+ ])
+ .expect("6 is a valid length");
+
+ assert_eq!(filter.match_v0_actions(&action_bits.into()), Err(NoMatch))
+}
+
+#[test]
+fn test_multiple_actions_set() {
+ // default is all 0 bits
+ let mut action_bits = ActionBits::<Ciphertext>::default();
+ action_bits.set_action(NearbyShare::from(true));
+ action_bits.set_action(InstantTethering::from(true));
+
+ let filter = V0ActionsFilter::new_from_slice(&[actions::ActionType::InstantTethering])
+ .expect("1 is a valid length");
+
+ assert_eq!(filter.match_v0_actions(&action_bits.into()), Ok(()))
+}
+
+#[test]
+fn test_multiple_actions_set_both_present() {
+ // default is all 0 bits
+ let mut action_bits = ActionBits::<Ciphertext>::default();
+ action_bits.set_action(NearbyShare::from(true));
+ action_bits.set_action(InstantTethering::from(true));
+
+ let filter = V0ActionsFilter::new_from_slice(&[
+ actions::ActionType::InstantTethering,
+ actions::ActionType::NearbyShare,
+ ])
+ .expect("7 is a valid length");
+
+ assert_eq!(filter.match_v0_actions(&action_bits.into()), Ok(()))
+}
diff --git a/nearby/presence/np_adv/src/filter/tests/data_elements_filter_tests.rs b/nearby/presence/np_adv/src/filter/tests/data_elements_filter_tests.rs
new file mode 100644
index 0000000..4a893a2
--- /dev/null
+++ b/nearby/presence/np_adv/src/filter/tests/data_elements_filter_tests.rs
@@ -0,0 +1,107 @@
+// 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 super::super::*;
+use crate::legacy::actions::{ActionBits, ActiveUnlock};
+use crate::legacy::data_elements::TxPowerDataElement;
+use crate::legacy::{Ciphertext, Plaintext};
+use crate::shared_data::TxPower;
+
+#[test]
+fn match_contains_tx_power() {
+ let filter = V0DataElementsFilter { contains_tx_power: Some(true), actions_filter: None };
+
+ let tx_power = TxPower::try_from(5).expect("within range");
+ let result = filter.match_v0_legible_adv(|| {
+ [Ok(PlainDataElement::<Ciphertext>::TxPower(TxPowerDataElement::from(tx_power.clone())))]
+ .into_iter()
+ });
+ assert_eq!(result, Ok(()))
+}
+
+#[test]
+fn match_not_contains_tx_power() {
+ let filter = V0DataElementsFilter { contains_tx_power: Some(true), actions_filter: None };
+ let result = filter.match_v0_legible_adv::<Plaintext, _>(|| [].into_iter());
+ assert_eq!(result, Err(NoMatch))
+}
+
+#[test]
+fn match_not_contains_actions() {
+ let filter = V0ActionsFilter::new_from_slice(&[actions::ActionType::ActiveUnlock; 1])
+ .expect("1 is a valid length");
+ let filter = V0DataElementsFilter { contains_tx_power: None, actions_filter: Some(filter) };
+ let tx_power = TxPower::try_from(5).expect("within range");
+ let result = filter.match_v0_legible_adv(|| {
+ [Ok(PlainDataElement::<Ciphertext>::TxPower(TxPowerDataElement::from(tx_power.clone())))]
+ .into_iter()
+ });
+ assert_eq!(result, Err(NoMatch))
+}
+
+#[test]
+fn match_contains_actions() {
+ let filter = V0ActionsFilter::new_from_slice(&[actions::ActionType::ActiveUnlock; 1])
+ .expect("1 is a valid length");
+ let filter = V0DataElementsFilter { contains_tx_power: None, actions_filter: Some(filter) };
+ let tx_power = TxPower::try_from(5).expect("within range");
+
+ let mut action_bits = ActionBits::<Ciphertext>::default();
+ action_bits.set_action(ActiveUnlock::from(true));
+
+ let result = filter.match_v0_legible_adv(|| {
+ [
+ Ok(PlainDataElement::<Ciphertext>::TxPower(TxPowerDataElement::from(tx_power.clone()))),
+ Ok(PlainDataElement::Actions(action_bits.into())),
+ ]
+ .into_iter()
+ });
+ assert_eq!(result, Ok(()))
+}
+
+#[test]
+fn match_contains_both() {
+ let filter = V0ActionsFilter::new_from_slice(&[actions::ActionType::ActiveUnlock; 1])
+ .expect("1 is a valid length");
+ let filter =
+ V0DataElementsFilter { contains_tx_power: Some(true), actions_filter: Some(filter) };
+ let tx_power = TxPower::try_from(5).expect("within range");
+
+ let mut action_bits = ActionBits::<Ciphertext>::default();
+ action_bits.set_action(ActiveUnlock::from(true));
+
+ let result = filter.match_v0_legible_adv(|| {
+ [
+ Ok(PlainDataElement::<Ciphertext>::TxPower(TxPowerDataElement::from(tx_power.clone()))),
+ Ok(PlainDataElement::Actions(action_bits.into())),
+ ]
+ .into_iter()
+ });
+ assert_eq!(result, Ok(()))
+}
+
+#[test]
+fn match_contains_either() {
+ let filter = V0ActionsFilter::new_from_slice(&[actions::ActionType::ActiveUnlock; 1])
+ .expect("1 is a valid length");
+ let filter =
+ V0DataElementsFilter { contains_tx_power: Some(true), actions_filter: Some(filter) };
+ let tx_power = TxPower::try_from(5).expect("within range");
+
+ let result = filter.match_v0_legible_adv(|| {
+ [Ok(PlainDataElement::<Ciphertext>::TxPower(TxPowerDataElement::from(tx_power.clone())))]
+ .into_iter()
+ });
+ assert_eq!(result, Err(NoMatch))
+}
diff --git a/nearby/presence/np_adv/src/filter/tests/mod.rs b/nearby/presence/np_adv/src/filter/tests/mod.rs
new file mode 100644
index 0000000..29d3e7c
--- /dev/null
+++ b/nearby/presence/np_adv/src/filter/tests/mod.rs
@@ -0,0 +1,107 @@
+// 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 crate::credential::book::CredentialBookBuilder;
+use crate::credential::KeySeedMatchedCredential;
+use crate::filter::IdentityFilterType::Any;
+use crate::filter::{
+ FilterOptions, FilterResult, NoMatch, V0DataElementsFilter, V0Filter, V1Filter,
+};
+use crypto_provider_default::CryptoProviderImpl;
+
+mod actions_filter_tests;
+mod data_elements_filter_tests;
+mod v0_filter_tests;
+
+#[test]
+fn top_level_match_v0_adv() {
+ let v0_filter = V0Filter {
+ identity: Any,
+ data_elements: V0DataElementsFilter { contains_tx_power: None, actions_filter: None },
+ };
+
+ let empty_cred_book =
+ CredentialBookBuilder::<KeySeedMatchedCredential>::build_cached_slice_book::<
+ 0,
+ 0,
+ CryptoProviderImpl,
+ >(&[], &[]);
+
+ let filter = FilterOptions::V0FilterOptions(v0_filter);
+ let result = filter.match_advertisement::<_, CryptoProviderImpl>(
+ &empty_cred_book,
+ &[
+ 0x0, // adv header
+ 0x03, // public DE
+ 0x16, 0x00, // actions
+ ],
+ );
+
+ assert_eq!(Ok(FilterResult::Public), result);
+}
+
+#[test]
+fn top_level_match_v0_adv_either() {
+ let v0_filter = V0Filter {
+ identity: Any,
+ data_elements: V0DataElementsFilter { contains_tx_power: None, actions_filter: None },
+ };
+
+ let empty_cred_book =
+ CredentialBookBuilder::<KeySeedMatchedCredential>::build_cached_slice_book::<
+ 0,
+ 0,
+ CryptoProviderImpl,
+ >(&[], &[]);
+
+ let filter = FilterOptions::Either(v0_filter, V1Filter {});
+ let result = filter.match_advertisement::<_, CryptoProviderImpl>(
+ &empty_cred_book,
+ &[
+ 0x0, // adv header
+ 0x03, // public DE
+ 0x16, 0x00, // actions
+ ],
+ );
+
+ assert_eq!(Ok(FilterResult::Public), result);
+}
+
+#[test]
+fn top_level_no_match_v0_adv() {
+ let v0_filter = V0Filter {
+ identity: Any,
+ data_elements: V0DataElementsFilter { contains_tx_power: None, actions_filter: None },
+ };
+
+ let empty_cred_book =
+ CredentialBookBuilder::<KeySeedMatchedCredential>::build_cached_slice_book::<
+ 0,
+ 0,
+ CryptoProviderImpl,
+ >(&[], &[]);
+
+ let filter = FilterOptions::V0FilterOptions(v0_filter);
+ let result = filter.match_advertisement::<_, CryptoProviderImpl>(
+ &empty_cred_book,
+ &[
+ 0x20, // V1 Advertisement header
+ 0x03, // Section Header
+ 0x03, // Public Identity DE header
+ 0x15, 0x03, // Length 1 Tx Power DE with value 3
+ ],
+ );
+
+ assert_eq!(Err(NoMatch), result);
+}
diff --git a/nearby/presence/np_adv/src/filter/tests/v0_filter_tests.rs b/nearby/presence/np_adv/src/filter/tests/v0_filter_tests.rs
new file mode 100644
index 0000000..971a39e
--- /dev/null
+++ b/nearby/presence/np_adv/src/filter/tests/v0_filter_tests.rs
@@ -0,0 +1,221 @@
+// 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 super::super::*;
+use crate::credential::book::CredentialBookBuilder;
+use crate::credential::v0::{V0DiscoveryCredential, V0};
+use crate::credential::{
+ KeySeedMatchedCredential, MatchableCredential, ReferencedMatchedCredential,
+};
+use crate::filter::IdentityFilterType::{Any, Private, Public};
+use crypto_provider_default::CryptoProviderImpl;
+use ldt_np_adv::NP_LEGACY_METADATA_KEY_LEN;
+
+const METADATA_KEY: [u8; NP_LEGACY_METADATA_KEY_LEN] = [0x33; NP_LEGACY_METADATA_KEY_LEN];
+const KEY_SEED: [u8; 32] = [0x11_u8; 32];
+const PRIVATE_IDENTITY_V0_ADV_CONTENTS: [u8; 19] = [
+ 0x21, // private DE
+ 0x22, 0x22, // salt
+ // ciphertext
+ 0x85, 0xBF, 0xA8, 0x83, 0x58, 0x7C, 0x50, 0xCF, 0x98, 0x38, 0xA7, 0x8A, 0xC0, 0x1C, 0x96, 0xF9,
+];
+
+const PUBLIC_IDENTITY_V0_ADV_CONTENTS: [u8; 3] = [
+ 0x03, // public DE
+ 0x16, 0x00, // actions
+];
+
+#[test]
+fn test_contains_public_identity() {
+ let filter = V0Filter {
+ identity: Public,
+ data_elements: V0DataElementsFilter { contains_tx_power: None, actions_filter: None },
+ };
+
+ let cred_book = CredentialBookBuilder::<KeySeedMatchedCredential>::build_cached_slice_book::<
+ 0,
+ 0,
+ CryptoProviderImpl,
+ >(&[], &[]);
+
+ let result =
+ filter.match_v0_adv::<_, CryptoProviderImpl>(&cred_book, &PUBLIC_IDENTITY_V0_ADV_CONTENTS);
+
+ assert_eq!(result, Ok(FilterResult::Public));
+}
+
+#[test]
+fn test_not_contains_private() {
+ let filter = V0Filter {
+ identity: Private,
+ data_elements: V0DataElementsFilter { contains_tx_power: None, actions_filter: None },
+ };
+
+ let cred_book = CredentialBookBuilder::<KeySeedMatchedCredential>::build_cached_slice_book::<
+ 0,
+ 0,
+ CryptoProviderImpl,
+ >(&[], &[]);
+
+ let result =
+ filter.match_v0_adv::<_, CryptoProviderImpl>(&cred_book, &PUBLIC_IDENTITY_V0_ADV_CONTENTS);
+
+ assert_eq!(result, Err(NoMatch));
+}
+
+#[test]
+fn test_contains_any_public() {
+ let filter = V0Filter {
+ identity: Any,
+ data_elements: V0DataElementsFilter { contains_tx_power: None, actions_filter: None },
+ };
+
+ let cred_book = CredentialBookBuilder::<KeySeedMatchedCredential>::build_cached_slice_book::<
+ 0,
+ 0,
+ CryptoProviderImpl,
+ >(&[], &[]);
+
+ let result =
+ filter.match_v0_adv::<_, CryptoProviderImpl>(&cred_book, &PUBLIC_IDENTITY_V0_ADV_CONTENTS);
+
+ assert_eq!(result, Ok(FilterResult::Public));
+}
+
+#[test]
+fn test_not_contains_public_identity() {
+ let filter = V0Filter {
+ identity: Public,
+ data_elements: V0DataElementsFilter { contains_tx_power: None, actions_filter: None },
+ };
+
+ let cred_book = CredentialBookBuilder::<KeySeedMatchedCredential>::build_cached_slice_book::<
+ 0,
+ 0,
+ CryptoProviderImpl,
+ >(&[], &[]);
+
+ let result =
+ filter.match_v0_adv::<_, CryptoProviderImpl>(&cred_book, &PRIVATE_IDENTITY_V0_ADV_CONTENTS);
+
+ assert_eq!(result, Err(NoMatch));
+}
+
+#[test]
+fn test_contains_private_identity() {
+ let filter = V0Filter {
+ identity: Private,
+ data_elements: V0DataElementsFilter { contains_tx_power: None, actions_filter: None },
+ };
+
+ let hkdf = np_hkdf::NpKeySeedHkdf::<CryptoProviderImpl>::new(&KEY_SEED);
+ let metadata_key_hmac: [u8; 32] =
+ hkdf.legacy_metadata_key_hmac_key().calculate_hmac(&METADATA_KEY);
+ let discovery_credential = V0DiscoveryCredential::new(KEY_SEED, metadata_key_hmac);
+ let match_data: KeySeedMatchedCredential = KEY_SEED.into();
+ let v0_creds: [MatchableCredential<V0, KeySeedMatchedCredential>; 1] =
+ [MatchableCredential { discovery_credential, match_data: match_data.clone() }];
+
+ let cred_book = CredentialBookBuilder::<KeySeedMatchedCredential>::build_cached_slice_book::<
+ 0,
+ 0,
+ CryptoProviderImpl,
+ >(&v0_creds, &[]);
+
+ let result =
+ filter.match_v0_adv::<_, CryptoProviderImpl>(&cred_book, &PRIVATE_IDENTITY_V0_ADV_CONTENTS);
+
+ assert_eq!(result, Ok(FilterResult::Private(ReferencedMatchedCredential::from(&match_data))));
+}
+
+#[test]
+fn test_contains_any_private_identity() {
+ let filter = V0Filter {
+ identity: Any,
+ data_elements: V0DataElementsFilter { contains_tx_power: None, actions_filter: None },
+ };
+
+ let hkdf = np_hkdf::NpKeySeedHkdf::<CryptoProviderImpl>::new(&KEY_SEED);
+ let metadata_key_hmac: [u8; 32] =
+ hkdf.legacy_metadata_key_hmac_key().calculate_hmac(&METADATA_KEY);
+ let discovery_credential = V0DiscoveryCredential::new(KEY_SEED, metadata_key_hmac);
+ let match_data: KeySeedMatchedCredential = KEY_SEED.into();
+ let v0_creds: [MatchableCredential<V0, KeySeedMatchedCredential>; 1] =
+ [MatchableCredential { discovery_credential, match_data: match_data.clone() }];
+
+ let cred_book = CredentialBookBuilder::<KeySeedMatchedCredential>::build_cached_slice_book::<
+ 0,
+ 0,
+ CryptoProviderImpl,
+ >(&v0_creds, &[]);
+
+ let result =
+ filter.match_v0_adv::<_, CryptoProviderImpl>(&cred_book, &PRIVATE_IDENTITY_V0_ADV_CONTENTS);
+
+ assert_eq!(result, Ok(FilterResult::Private(ReferencedMatchedCredential::from(&match_data))));
+}
+
+#[test]
+fn test_contains_private_identity_no_matching_credential() {
+ let filter = V0Filter {
+ identity: Private,
+ data_elements: V0DataElementsFilter { contains_tx_power: None, actions_filter: None },
+ };
+
+ let mut key_seed = [0u8; 32];
+ key_seed.copy_from_slice(&KEY_SEED);
+ // change one byte
+ key_seed[0] = 0x00;
+
+ let hkdf = np_hkdf::NpKeySeedHkdf::<CryptoProviderImpl>::new(&key_seed);
+ let metadata_key_hmac: [u8; 32] =
+ hkdf.legacy_metadata_key_hmac_key().calculate_hmac(&METADATA_KEY);
+ let discovery_credential = V0DiscoveryCredential::new(key_seed, metadata_key_hmac);
+ let v0_creds: [MatchableCredential<V0, KeySeedMatchedCredential>; 1] =
+ [MatchableCredential { discovery_credential, match_data: KEY_SEED.into() }];
+
+ let cred_book = CredentialBookBuilder::<KeySeedMatchedCredential>::build_cached_slice_book::<
+ 0,
+ 0,
+ CryptoProviderImpl,
+ >(&v0_creds, &[]);
+
+ let result =
+ filter.match_v0_adv::<_, CryptoProviderImpl>(&cred_book, &PRIVATE_IDENTITY_V0_ADV_CONTENTS);
+
+ assert_eq!(result, Err(NoMatch));
+}
+
+#[test]
+fn test_contains_private_identity_invalid_hmac_match() {
+ let filter = V0Filter {
+ identity: Private,
+ data_elements: V0DataElementsFilter { contains_tx_power: None, actions_filter: None },
+ };
+
+ let discovery_credential = V0DiscoveryCredential::new(KEY_SEED, [0u8; 32]);
+ let v0_creds: [MatchableCredential<V0, KeySeedMatchedCredential>; 1] =
+ [MatchableCredential { discovery_credential, match_data: KEY_SEED.into() }];
+
+ let cred_book = CredentialBookBuilder::<KeySeedMatchedCredential>::build_cached_slice_book::<
+ 0,
+ 0,
+ CryptoProviderImpl,
+ >(&v0_creds, &[]);
+
+ let result =
+ filter.match_v0_adv::<_, CryptoProviderImpl>(&cred_book, &PRIVATE_IDENTITY_V0_ADV_CONTENTS);
+
+ assert_eq!(result, Err(NoMatch));
+}
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/macros.rs b/nearby/presence/np_adv/src/legacy/actions/macros.rs
index 8208a57..db241d6 100644
--- a/nearby/presence/np_adv/src/legacy/actions/macros.rs
+++ b/nearby/presence/np_adv/src/legacy/actions/macros.rs
@@ -48,25 +48,6 @@ macro_rules! boolean_element_action_element_impl_shared {
/// Use `plaintext_only`, `ciphertext_only`, or `plaintext_and_ciphertext` to create appropriate
/// impls.
macro_rules! boolean_element {
- ($type_name:ident, $index:expr, plaintext_only) => {
- $crate::legacy::actions::macros::boolean_element_struct!($type_name);
- $crate::legacy::actions::macros::boolean_element_struct_from_bool!($type_name);
-
- impl $crate::legacy::actions::ActionElement for $type_name {
- $crate::legacy::actions::macros::boolean_element_action_element_impl_shared!(
- $type_name, $index
- );
-
- fn supports_flavor(flavor: $crate::legacy::PacketFlavorEnum) -> bool {
- match flavor {
- $crate::legacy::PacketFlavorEnum::Plaintext => true,
- $crate::legacy::PacketFlavorEnum::Ciphertext => false,
- }
- }
- }
-
- $crate::legacy::actions::macros::boolean_element_to_plaintext_element!($type_name);
- };
($type_name:ident, $index:expr, ciphertext_only) => {
$crate::legacy::actions::macros::boolean_element_struct!($type_name);
$crate::legacy::actions::macros::boolean_element_struct_from_bool!($type_name);
diff --git a/nearby/presence/np_adv/src/legacy/actions/mod.rs b/nearby/presence/np_adv/src/legacy/actions/mod.rs
index 353848e..e0f4514 100644
--- a/nearby/presence/np_adv/src/legacy/actions/mod.rs
+++ b/nearby/presence/np_adv/src/legacy/actions/mod.rs
@@ -42,27 +42,8 @@ pub(crate) mod tests;
/// of 3 bytes occupied by the DE payload.
#[derive(Debug, PartialEq, Eq)]
pub struct ActionsDataElement<F: PacketFlavor> {
- action: ActionBits<F>,
-}
-
-impl<F: PacketFlavor> ActionsDataElement<F> {
- /// Returns the actions bits as a u32. The upper limit of an actions field is 3 bytes,
- /// so the last bytes of this u32 will always be 0
- pub fn as_u32(self) -> u32 {
- self.action.bits
- }
-
- /// Return whether a boolean action type is set in this data element, or `None` if the given
- /// action type does not represent a boolean.
- pub fn has_action(&self, action_type: &ActionType) -> Option<bool> {
- (action_type.bits_len() == 1).then_some(self.action.bits_for_type(action_type) != 0)
- }
-
- /// Return the context sync sequence number.
- pub fn context_sync_seq_num(&self) -> ContextSyncSeqNum {
- ContextSyncSeqNum::try_from(self.action.bits_for_type(&ActionType::ContextSyncSeqNum) as u8)
- .expect("Masking with ActionType::ContextSyncSeqNum should always be in range")
- }
+ /// The action bits
+ pub action: ActionBits<F>,
}
pub(crate) const ACTIONS_MAX_LEN: usize = 3;
@@ -172,6 +153,26 @@ pub struct ActionBits<F: PacketFlavor> {
flavor: marker::PhantomData<F>,
}
+impl<F: PacketFlavor> ActionBits<F> {
+ /// Returns the actions bits as a u32. The upper limit of an actions field is 3 bytes,
+ /// so the last bytes of this u32 will always be 0
+ pub fn as_u32(self) -> u32 {
+ self.bits
+ }
+
+ /// Return whether a boolean action type is set in this data element, or `None` if the given
+ /// action type does not represent a boolean.
+ pub fn has_action(&self, action_type: &ActionType) -> Option<bool> {
+ (action_type.bits_len() == 1).then_some(self.bits_for_type(action_type) != 0)
+ }
+
+ /// Return the context sync sequence number.
+ pub fn context_sync_seq_num(&self) -> ContextSyncSeqNum {
+ ContextSyncSeqNum::try_from(self.bits_for_type(&ActionType::ContextSyncSeqNum) as u8)
+ .expect("Masking with ActionType::ContextSyncSeqNum should always be in range")
+ }
+}
+
impl<F: PacketFlavor> Default for ActionBits<F> {
fn default() -> Self {
ActionBits {
@@ -438,5 +439,5 @@ macros::boolean_element!(NearbyShare, 9, plaintext_and_ciphertext);
macros::boolean_element!(InstantTethering, 10, ciphertext_only);
macros::boolean_element!(PhoneHub, 11, ciphertext_only);
macros::boolean_element!(PresenceManager, 12, ciphertext_only);
-macros::boolean_element!(Finder, 13, plaintext_only);
-macros::boolean_element!(FastPairSass, 14, plaintext_only);
+macros::boolean_element!(Finder, 13, plaintext_and_ciphertext);
+macros::boolean_element!(FastPairSass, 14, plaintext_and_ciphertext);
diff --git a/nearby/presence/np_adv/src/legacy/actions/tests.rs b/nearby/presence/np_adv/src/legacy/actions/tests.rs
index 2e3fda0..6e68211 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::{
@@ -248,18 +250,6 @@ fn action_bits_try_from_flavor_mismatch_plaintext() {
}
#[test]
-fn action_bits_try_from_flavor_mismatch_ciphertext() {
- assert_eq!(
- FlavorNotSupported { flavor: PacketFlavorEnum::Ciphertext },
- ActionBits::<Ciphertext>::try_from(ActionType::Finder.all_bits()).unwrap_err()
- );
- assert_eq!(
- 0xF0000000,
- ActionBits::<Ciphertext>::try_from(ActionType::ContextSyncSeqNum.all_bits()).unwrap().bits
- );
-}
-
-#[test]
fn actions_de_deser_plaintext_with_ciphertext_action() {
assert_eq!(
DataElementDeserializeError::FlavorNotSupported {
@@ -294,14 +284,14 @@ fn context_sync_seq_num_works() {
let mut action_bits = ActionBits::<Plaintext>::default();
action_bits.set_action(ContextSyncSeqNum::try_from(15).unwrap());
let action_de = ActionsDataElement::from(action_bits);
- assert_eq!(15, action_de.context_sync_seq_num().as_u8());
+ assert_eq!(15, action_de.action.context_sync_seq_num().as_u8());
}
#[test]
fn context_sync_seq_num_default_zero() {
let action_bits = ActionBits::<Plaintext>::default();
let action_de = ActionsDataElement::from(action_bits);
- assert_eq!(0, action_de.context_sync_seq_num().as_u8());
+ assert_eq!(0, action_de.action.context_sync_seq_num().as_u8());
}
#[test]
@@ -310,9 +300,9 @@ fn has_action_plaintext_works() {
action_bits.set_action(ContextSyncSeqNum::try_from(15).unwrap());
action_bits.set_action(NearbyShare::from(true));
let action_de = ActionsDataElement::from(action_bits);
- assert_eq!(action_de.has_action(&ActionType::NearbyShare), Some(true));
- assert_eq!(action_de.has_action(&ActionType::ActiveUnlock), Some(false));
- assert_eq!(action_de.has_action(&ActionType::PhoneHub), Some(false));
+ assert_eq!(action_de.action.has_action(&ActionType::NearbyShare), Some(true));
+ assert_eq!(action_de.action.has_action(&ActionType::ActiveUnlock), Some(false));
+ assert_eq!(action_de.action.has_action(&ActionType::PhoneHub), Some(false));
}
#[test]
@@ -322,10 +312,10 @@ fn has_action_encrypted_works() {
action_bits.set_action(NearbyShare::from(true));
action_bits.set_action(ActiveUnlock::from(true));
let action_de = ActionsDataElement::from(action_bits);
- assert_eq!(action_de.has_action(&ActionType::NearbyShare), Some(true));
- assert_eq!(action_de.has_action(&ActionType::ActiveUnlock), Some(true));
- assert_eq!(action_de.has_action(&ActionType::PhoneHub), Some(false));
- assert_eq!(action_de.has_action(&ActionType::ContextSyncSeqNum), None);
+ assert_eq!(action_de.action.has_action(&ActionType::NearbyShare), Some(true));
+ assert_eq!(action_de.action.has_action(&ActionType::ActiveUnlock), Some(true));
+ assert_eq!(action_de.action.has_action(&ActionType::PhoneHub), Some(false));
+ assert_eq!(action_de.action.has_action(&ActionType::ContextSyncSeqNum), None);
}
// hypothetical action using the last bit
diff --git a/nearby/presence/np_adv/src/legacy/data_elements.rs b/nearby/presence/np_adv/src/legacy/data_elements.rs
index e9c2308..c97cb25 100644
--- a/nearby/presence/np_adv/src/legacy/data_elements.rs
+++ b/nearby/presence/np_adv/src/legacy/data_elements.rs
@@ -13,6 +13,8 @@
// limitations under the License.
//! V0 data elements and core trait impls.
+use nom::error::{ErrorKind, FromExternalError};
+
use crate::legacy::{
de_type::{DataElementType, PlainDataElementType},
serialize::{DataElementBundle, ToDataElementBundle},
@@ -53,12 +55,44 @@ pub enum DataElementDeserializeError {
/// The DE type attempting to be deserialized
de_type: DataElementType,
},
+ /// Only one identity data element is allowed in an advertisement, but a duplicate is found
+ /// while parsing.
+ DuplicateIdentityDataElement,
+ /// There is unexpected data remaining in the incoming payload.
+ UnexpectedDataRemaining,
+ /// Parsing error returned from Nom.
+ NomError(nom::error::ErrorKind),
+}
+
+impl FromExternalError<&[u8], DataElementDeserializeError> for DataElementDeserializeError {
+ fn from_external_error(
+ _input: &[u8],
+ _kind: ErrorKind,
+ e: DataElementDeserializeError,
+ ) -> Self {
+ e
+ }
+}
+
+impl nom::error::ParseError<&[u8]> for DataElementDeserializeError {
+ /// Creates an error from the input position and an [ErrorKind]
+ fn from_error_kind(_input: &[u8], kind: ErrorKind) -> Self {
+ Self::NomError(kind)
+ }
+
+ /// Combines an existing error with a new one created from the input
+ /// position and an [ErrorKind]. This is useful when backtracking
+ /// through a parse tree, accumulating error context on the way
+ fn append(_input: &[u8], kind: ErrorKind, _other: Self) -> Self {
+ Self::NomError(kind)
+ }
}
/// Data element holding a [TxPower].
#[derive(Debug, PartialEq, Eq)]
pub struct TxPowerDataElement {
- tx_power: TxPower,
+ /// The tx power value
+ pub tx_power: TxPower,
}
impl TxPowerDataElement {
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 @@ impl DeTypeCode {
}
}
+/// 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 5112ed7..d6c6b36 100644
--- a/nearby/presence/np_adv/src/legacy/deserialize/mod.rs
+++ b/nearby/presence/np_adv/src/legacy/deserialize/mod.rs
@@ -16,22 +16,25 @@
//!
//! This module only deals with the _contents_ of an advertisement, not the advertisement header.
-extern crate alloc;
+use core::marker::PhantomData;
use crate::{
+ credential::v0::V0,
de_type::EncryptedIdentityDataElementType,
legacy::{
actions,
data_elements::{DataElement, *},
de_type::{DataElementType, DeEncodedLength, DeTypeCode, PlainDataElementType},
- Ciphertext, PacketFlavor, Plaintext, NP_MAX_DE_CONTENT_LEN,
+ Ciphertext, PacketFlavor, Plaintext, ShortMetadataKey, NP_MAX_DE_CONTENT_LEN,
},
- PlaintextIdentityMode,
+ HasIdentityMatch, PlaintextIdentityMode,
};
-use alloc::vec::Vec;
+use array_view::ArrayView;
use crypto_provider::CryptoProvider;
use ldt_np_adv::{LegacySalt, NP_LEGACY_METADATA_KEY_LEN};
-use nom::{bytes, combinator, multi, number, sequence};
+use nom::{bytes, combinator, number, sequence};
+
+use super::BLE_ADV_SVC_CONTENT_LEN;
#[cfg(test)]
mod tests;
@@ -40,12 +43,15 @@ mod tests;
/// ciphertext.
pub(crate) fn deserialize_adv_contents<C: CryptoProvider>(
input: &[u8],
-) -> Result<IntermediateAdvContents, AdvDeserializeError> {
+) -> Result<IntermediateAdvContents<'_>, AdvDeserializeError> {
parse_raw_adv_contents::<C>(input).and_then(|raw_adv| match raw_adv {
- RawAdvertisement::Plaintext(parc) => parc
- .try_deserialize()
- .map(IntermediateAdvContents::Plaintext)
- .map_err(AdvDeserializeError::DataElementDeserializeError),
+ RawAdvertisement::Plaintext(adv_contents) => {
+ if adv_contents.data_elements().next().is_none() {
+ return Err(AdvDeserializeError::NoPublicDataElements);
+ }
+
+ Ok(IntermediateAdvContents::Plaintext(adv_contents))
+ }
RawAdvertisement::Ciphertext(eac) => Ok(IntermediateAdvContents::Ciphertext(eac)),
})
}
@@ -57,50 +63,56 @@ pub(crate) fn deserialize_adv_contents<C: CryptoProvider>(
fn parse_raw_adv_contents<C: CryptoProvider>(
input: &[u8],
) -> Result<RawAdvertisement, AdvDeserializeError> {
- let (_, data_elements) = parse_data_elements(input)
- .map_err(|_e| AdvDeserializeError::AdvertisementDeserializeError)?;
-
- if let Some(identity_de_type) =
- data_elements.first().and_then(|de| de.de_type.try_as_identity_de_type())
- {
- match identity_de_type.as_encrypted_identity_de_type() {
- Some(encrypted_de_type) => {
- if data_elements.len() == 1 {
- match encrypted_de_type {
- // TODO handle length=0 provisioned identity DEs
- EncryptedIdentityDataElementType::Private
- | EncryptedIdentityDataElementType::Trusted
- | EncryptedIdentityDataElementType::Provisioned => {
- combinator::map(
- parse_encrypted_identity_de_contents,
- |(salt, payload)| {
- RawAdvertisement::Ciphertext(EncryptedAdvContents {
- identity_type: encrypted_de_type,
- salt_padder: ldt_np_adv::salt_padder::<16, C>(salt),
- salt,
- ciphertext: payload,
- })
- },
- )(data_elements[0].contents)
- .map(|(_rem, contents)| contents)
- .map_err(|_e| AdvDeserializeError::AdvertisementDeserializeError)
+ if input.is_empty() {
+ return Err(AdvDeserializeError::MissingIdentity);
+ }
+ match parse_de(input) {
+ Ok((rem, identity_de)) => {
+ if let Some(identity_de_type) = identity_de.de_type.try_as_identity_de_type() {
+ match identity_de_type.as_encrypted_identity_de_type() {
+ Some(encrypted_de_type) => {
+ if matches!(parse_de(rem), Err(nom::Err::Error(..))) {
+ match encrypted_de_type {
+ // TODO handle length=0 provisioned identity DEs
+ EncryptedIdentityDataElementType::Private
+ | EncryptedIdentityDataElementType::Trusted
+ | EncryptedIdentityDataElementType::Provisioned => combinator::map(
+ parse_encrypted_identity_de_contents,
+ |(salt, payload)| {
+ RawAdvertisement::Ciphertext(EncryptedAdvContents {
+ identity_type: encrypted_de_type,
+ salt_padder: ldt_np_adv::salt_padder::<16, C>(salt),
+ salt,
+ ciphertext: payload,
+ })
+ },
+ )(
+ identity_de.contents,
+ )
+ .map(|(_rem, contents)| contents)
+ .map_err(|_e| AdvDeserializeError::AdvertisementDeserializeError),
+ }
+ } else {
+ Err(AdvDeserializeError::TooManyTopLevelDataElements)
}
}
- } else {
- Err(AdvDeserializeError::TooManyTopLevelDataElements)
+ // It's an identity de, but not encrypted, so it must be public, and the rest
+ // must be plain
+ None => Ok(RawAdvertisement::Plaintext(PlaintextAdvContents {
+ identity_type: PlaintextIdentityMode::Public,
+ data: rem,
+ })),
}
+ } else {
+ Err(AdvDeserializeError::MissingIdentity)
}
- // It's an identity de, but not encrypted, so it must be public, and the rest must be
- // plain
- None => plain_data_elements(&data_elements[1..]).map(|pdes| {
- RawAdvertisement::Plaintext(PlaintextAdvRawContents {
- identity_type: PlaintextIdentityMode::Public,
- data_elements: pdes,
- })
- }),
}
- } else {
- Err(AdvDeserializeError::MissingIdentity)
+ Err(nom::Err::Error(_)) | Err(nom::Err::Failure(_)) => {
+ Err(AdvDeserializeError::AdvertisementDeserializeError)
+ }
+ Err(nom::Err::Incomplete(_)) => {
+ panic!("Should not hit Incomplete when using nom::complete parsers")
+ }
}
}
@@ -109,25 +121,16 @@ fn parse_raw_adv_contents<C: CryptoProvider>(
pub(crate) enum AdvDeserializeError {
/// Parsing the overall advertisement or DE structure failed
AdvertisementDeserializeError,
- /// Deserializing an individual DE from its DE contents failed
- DataElementDeserializeError(DataElementDeserializeError),
/// Must not have any other top level data elements if there is an encrypted identity DE
TooManyTopLevelDataElements,
- /// Must not have an identity DE inside an identity DE
- InvalidDataElementHierarchy,
/// Missing identity DE
MissingIdentity,
-}
-
-/// Parse an advertisement's contents into raw DEs.
-///
-/// Consumes the entire input.
-fn parse_data_elements(adv_contents: &[u8]) -> nom::IResult<&[u8], Vec<RawDataElement>> {
- combinator::all_consuming(multi::many0(parse_de))(adv_contents)
+ /// Non-identity DE contents must not be empty
+ NoPublicDataElements,
}
/// Parse an individual DE into its header and contents.
-fn parse_de(input: &[u8]) -> nom::IResult<&[u8], RawDataElement> {
+fn parse_de(input: &[u8]) -> nom::IResult<&[u8], RawDataElement, DataElementDeserializeError> {
let (remaining, (de_type, actual_len)) =
combinator::map_opt(number::complete::u8, |de_header| {
// header: LLLLTTTT
@@ -151,22 +154,6 @@ fn parse_de(input: &[u8]) -> nom::IResult<&[u8], RawDataElement> {
})(remaining)
}
-/// Returns `Err`` if any DEs are not of a plain DE type.
-fn plain_data_elements<'d, D: AsRef<[RawDataElement<'d>]>>(
- data_elements: D,
-) -> Result<Vec<RawPlainDataElement<'d>>, AdvDeserializeError> {
- data_elements
- .as_ref()
- .iter()
- .map(|de| {
- de.de_type
- .try_as_plain_de_type()
- .map(|de_type| RawPlainDataElement { de_type, contents: de.contents })
- })
- .collect::<Option<Vec<_>>>()
- .ok_or(AdvDeserializeError::InvalidDataElementHierarchy)
-}
-
/// Parse legacy encrypted identity DEs (private, trusted, provisioned) into salt and ciphertext
/// (encrypted metadata key and at least 2 bytes of DEs).
///
@@ -191,6 +178,7 @@ fn parse_encrypted_identity_de_contents(
#[derive(Debug, PartialEq, Eq)]
struct RawDataElement<'d> {
de_type: DataElementType,
+ /// Byte array payload of the data element, without the DE header.
contents: &'d [u8],
}
@@ -198,30 +186,76 @@ struct RawDataElement<'d> {
/// level DE representations.
#[derive(Debug, PartialEq, Eq)]
enum RawAdvertisement<'d> {
- Plaintext(PlaintextAdvRawContents<'d>),
+ Plaintext(PlaintextAdvContents<'d>),
Ciphertext(EncryptedAdvContents<'d>),
}
-/// A plaintext advertisement's content in raw DEs but without further deserialization.
-#[derive(Debug, PartialEq, Eq)]
-struct PlaintextAdvRawContents<'d> {
- identity_type: PlaintextIdentityMode,
- data_elements: Vec<RawPlainDataElement<'d>>,
+/// An iterator that parses the given data elements iteratively. In environments
+/// where memory is not severely constrained, it is usually safer to collect
+/// this into `Result<Vec<PlainDataElement>>` so the validity of the whole
+/// advertisement can be checked before proceeding with further processing.
+#[derive(Clone, Debug, PartialEq, Eq)]
+pub struct PlainDeIterator<'d, F>
+where
+ F: PacketFlavor,
+ actions::ActionsDataElement<F>: DataElement,
+{
+ /// Data to be parsed, containing a sequence of data elements in serialized
+ /// form. This should not contain the identity data elements.
+ data: &'d [u8],
+ _marker: PhantomData<F>,
}
-impl<'d> PlaintextAdvRawContents<'d> {
- /// Deserialize the DE contents into per-DE-type structures.
- ///
- /// Returns `Some` if each DE's contents can be successfully deserialized, otherwise `None`.
- fn try_deserialize(&self) -> Result<PlaintextAdvContents, DataElementDeserializeError> {
- self.data_elements
- .iter()
- .map(|de| de.try_deserialize::<Plaintext>())
- .collect::<Result<Vec<_>, _>>()
- .map(|des| PlaintextAdvContents {
- identity_type: self.identity_type,
- data_elements: des,
- })
+impl<'d, F> PlainDeIterator<'d, F>
+where
+ F: PacketFlavor,
+ actions::ActionsDataElement<F>: DataElement,
+{
+ fn raw_de_to_plain_de(
+ raw_de: RawDataElement<'d>,
+ ) -> Result<PlainDataElement<F>, DataElementDeserializeError> {
+ let de_type = raw_de
+ .de_type
+ .try_as_plain_de_type()
+ .ok_or(DataElementDeserializeError::DuplicateIdentityDataElement)?;
+ (RawPlainDataElement { de_type, contents: raw_de.contents }).try_deserialize()
+ }
+}
+
+impl<'d, F> Iterator for PlainDeIterator<'d, F>
+where
+ F: PacketFlavor,
+ actions::ActionsDataElement<F>: DataElement,
+{
+ type Item = Result<PlainDataElement<F>, DataElementDeserializeError>;
+
+ fn next(&mut self) -> Option<Self::Item> {
+ let parse_result = nom::combinator::cut(nom::combinator::map_res(
+ parse_de,
+ Self::raw_de_to_plain_de,
+ ))(self.data);
+ match parse_result {
+ Ok((rem, de)) => {
+ self.data = rem;
+ Some(Ok(de))
+ }
+ Err(nom::Err::Error(_)) => {
+ panic!("All Errors are turned into Failures with `cut` above");
+ }
+ Err(nom::Err::Failure(DataElementDeserializeError::NomError(
+ nom::error::ErrorKind::Eof,
+ ))) => {
+ if self.data.is_empty() {
+ None
+ } else {
+ Some(Err(DataElementDeserializeError::UnexpectedDataRemaining))
+ }
+ }
+ Err(nom::Err::Failure(e)) => Some(Err(e)),
+ Err(nom::Err::Incomplete(_)) => {
+ panic!("Incomplete unexpected when using nom::complete APIs")
+ }
+ }
}
}
@@ -229,6 +263,7 @@ impl<'d> PlaintextAdvRawContents<'d> {
#[derive(Debug, PartialEq, Eq)]
pub(crate) struct RawPlainDataElement<'d> {
de_type: PlainDataElementType,
+ /// Byte array payload of the data element, without the DE header.
contents: &'d [u8],
}
@@ -281,7 +316,6 @@ impl<'d> EncryptedAdvContents<'d> {
.map_err(|_e| DecryptError::DecryptOrVerifyError)?;
// plaintext starts with 14 bytes of metadata key, then DEs.
-
let (remaining, metadata_key) = combinator::map_res(
bytes::complete::take(NP_LEGACY_METADATA_KEY_LEN),
|slice: &[u8]| slice.try_into(),
@@ -290,26 +324,15 @@ impl<'d> EncryptedAdvContents<'d> {
DecryptError::DeserializeError(AdvDeserializeError::AdvertisementDeserializeError)
})?;
- let (_remaining, raw_des) = combinator::all_consuming(parse_data_elements)(remaining)
- .map_err(|_e| {
- DecryptError::DeserializeError(AdvDeserializeError::AdvertisementDeserializeError)
- })?;
-
- plain_data_elements(&raw_des)?
- .into_iter()
- .map(|de| de.try_deserialize())
- .collect::<Result<Vec<_>, _>>()
- .map_err(|e| {
- DecryptError::DeserializeError(AdvDeserializeError::DataElementDeserializeError(e))
- })
- .map(|data_elements| {
- DecryptedAdvContents::new(
- self.identity_type,
- metadata_key,
- self.salt,
- data_elements,
- )
- })
+ let remaining_arr = ArrayView::try_from_slice(remaining)
+ .expect("Max remaining = 31 - 14 = 17 bytes < BLE_ADV_SVC_CONTENT_LEN");
+
+ Ok(DecryptedAdvContents::new(
+ self.identity_type,
+ ShortMetadataKey(metadata_key),
+ self.salt,
+ remaining_arr,
+ ))
}
}
@@ -336,26 +359,50 @@ pub enum PlainDataElement<F: PacketFlavor> {
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 {
+pub struct PlaintextAdvContents<'d> {
identity_type: PlaintextIdentityMode,
- data_elements: Vec<PlainDataElement<Plaintext>>,
+ /// Contents of the advertisement excluding the identity DE
+ data: &'d [u8],
}
-impl PlaintextAdvContents {
+impl<'d> PlaintextAdvContents<'d> {
/// Returns the identity type used for the advertisement
pub fn identity(&self) -> PlaintextIdentityMode {
self.identity_type
}
- /// Returns the deserialized data elements
- pub fn data_elements(&self) -> impl Iterator<Item = &PlainDataElement<Plaintext>> {
- self.data_elements.iter()
- }
- /// Destructures this V0 plaintext advertisement
- /// into just the contained data elements
- pub fn to_data_elements(self) -> Vec<PlainDataElement<Plaintext>> {
- self.data_elements
+
+ /// Returns an iterator over the v0 data elements
+ pub fn data_elements(&self) -> PlainDeIterator<'d, Plaintext> {
+ PlainDeIterator { data: self.data, _marker: PhantomData }
}
}
@@ -363,20 +410,22 @@ impl PlaintextAdvContents {
#[derive(Debug, PartialEq, Eq)]
pub struct DecryptedAdvContents {
identity_type: EncryptedIdentityDataElementType,
- metadata_key: [u8; NP_LEGACY_METADATA_KEY_LEN],
+ metadata_key: ShortMetadataKey,
salt: LegacySalt,
- data_elements: Vec<PlainDataElement<Ciphertext>>,
+ /// The decrypted data in this advertisement. This should be a sequence of
+ /// serialized data elements, excluding the identity DE.
+ data: ArrayView<u8, { BLE_ADV_SVC_CONTENT_LEN }>,
}
impl DecryptedAdvContents {
/// Returns a new DecryptedAdvContents with the provided contents.
fn new(
identity_type: EncryptedIdentityDataElementType,
- metadata_key: [u8; NP_LEGACY_METADATA_KEY_LEN],
+ metadata_key: ShortMetadataKey,
salt: LegacySalt,
- data_elements: Vec<PlainDataElement<Ciphertext>>,
+ data: ArrayView<u8, { BLE_ADV_SVC_CONTENT_LEN }>,
) -> Self {
- Self { identity_type, metadata_key, salt, data_elements }
+ Self { identity_type, metadata_key, salt, data }
}
/// The type of identity DE used in the advertisement.
@@ -384,16 +433,11 @@ impl DecryptedAdvContents {
self.identity_type
}
- /// The decrypted metadata key from the identity DE.
- pub fn metadata_key(&self) -> &[u8; 14] {
- &self.metadata_key
- }
-
/// Iterator over the data elements in an advertisement, except for any DEs related to resolving
/// the identity or otherwise validating the payload (e.g. any identity DEs like Private
/// Identity).
- pub fn data_elements(&self) -> impl Iterator<Item = &PlainDataElement<Ciphertext>> {
- self.data_elements.iter()
+ pub fn data_elements(&self) -> PlainDeIterator<Ciphertext> {
+ PlainDeIterator { data: self.data.as_slice(), _marker: PhantomData }
}
/// The salt used for decryption of this advertisement.
@@ -402,12 +446,19 @@ impl DecryptedAdvContents {
}
}
+impl HasIdentityMatch for DecryptedAdvContents {
+ type Version = V0;
+ fn metadata_key(&self) -> ShortMetadataKey {
+ self.metadata_key
+ }
+}
+
/// The contents of an advertisement after plaintext DEs, if any, have been deserialized, but
/// before any decryption is done.
#[derive(Debug, PartialEq, Eq)]
pub(crate) enum IntermediateAdvContents<'d> {
/// Plaintext advertisements
- Plaintext(PlaintextAdvContents),
+ Plaintext(PlaintextAdvContents<'d>),
/// Ciphertext advertisements
Ciphertext(EncryptedAdvContents<'d>),
}
diff --git a/nearby/presence/np_adv/src/legacy/deserialize/tests.rs b/nearby/presence/np_adv/src/legacy/deserialize/tests.rs
index 4cf6041..3dfecd1 100644
--- a/nearby/presence/np_adv/src/legacy/deserialize/tests.rs
+++ b/nearby/presence/np_adv/src/legacy/deserialize/tests.rs
@@ -12,30 +12,34 @@
// 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};
-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, id_de_type_as_generic_de_type, AdvBuilder,
- DataElementBundle, Identity, LdtIdentity, ToDataElementBundle as _,
+ 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;
+use nom::error::{self, ErrorKind};
use rand_ext::rand::{prelude::SliceRandom, Rng as _};
use std::vec;
use strum::IntoEnumIterator as _;
@@ -75,22 +79,27 @@ fn parse_raw_adv_3_de_public_identity() {
let adv = parse_raw_adv_contents::<CryptoProviderImpl>(&[
0x03, // public identity
0x15, 0x05, // tx power 5
- 0x36, 0x11, 0x12, 0x13, // actions
+ 0x26, 0x00, 0x44, // actions
])
.unwrap();
- assert_eq!(
- RawAdvertisement::Plaintext(PlaintextAdvRawContents {
- identity_type: PlaintextIdentityMode::Public,
- data_elements: vec![
- RawPlainDataElement { de_type: PlainDataElementType::TxPower, contents: &[0x05] },
- RawPlainDataElement {
- de_type: PlainDataElementType::Actions,
- contents: &[0x11, 0x12, 0x13]
- }
- ],
- }),
- adv
- );
+ match adv {
+ RawAdvertisement::Plaintext(plaintext) => {
+ assert_eq!(PlaintextIdentityMode::Public, plaintext.identity_type);
+ let mut action_bits = ActionBits::default();
+ action_bits.set_action(NearbyShare::from(true));
+ action_bits.set_action(Finder::from(true));
+ assert_eq!(
+ vec![
+ PlainDataElement::<Plaintext>::TxPower(TxPowerDataElement::from(
+ TxPower::try_from(5).unwrap()
+ )),
+ PlainDataElement::Actions(ActionsDataElement::from(action_bits)),
+ ],
+ plaintext.data_elements().collect::<Result<Vec<_>, _>>().unwrap(),
+ );
+ }
+ RawAdvertisement::Ciphertext(_) => panic!("adv should be plaintext"),
+ }
}
#[test]
@@ -99,13 +108,16 @@ fn parse_raw_adv_0_de_public_identity() {
0x03, // public identity
])
.unwrap();
- assert_eq!(
- RawAdvertisement::Plaintext(PlaintextAdvRawContents {
- identity_type: PlaintextIdentityMode::Public,
- data_elements: vec![],
- }),
- adv
- );
+ match adv {
+ RawAdvertisement::Plaintext(plaintext) => {
+ assert_eq!(PlaintextIdentityMode::Public, plaintext.identity_type);
+ assert_eq!(
+ Vec::<PlainDataElement<Plaintext>>::new(),
+ plaintext.data_elements().collect::<Result<Vec<_>, _>>().unwrap()
+ );
+ }
+ RawAdvertisement::Ciphertext(_) => panic!("adv should be plaintext"),
+ }
}
#[test]
@@ -113,8 +125,8 @@ fn parse_raw_adv_1_de_length_overrun() {
// battery uses the header length as is
let input = &[0xFB, 0x01, 0x02, 0x03];
assert_eq!(
- nom::Err::Error(error::Error { input: input.as_slice(), code: error::ErrorKind::Eof }),
- parse_data_elements(input).unwrap_err()
+ nom::Err::Error(DataElementDeserializeError::NomError(ErrorKind::MapOpt)),
+ parse_de(input).unwrap_err(),
);
}
@@ -125,10 +137,15 @@ fn parse_raw_adv_public_identity_containing_public_identity() {
0x03, // another public identity
0x15, 0x03, // tx power de
];
- assert_eq!(
- AdvDeserializeError::InvalidDataElementHierarchy,
- parse_raw_adv_contents::<CryptoProviderImpl>(input).unwrap_err()
- );
+ match parse_raw_adv_contents::<CryptoProviderImpl>(input).unwrap() {
+ RawAdvertisement::Plaintext(content) => {
+ assert_eq!(
+ DataElementDeserializeError::DuplicateIdentityDataElement,
+ content.data_elements().collect::<Result<Vec<_>, _>>().unwrap_err(),
+ );
+ }
+ RawAdvertisement::Ciphertext(_) => panic!("Adv should be plaintext"),
+ }
}
#[test]
@@ -296,35 +313,42 @@ fn parse_de_invalid_de_len_error() {
];
assert_eq!(
- nom::Err::Error(error::Error { input: input.as_slice(), code: error::ErrorKind::MapOpt }),
+ nom::Err::Error(DataElementDeserializeError::NomError(ErrorKind::MapOpt)),
parse_de(&input[..]).unwrap_err()
);
}
#[test]
-fn plain_data_elements_matches_plain_des() {
+fn raw_de_to_plain_de_matches_plain_des() {
assert_eq!(
- vec![
- RawPlainDataElement { de_type: PlainDataElementType::TxPower, contents: &[0x01] },
- RawPlainDataElement { de_type: PlainDataElementType::Actions, contents: &[0x02] }
- ],
- plain_data_elements(&[
- RawDataElement { de_type: DataElementType::TxPower, contents: &[0x01] },
- RawDataElement { de_type: DataElementType::Actions, contents: &[0x02] }
- ])
- .unwrap()
+ PlainDataElement::TxPower(TxPowerDataElement::from(TxPower::try_from(1).unwrap())),
+ PlainDeIterator::<Plaintext>::raw_de_to_plain_de(RawDataElement {
+ de_type: DataElementType::TxPower,
+ contents: &[0x01]
+ })
+ .unwrap(),
+ );
+ assert_eq!(
+ PlainDataElement::Actions(ActionsDataElement::from(
+ ActionBits::try_from(0x00400000).unwrap()
+ )),
+ PlainDeIterator::<Plaintext>::raw_de_to_plain_de(RawDataElement {
+ de_type: DataElementType::Actions,
+ contents: &[0x00, 0x40]
+ })
+ .unwrap(),
);
}
#[test]
-fn plain_data_elements_rejects_identity_de_error() {
+fn raw_de_to_plain_de_rejects_identity_de_error() {
for idet in IdentityDataElementType::iter() {
assert_eq!(
- AdvDeserializeError::InvalidDataElementHierarchy,
- plain_data_elements(&[
- RawDataElement { de_type: DataElementType::TxPower, contents: &[0x01] },
- RawDataElement { de_type: id_de_type_as_generic_de_type(idet), contents: &[0x02] }
- ])
+ DataElementDeserializeError::DuplicateIdentityDataElement,
+ PlainDeIterator::<Plaintext>::raw_de_to_plain_de(RawDataElement {
+ de_type: id_de_type_as_generic_de_type(idet),
+ contents: &[0x02],
+ })
.unwrap_err()
);
}
@@ -333,7 +357,10 @@ fn plain_data_elements_rejects_identity_de_error() {
#[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()
@@ -343,7 +370,10 @@ fn parse_encrypted_identity_contents_too_short_error() {
#[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()
@@ -351,6 +381,17 @@ fn parse_encrypted_identity_contents_ok() {
}
#[test]
+fn deserialize_adv_public_identity_empty_des() {
+ let input = &[
+ 0x03, // public identity
+ ];
+ assert_eq!(
+ AdvDeserializeError::NoPublicDataElements,
+ deserialize_adv_contents::<CryptoProviderImpl>(input).unwrap_err()
+ );
+}
+
+#[test]
fn plaintext_random_adv_contents_round_trip_public() {
plaintext_random_adv_contents_round_trip(PublicIdentity::default, PlaintextIdentityMode::Public)
}
@@ -372,15 +413,17 @@ fn ciphertext_random_adv_contents_round_trip() {
let salt: ldt_np_adv::LegacySalt = rng.gen::<[u8; 2]>().into();
let metadata_key: [u8; NP_LEGACY_METADATA_KEY_LEN] = rng.gen();
let hkdf = np_hkdf::NpKeySeedHkdf::<CryptoProviderImpl>::new(&key_seed);
- let ldt_key = hkdf.legacy_ldt_key();
let metadata_key_hmac: [u8; 32] =
hkdf.legacy_metadata_key_hmac_key().calculate_hmac(&metadata_key);
let cipher = ldt_np_adv::build_np_adv_decrypter_from_key_seed(&hkdf, metadata_key_hmac);
+
+ let metadata_key = ShortMetadataKey(metadata_key);
+ let broadcast_cm = SimpleBroadcastCryptoMaterial::<V0>::new(key_seed, metadata_key);
+
let mut builder = AdvBuilder::new(LdtIdentity::<CryptoProviderImpl>::new(
identity_type,
salt,
- metadata_key,
- LdtEncrypterXtsAes128::<CryptoProviderImpl>::new(&ldt_key),
+ &broadcast_cm,
));
loop {
@@ -418,10 +461,11 @@ fn ciphertext_random_adv_contents_round_trip() {
eac
);
- assert_eq!(
- DecryptedAdvContents { identity_type, metadata_key, salt, data_elements: des },
- eac.try_decrypt(&cipher).unwrap()
- )
+ let contents = eac.try_decrypt(&cipher).unwrap();
+ assert_eq!(identity_type, contents.identity_type);
+ assert_eq!(metadata_key, contents.metadata_key);
+ assert_eq!(salt, contents.salt);
+ assert_eq!(des, contents.data_elements().collect::<Result<Vec<_>, _>>().unwrap());
} else {
panic!("Unexpected variant: {:?}", parsed_adv);
}
@@ -435,15 +479,18 @@ fn decrypt_and_deserialize_ciphertext_adv_canned() {
let metadata_key: [u8; NP_LEGACY_METADATA_KEY_LEN] = [0x33; NP_LEGACY_METADATA_KEY_LEN];
let hkdf = np_hkdf::NpKeySeedHkdf::<CryptoProviderImpl>::new(&key_seed);
- let ldt_key = hkdf.legacy_ldt_key();
let metadata_key_hmac: [u8; 32] =
hkdf.legacy_metadata_key_hmac_key().calculate_hmac(&metadata_key);
let cipher = ldt_np_adv::build_np_adv_decrypter_from_key_seed(&hkdf, metadata_key_hmac);
+
+ let metadata_key = ShortMetadataKey(metadata_key);
+
+ let broadcast_cm = SimpleBroadcastCryptoMaterial::<V0>::new(key_seed, metadata_key);
+
let mut builder = AdvBuilder::new(LdtIdentity::<CryptoProviderImpl>::new(
EncryptedIdentityDataElementType::Private,
salt,
- metadata_key,
- LdtEncrypterXtsAes128::<CryptoProviderImpl>::new(&ldt_key),
+ &broadcast_cm,
));
let tx = shared_data::TxPower::try_from(3).unwrap();
@@ -479,17 +526,16 @@ fn decrypt_and_deserialize_ciphertext_adv_canned() {
eac
);
+ let decrypted = eac.try_decrypt(&cipher).unwrap();
+ assert_eq!(EncryptedIdentityDataElementType::Private, decrypted.identity_type);
+ assert_eq!(metadata_key, decrypted.metadata_key);
+ assert_eq!(salt, decrypted.salt);
assert_eq!(
- DecryptedAdvContents {
- identity_type: EncryptedIdentityDataElementType::Private,
- metadata_key,
- salt,
- data_elements: vec![PlainDataElement::TxPower(TxPowerDataElement::from(
- TxPower::try_from(3).unwrap()
- ))],
- },
- eac.try_decrypt(&cipher).unwrap()
- )
+ vec![PlainDataElement::TxPower(TxPowerDataElement::from(
+ TxPower::try_from(3).unwrap()
+ ))],
+ decrypted.data_elements().collect::<Result<Vec<_>, _>>().unwrap()
+ );
} else {
panic!("Unexpected variant: {:?}", parsed_adv);
}
@@ -497,7 +543,7 @@ fn decrypt_and_deserialize_ciphertext_adv_canned() {
#[test]
fn decrypt_and_deserialize_plaintext_adv_canned() {
- let mut builder = AdvBuilder::new(PublicIdentity::default());
+ let mut builder = AdvBuilder::new(PublicIdentity);
let actions = ActionBits::default();
builder.add_data_element(ActionsDataElement::from(actions)).unwrap();
@@ -517,16 +563,14 @@ fn decrypt_and_deserialize_plaintext_adv_canned() {
assert_eq!(AdvHeader::V0, header);
let parsed_adv = deserialize_adv_contents::<CryptoProviderImpl>(remaining).unwrap();
- if let IntermediateAdvContents::Plaintext(parc) = parsed_adv {
+ if let IntermediateAdvContents::Plaintext(adv_contents) = parsed_adv {
+ assert_eq!(PlaintextIdentityMode::Public, adv_contents.identity());
assert_eq!(
- PlaintextAdvContents {
- identity_type: PlaintextIdentityMode::Public,
- data_elements: vec![PlainDataElement::Actions(ActionsDataElement::from(
- ActionBits::default()
- ))],
- },
- parc
- )
+ vec![PlainDataElement::<Plaintext>::Actions(ActionsDataElement::from(
+ ActionBits::default()
+ ))],
+ adv_contents.data_elements().collect::<Result<Vec<_>, _>>().unwrap()
+ );
} else {
panic!("Unexpected variant: {:?}", parsed_adv);
}
@@ -653,8 +697,12 @@ fn decrypt_and_deserialize_ciphertext_with_public_adv_inside_error() {
let parsed_adv = deserialize_adv_contents::<CryptoProviderImpl>(&adv[1..]).unwrap();
if let IntermediateAdvContents::Ciphertext(eac) = parsed_adv {
assert_eq!(
- DecryptError::DeserializeError(AdvDeserializeError::InvalidDataElementHierarchy),
- eac.try_decrypt(&cipher).unwrap_err()
+ DataElementDeserializeError::DuplicateIdentityDataElement,
+ eac.try_decrypt(&cipher)
+ .unwrap()
+ .data_elements()
+ .collect::<Result<Vec<_>, _>>()
+ .unwrap_err()
)
} else {
panic!("Unexpected variant: {:?}", parsed_adv);
@@ -667,18 +715,19 @@ fn build_ciphertext_adv_contents<C: CryptoProvider>(
correct_key_seed: [u8; 32],
) -> (ArrayView<u8, { BLE_ADV_SVC_CONTENT_LEN }>, ldt_np_adv::LdtNpAdvDecrypterXtsAes128<C>) {
let hkdf = np_hkdf::NpKeySeedHkdf::<C>::new(&correct_key_seed);
- let ldt_key = hkdf.legacy_ldt_key();
let metadata_key_hmac: [u8; 32] =
hkdf.legacy_metadata_key_hmac_key().calculate_hmac(metadata_key.as_slice());
let correct_cipher = ldt_np_adv::build_np_adv_decrypter_from_key_seed(&hkdf, metadata_key_hmac);
+ let broadcast_cm =
+ SimpleBroadcastCryptoMaterial::<V0>::new(correct_key_seed, ShortMetadataKey(*metadata_key));
+
let mut builder = AdvBuilder::new(LdtIdentity::<CryptoProviderImpl>::new(
EncryptedIdentityDataElementType::Private,
salt,
- *metadata_key,
- LdtEncrypterXtsAes128::<CryptoProviderImpl>::new(&ldt_key),
+ &broadcast_cm,
));
builder.add_data_element(TxPowerDataElement::from(TxPower::try_from(3).unwrap())).unwrap();
(builder.into_advertisement().unwrap(), correct_cipher)
@@ -716,11 +765,11 @@ where
);
buf.extend_from_slice(contents);
- let raw_de = combinator::all_consuming(parse_de)(&buf).map(|(_remaining, de)| de).unwrap();
-
- let plain_des = plain_data_elements(&[raw_de]).unwrap();
+ let mut plain_des = PlainDeIterator { data: &buf, _marker: PhantomData }
+ .collect::<Result<Vec<_>, _>>()
+ .unwrap();
assert_eq!(1, plain_des.len());
- plain_des.first().unwrap().try_deserialize().unwrap()
+ plain_des.swap_remove(0)
}
fn plaintext_random_adv_contents_round_trip<I: Identity<Flavor = Plaintext>, F: Fn() -> I>(
@@ -756,31 +805,12 @@ fn plaintext_random_adv_contents_round_trip<I: Identity<Flavor = Plaintext>, F:
parse_raw_adv_contents::<CryptoProviderImpl>(&serialized.as_slice()[1..]).unwrap();
assert_eq!(AdvHeader::V0, header);
- if let RawAdvertisement::Plaintext(parc) = parsed_adv {
+ if let RawAdvertisement::Plaintext(adv_contents) = parsed_adv {
+ assert_eq!(identity_type, adv_contents.identity_type);
assert_eq!(
- PlaintextAdvRawContents {
- identity_type,
- data_elements: de_tuples
- .iter()
- .map(|(_de, de_type, bundle)| RawPlainDataElement {
- de_type: *de_type,
- contents: bundle.contents_as_slice(),
- })
- .collect()
- },
- parc
+ de_tuples.into_iter().map(|(de, _de_type, _bundle)| de).collect::<Vec<_>>(),
+ adv_contents.data_elements().collect::<Result<Vec<_>, _>>().unwrap(),
);
-
- assert_eq!(
- PlaintextAdvContents {
- identity_type,
- data_elements: de_tuples
- .into_iter()
- .map(|(de, _de_type, _bundle)| de)
- .collect(),
- },
- parc.try_deserialize().unwrap()
- )
} else {
panic!("Unexpected variant: {:?}", parsed_adv);
}
diff --git a/nearby/presence/np_adv/src/legacy/mod.rs b/nearby/presence/np_adv/src/legacy/mod.rs
index 0eb035d..59f2308 100644
--- a/nearby/presence/np_adv/src/legacy/mod.rs
+++ b/nearby/presence/np_adv/src/legacy/mod.rs
@@ -14,7 +14,10 @@
//! V0 advertisement support.
+use crate::MetadataKey;
use core::fmt;
+use crypto_provider::CryptoProvider;
+use ldt_np_adv::NP_LEGACY_METADATA_KEY_LEN;
pub mod actions;
pub mod data_elements;
@@ -31,6 +34,26 @@ pub const BLE_ADV_SVC_CONTENT_LEN: usize = 24;
/// Maximum possible DE content: packet size minus 2 for adv header & DE header
const NP_MAX_DE_CONTENT_LEN: usize = BLE_ADV_SVC_CONTENT_LEN - 2;
+/// "Short" 14-byte metadata key type employed for V0, which needs to be
+/// expanded to a regular-size 16-byte metadata key to decrypt metadata.
+#[derive(Debug, Clone, Copy, Hash, PartialEq, Eq)]
+pub struct ShortMetadataKey(pub [u8; NP_LEGACY_METADATA_KEY_LEN]);
+
+impl AsRef<[u8]> for ShortMetadataKey {
+ fn as_ref(&self) -> &[u8] {
+ &self.0
+ }
+}
+
+impl ShortMetadataKey {
+ /// Expand this short 14-byte metadata key to a 16-byte metadata key
+ /// which may be used to decrypt metadata.
+ pub fn expand<C: CryptoProvider>(&self) -> MetadataKey {
+ let expanded_bytes = np_hkdf::legacy_metadata_expanded_key::<C>(&self.0);
+ MetadataKey(expanded_bytes)
+ }
+}
+
/// Marker type to allow disambiguating between plaintext and encrypted packets at compile time.
///
/// See also [PacketFlavorEnum] for when runtime flavor checks are more suitable.
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..aca906b 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::{
@@ -87,9 +89,8 @@ impl distributions::Distribution<ActionsDataElement<Ciphertext>> for distributio
ActionType::PresenceManager => bits.set_action(PresenceManager::from(true)),
ActionType::InstantTethering => bits.set_action(InstantTethering::from(true)),
ActionType::PhoneHub => bits.set_action(PhoneHub::from(true)),
- ActionType::Finder | ActionType::FastPairSass => {
- unreachable!("not ciphertext actions")
- }
+ ActionType::Finder => bits.set_action(Finder::from(true)),
+ ActionType::FastPairSass => bits.set_action(FastPairSass::from(true)),
}
}
diff --git a/nearby/presence/np_adv/src/legacy/serialize/mod.rs b/nearby/presence/np_adv/src/legacy/serialize/mod.rs
index 68fc8a6..3746796 100644
--- a/nearby/presence/np_adv/src/legacy/serialize/mod.rs
+++ b/nearby/presence/np_adv/src/legacy/serialize/mod.rs
@@ -40,24 +40,29 @@
//! Serializing an encrypted advertisement:
//!
//! ```
-//! use np_adv::{shared_data::*, de_type::*, legacy::{de_type::*, data_elements::*, serialize::*}};
+//! use np_adv::{shared_data::*, de_type::*, legacy::{de_type::*, data_elements::*, serialize::*, *}};
+//! use np_adv::credential::{v0::V0, SimpleBroadcastCryptoMaterial};
//! use crypto_provider::CryptoProvider;
//! use crypto_provider_default::CryptoProviderImpl;
//! use ldt_np_adv::{salt_padder, LegacySalt, LdtEncrypterXtsAes128};
//!
//! // Generate these from proper CSPRNGs -- using fixed data here
-//! let metadata_key = [0x33; 14];
+//! let metadata_key = ShortMetadataKey([0x33; 14]);
//! let salt = LegacySalt::from([0x01, 0x02]);
//! let key_seed = [0x44; 32];
//! let ldt_enc = LdtEncrypterXtsAes128::<CryptoProviderImpl>::new(
//! &np_hkdf::NpKeySeedHkdf::<CryptoProviderImpl>::new(&key_seed).legacy_ldt_key()
//! );
//!
+//! let broadcast_cm = SimpleBroadcastCryptoMaterial::<V0>::new(
+//! key_seed,
+//! metadata_key,
+//! );
+//!
//! let mut builder = AdvBuilder::new(LdtIdentity::<CryptoProviderImpl>::new(
//! EncryptedIdentityDataElementType::Private,
//! salt,
-//! metadata_key,
-//! ldt_enc,
+//! &broadcast_cm,
//! ));
//!
//! builder
@@ -66,18 +71,19 @@
//!
//! let packet = builder.into_advertisement().unwrap();
//! ```
+use crate::credential::{v0::V0, BroadcastCryptoMaterial};
use crate::{
de_type::{EncryptedIdentityDataElementType, IdentityDataElementType},
legacy::{
de_type::{DataElementType, DeActualLength, DeEncodedLength, PlainDataElementType},
- Ciphertext, PacketFlavor, Plaintext, BLE_ADV_SVC_CONTENT_LEN, NP_MAX_DE_CONTENT_LEN,
+ Ciphertext, PacketFlavor, Plaintext, ShortMetadataKey, BLE_ADV_SVC_CONTENT_LEN,
+ NP_MAX_DE_CONTENT_LEN,
},
DeLengthOutOfRange, PublicIdentity,
};
use array_view::ArrayView;
use core::{convert, fmt, marker};
use crypto_provider::CryptoProvider;
-use ldt_np_adv::NP_LEGACY_METADATA_KEY_LEN;
#[cfg(test)]
mod tests;
@@ -105,7 +111,7 @@ pub trait Identity: fmt::Debug {
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 {
@@ -126,10 +132,8 @@ impl Identity for PublicIdentity {
pub struct LdtIdentity<C: CryptoProvider> {
de_type: EncryptedIdentityDataElementType,
salt: ldt_np_adv::LegacySalt,
- metadata_key: [u8; NP_LEGACY_METADATA_KEY_LEN],
+ metadata_key: ShortMetadataKey,
ldt_enc: ldt_np_adv::LdtEncrypterXtsAes128<C>,
- // keep C parameter alive for when A disappears into it
- _crypto_provider: marker::PhantomData<C>,
}
// Exclude sensitive members
@@ -140,14 +144,20 @@ impl<C: CryptoProvider> fmt::Debug for LdtIdentity<C> {
}
impl<C: CryptoProvider> LdtIdentity<C> {
- /// Build an `LdtIdentity` for the provided identity, salt, metadata key, and ldt.
- pub fn new(
+ /// Build an `LdtIdentity` for the provided identity type, salt, and
+ /// broadcast crypto-materials.
+ pub fn new<B: BroadcastCryptoMaterial<V0>>(
de_type: EncryptedIdentityDataElementType,
salt: ldt_np_adv::LegacySalt,
- metadata_key: [u8; NP_LEGACY_METADATA_KEY_LEN],
- ldt_enc: ldt_np_adv::LdtEncrypterXtsAes128<C>,
- ) -> LdtIdentity<C> {
- LdtIdentity { de_type, salt, metadata_key, ldt_enc, _crypto_provider: marker::PhantomData }
+ crypto_material: &B,
+ ) -> Self {
+ let metadata_key = crypto_material.metadata_key();
+ let key_seed = crypto_material.key_seed();
+ let key_seed_hkdf = np_hkdf::NpKeySeedHkdf::<C>::new(&key_seed);
+ let ldt_key = key_seed_hkdf.legacy_ldt_key();
+ let ldt_enc = ldt_np_adv::LdtEncrypterXtsAes128::<C>::new(&ldt_key);
+
+ Self { de_type, salt, metadata_key, ldt_enc }
}
}
@@ -169,7 +179,7 @@ impl<C: CryptoProvider> Identity for LdtIdentity<C> {
buf[0] = encode_de_header_actual_len(id_de_type_as_generic_de_type(de_type), actual_len)
.map_err(|_e| LdtPostprocessError::InvalidLength)?;
buf[1..3].copy_from_slice(self.salt.bytes().as_slice());
- buf[3..17].copy_from_slice(&self.metadata_key);
+ buf[3..17].copy_from_slice(&self.metadata_key.0);
// encrypt everything after DE header and salt
self.ldt_enc.encrypt(&mut buf[3..], &ldt_np_adv::salt_padder::<16, C>(self.salt)).map_err(
diff --git a/nearby/presence/np_adv/src/legacy/serialize/tests.rs b/nearby/presence/np_adv/src/legacy/serialize/tests.rs
index 8972313..7be30ba 100644
--- a/nearby/presence/np_adv/src/legacy/serialize/tests.rs
+++ b/nearby/presence/np_adv/src/legacy/serialize/tests.rs
@@ -12,11 +12,14 @@
// 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;
use crate::legacy::actions::NearbyShare;
use crate::{
+ credential::{v0::V0, SimpleBroadcastCryptoMaterial},
de_type::EncryptedIdentityDataElementType,
legacy::{actions::*, data_elements::*, serialize::*},
shared_data::TxPower,
@@ -27,7 +30,7 @@ use std::vec;
#[test]
fn public_identity_packet_serialization() {
- let mut builder = AdvBuilder::new(PublicIdentity::default());
+ let mut builder = AdvBuilder::new(PublicIdentity);
let tx_power = TxPower::try_from(3).unwrap();
let mut action = ActionBits::default();
@@ -49,7 +52,7 @@ fn public_identity_packet_serialization() {
#[test]
fn packet_limits_capacity() {
- let mut builder = AdvBuilder::new(PublicIdentity::default());
+ let mut builder = AdvBuilder::new(PublicIdentity);
// 2 + 1 left out of 24 payload bytes
builder.len = 21;
let mut bits = ActionBits::default();
@@ -69,22 +72,24 @@ fn packet_limits_capacity() {
#[test]
fn ldt_packet_serialization() {
// don't care about the HMAC since we're not decrypting
- let hkdf = np_hkdf::NpKeySeedHkdf::<CryptoProviderImpl>::new(&[0; 32]);
+ let key_seed = [0; 32];
+ let hkdf = np_hkdf::NpKeySeedHkdf::<CryptoProviderImpl>::new(&key_seed);
let ldt = LdtEncrypterXtsAes128::<CryptoProviderImpl>::new(&hkdf.legacy_ldt_key());
- let metadata_key = [0x33; 14];
+ let metadata_key = ShortMetadataKey([0x33; 14]);
let salt = LegacySalt::from([0x01, 0x02]);
let mut ciphertext = vec![];
- ciphertext.extend_from_slice(&metadata_key);
+ ciphertext.extend_from_slice(&metadata_key.0);
// tx power & action DEs
ciphertext.extend_from_slice(&[0x15, 0x03, 0x26, 0x00, 0x10]);
ldt.encrypt(&mut ciphertext, &salt_padder::<16, CryptoProviderImpl>(salt)).unwrap();
+ let broadcast_cm = SimpleBroadcastCryptoMaterial::<V0>::new(key_seed, metadata_key);
+
let mut builder = AdvBuilder::new(LdtIdentity::<CryptoProviderImpl>::new(
EncryptedIdentityDataElementType::Private,
salt,
- metadata_key,
- ldt,
+ &broadcast_cm,
));
let tx_power = TxPower::try_from(3).unwrap();
@@ -105,16 +110,16 @@ fn ldt_packet_serialization() {
#[test]
fn ldt_packet_cant_encrypt_without_des() {
- let metadata_key = [0x33; 14];
+ let metadata_key = ShortMetadataKey([0x33; 14]);
let salt = LegacySalt::from([0x01, 0x02]);
+ let key_seed = [0xFE; 32];
+
+ let broadcast_cm = SimpleBroadcastCryptoMaterial::<V0>::new(key_seed, metadata_key);
let builder = AdvBuilder::new(LdtIdentity::<CryptoProviderImpl>::new(
EncryptedIdentityDataElementType::Private,
salt,
- metadata_key,
- LdtEncrypterXtsAes128::<CryptoProviderImpl>::new(
- &np_hkdf::NpKeySeedHkdf::<CryptoProviderImpl>::new(&[0xFE; 32]).legacy_ldt_key(),
- ),
+ &broadcast_cm,
));
// not enough ciphertext
@@ -123,7 +128,7 @@ fn ldt_packet_cant_encrypt_without_des() {
#[test]
fn nearby_share_action() {
- let mut builder = AdvBuilder::new(PublicIdentity::default());
+ let mut builder = AdvBuilder::new(PublicIdentity);
let mut action = ActionBits::default();
action.set_action(NearbyShare::from(true));
diff --git a/nearby/presence/np_adv/src/lib.rs b/nearby/presence/np_adv/src/lib.rs
index 51a5046..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,48 +19,58 @@
//! deserialization scenarios.
#![no_std]
-#![forbid(unsafe_code)]
-#![deny(missing_docs)]
+#![allow(clippy::expect_used, clippy::indexing_slicing, clippy::panic)]
-extern crate alloc;
-extern crate core;
use crate::{
credential::{
- source::{BothCredentialSource, CredentialSource},
- v0::V0CryptoMaterial,
- v1::{BorrowableIdentityResolutionMaterial, V1CryptoMaterial},
- MatchedCredFromCred, MatchedCredential, V0Credential, V1Credential,
+ book::CredentialBook, v0::V0DiscoveryCryptoMaterial, v0::V0, v1::V1DiscoveryCryptoMaterial,
+ v1::V1, DiscoveryCryptoMaterial, MatchedCredential, ProtocolVersion,
+ ReferencedMatchedCredential,
},
+ deserialization_arena::ArenaOutOfSpace,
extended::deserialize::{
- encrypted_section::*, parse_sections, CiphertextSection, DataElements, DecryptedSection,
- IntermediateSection, PlaintextSection, Section, SectionDeserializeError,
+ encrypted_section::*, parse_sections, CiphertextSection, DataElementParseError,
+ DataElementParsingIterator, DecryptedSection, IntermediateSection, PlaintextSection,
+ Section, SectionDeserializeError,
},
legacy::deserialize::{
DecryptError, DecryptedAdvContents, IntermediateAdvContents, PlaintextAdvContents,
},
};
+
+#[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;
-use core::{fmt::Debug, marker};
+use core::fmt::Debug;
use crypto_provider::CryptoProvider;
+use deserialization_arena::{DeserializationArena, DeserializationArenaAllocator};
#[cfg(feature = "devtools")]
use extended::NP_ADV_MAX_SECTION_LEN;
+use extended::NP_V1_ADV_MAX_ENCRYPTED_SECTION_COUNT;
use legacy::{data_elements::DataElementDeserializeError, deserialize::AdvDeserializeError};
use nom::{combinator, number};
pub use strum;
+mod array_vec;
pub mod credential;
pub mod de_type;
#[cfg(test)]
mod deser_v0_tests;
#[cfg(test)]
mod deser_v1_tests;
+pub mod deserialization_arena;
pub mod extended;
+pub mod filter;
#[cfg(test)]
mod header_parse_tests;
pub mod legacy;
pub mod shared_data;
+
/// Canonical form of NP's service UUID.
///
/// Note that UUIDs are encoded in BT frames in little-endian order, so these bytes may need to be
@@ -68,73 +79,26 @@ pub const NP_SVC_UUID: [u8; 2] = [0xFC, 0xF1];
/// Parse, deserialize, decrypt, and validate a complete NP advertisement (the entire contents of
/// the service data for the NP UUID).
-pub fn deserialize_advertisement<'s, C0, C1, M, S, P>(
- adv: &'s [u8],
- cred_source: &'s S,
-) -> Result<DeserializedAdvertisement<'s, M>, AdvDeserializationError>
+pub fn deserialize_advertisement<'adv, 'cred, B, P>(
+ arena: DeserializationArena<'adv>,
+ adv: &'adv [u8],
+ cred_book: &'cred B,
+) -> Result<DeserializedAdvertisement<'adv, B::Matched>, AdvDeserializationError>
where
- C0: V0Credential<Matched<'s> = M> + 's,
- C1: V1Credential<Matched<'s> = M> + 's,
- M: MatchedCredential<'s>,
- S: BothCredentialSource<C0, C1>,
+ B: CredentialBook<'cred>,
P: CryptoProvider,
{
let (remaining, header) =
parse_adv_header(adv).map_err(|_e| AdvDeserializationError::HeaderParseError)?;
match header {
- AdvHeader::V1(header) => {
- deser_decrypt_v1::<C1, S::V1Source, P>(cred_source.v1(), remaining, header)
- .map(DeserializedAdvertisement::V1)
+ AdvHeader::V0 => {
+ deser_decrypt_v0::<B, P>(cred_book, remaining).map(DeserializedAdvertisement::V0)
}
- AdvHeader::V0 => deser_decrypt_v0::<C0, S::V0Source, P>(cred_source.v0(), remaining)
- .map(DeserializedAdvertisement::V0),
- }
-}
-
-/// Parse, deserialize, decrypt, and validate a complete V0 NP advertisement (the entire contents
-/// of the service data for the NP UUID). If the advertisement version header does not match V0,
-/// this method will return an [`AdvDeserializationError::HeaderParseError`]
-pub fn deserialize_v0_advertisement<'s, C, S, P>(
- adv: &[u8],
- cred_source: &'s S,
-) -> Result<V0AdvertisementContents<'s, C>, AdvDeserializationError>
-where
- C: V0Credential,
- S: CredentialSource<C>,
- P: CryptoProvider,
-{
- let (remaining, header) =
- parse_adv_header(adv).map_err(|_e| AdvDeserializationError::HeaderParseError)?;
-
- match header {
- AdvHeader::V0 => deser_decrypt_v0::<C, S, P>(cred_source, remaining),
- AdvHeader::V1(_) => Err(AdvDeserializationError::HeaderParseError),
- }
-}
-
-/// Parse, deserialize, decrypt, and validate a complete V1 NP advertisement (the entire contents
-/// of the service data for the NP UUID). If the advertisement version header does not match V1,
-/// this method will return an [`AdvDeserializationError::HeaderParseError`]
-pub fn deserialize_v1_advertisement<'s, C, S, P>(
- adv: &'s [u8],
- cred_source: &'s S,
-) -> Result<V1AdvertisementContents<'s, C>, AdvDeserializationError>
-where
- C: V1Credential,
- S: CredentialSource<C>,
- P: CryptoProvider,
-{
- let (remaining, header) =
- parse_adv_header(adv).map_err(|_e| AdvDeserializationError::HeaderParseError)?;
-
- match header {
- AdvHeader::V0 => Err(AdvDeserializationError::HeaderParseError),
- AdvHeader::V1(header) => deser_decrypt_v1::<C, S, P>(cred_source, remaining, header),
+ AdvHeader::V1(header) => deser_decrypt_v1::<B, P>(arena, cred_book, remaining, header)
+ .map(DeserializedAdvertisement::V1),
}
}
-type V1AdvertisementContents<'s, C> = V1AdvContents<'s, MatchedCredFromCred<'s, C>>;
-
/// The encryption scheme used for a V1 advertisement.
#[derive(Debug, Clone, PartialEq, Eq)]
pub enum V1EncryptionScheme {
@@ -157,17 +121,17 @@ pub enum AdvDecryptionError {
}
/// Decrypt, but do not further deserialize the v1 bytes, intended for developer tooling uses only.
-/// Production uses should use [deserialize_v1_advertisement] instead, which deserializes to a
+/// Production uses should use [deserialize_advertisement] instead, which deserializes to a
/// structured format and provides extra type safety.
#[cfg(feature = "devtools")]
-pub fn deser_decrypt_v1_section_bytes_for_dev_tools<S, V1, P>(
- cred_source: &S,
+pub fn deser_decrypt_v1_section_bytes_for_dev_tools<'adv, 'cred, B, P>(
+ arena: DeserializationArena<'adv>,
+ cred_book: &'cred B,
header_byte: u8,
- section_bytes: &[u8],
+ section_bytes: &'adv [u8],
) -> Result<(ArrayView<u8, NP_ADV_MAX_SECTION_LEN>, V1EncryptionScheme), AdvDecryptionError>
where
- S: CredentialSource<V1>,
- V1: V1Credential,
+ B: CredentialBook<'cred>,
P: CryptoProvider,
{
let header = V1Header { header_byte };
@@ -178,16 +142,22 @@ where
IntermediateSection::Ciphertext(section) => section,
};
- for cred in cred_source.iter() {
- let crypto_material = cred.crypto_material();
- if let Some(plaintext) =
- cipher_section.try_resolve_identity_and_decrypt::<_, P>(crypto_material)
+ let mut allocator = arena.into_allocator();
+ for (crypto_material, _) in cred_book.v1_iter() {
+ if let Some(plaintext) = cipher_section
+ .try_resolve_identity_and_decrypt::<_, P>(&mut allocator, &crypto_material)
{
+ let pt = plaintext.expect(concat!(
+ "Should not run out of space because DeserializationArenaAllocator is big ",
+ "enough to hold a single advertisement, and we exit immediately upon ",
+ "successful decryption",
+ ));
+
let encryption_scheme = match cipher_section {
CiphertextSection::SignatureEncryptedIdentity(_) => V1EncryptionScheme::Signature,
CiphertextSection::MicEncryptedIdentity(_) => V1EncryptionScheme::Mic,
};
- return Ok((plaintext, encryption_scheme));
+ return Ok((pt, encryption_scheme));
}
}
Err(AdvDecryptionError::NoMatchingCredentials)
@@ -208,26 +178,32 @@ struct ResolvableCiphertextSection<'a> {
/// Each potentially-valid section is tagged with a 0-based index derived from the original
/// section ordering as they appeared within the original advertisement to ensure
/// that the fully-deserialized advertisement may be correctly reconstructed.
-struct SectionsInProcessing<'a, M: MatchedCredential<'a>> {
- deserialized_sections: Vec<(usize, V1DeserializedSection<'a, M>)>,
- encrypted_sections: Vec<(usize, ResolvableCiphertextSection<'a>)>,
+struct SectionsInProcessing<'adv, M: MatchedCredential> {
+ deserialized_sections: ArrayVecOption<
+ (usize, V1DeserializedSection<'adv, M>),
+ { NP_V1_ADV_MAX_ENCRYPTED_SECTION_COUNT },
+ >,
+ encrypted_sections: ArrayVecOption<
+ (usize, ResolvableCiphertextSection<'adv>),
+ { NP_V1_ADV_MAX_ENCRYPTED_SECTION_COUNT },
+ >,
malformed_sections_count: usize,
}
-impl<'a, M: MatchedCredential<'a>> SectionsInProcessing<'a, M> {
+impl<'adv, M: MatchedCredential> SectionsInProcessing<'adv, M> {
/// Attempts to parse a V1 advertisement's contents after the version header
/// into a collection of not-yet-fully-deserialized sections which may
/// require credentials to be decrypted.
fn from_advertisement_contents<C: CryptoProvider>(
header: V1Header,
- remaining: &'a [u8],
+ remaining: &'adv [u8],
) -> Result<Self, AdvDeserializationError> {
let int_sections =
parse_sections(header, remaining).map_err(|_| AdvDeserializationError::ParseError {
details_hazmat: AdvDeserializationErrorDetailsHazmat::AdvertisementDeserializeError,
})?;
- let mut deserialized_sections = Vec::new();
- let mut encrypted_sections = Vec::new();
+ let mut deserialized_sections = ArrayVecOption::default();
+ let mut encrypted_sections = ArrayVecOption::default();
// keep track of ordering for later sorting during `self.finished_with_decryption_attempts()`.
for (idx, s) in int_sections.into_iter().enumerate() {
match s {
@@ -257,11 +233,12 @@ impl<'a, M: MatchedCredential<'a>> SectionsInProcessing<'a, M> {
/// to use the given credential to decrypt them. Suitable for situations
/// where iterating over credentials is relatively slow compared to
/// the cost of iterating over sections-in-memory.
- fn try_decrypt_with_credential<C, P: CryptoProvider>(&mut self, cred: &'a C)
- where
- C: V1Credential<Matched<'a> = M> + 'a,
- {
- let crypto_material = cred.crypto_material();
+ fn try_decrypt_with_credential<C: V1DiscoveryCryptoMaterial, P: CryptoProvider>(
+ &mut self,
+ arena: &mut DeserializationArenaAllocator<'adv>,
+ crypto_material: C,
+ match_data: M,
+ ) -> Result<(), ArenaOutOfSpace> {
let mut i = 0;
while i < self.encrypted_sections.len() {
let (section_idx, section): &(usize, ResolvableCiphertextSection) =
@@ -269,37 +246,34 @@ impl<'a, M: MatchedCredential<'a>> SectionsInProcessing<'a, M> {
// Fast-path: Check for an identity match, ignore if there's no identity match.
let identity_resolution_contents = &section.identity_resolution_contents;
let identity_resolution_material = match &section.ciphertext_section {
- CiphertextSection::MicEncryptedIdentity(_) => {
- BorrowableIdentityResolutionMaterial::unsigned_from_crypto_material::<_, P>(
- crypto_material,
- )
- }
- CiphertextSection::SignatureEncryptedIdentity(_) => {
- BorrowableIdentityResolutionMaterial::signed_from_crypto_material::<_, P>(
- crypto_material,
- )
- }
+ CiphertextSection::MicEncryptedIdentity(_) => crypto_material
+ .unsigned_identity_resolution_material::<P>()
+ .into_raw_resolution_material(),
+ CiphertextSection::SignatureEncryptedIdentity(_) => crypto_material
+ .signed_identity_resolution_material::<P>()
+ .into_raw_resolution_material(),
};
- match identity_resolution_contents
- .try_match::<P>(identity_resolution_material.as_raw_resolution_material())
- {
+ match identity_resolution_contents.try_match::<P>(&identity_resolution_material) {
None => {
- // Try again with another credential
+ // Try again with another section
i += 1;
continue;
}
Some(identity_match) => {
// The identity matched, so now we need to more closely scrutinize
// the provided ciphertext. Try to decrypt and parse the section.
+ let metadata_nonce = crypto_material.metadata_nonce::<P>();
let deserialization_result = match &section.ciphertext_section {
CiphertextSection::SignatureEncryptedIdentity(c) => c
.try_deserialize(
+ arena,
identity_match,
&crypto_material.signed_verification_material::<P>(),
)
.map_err(SectionDeserializeError::from),
CiphertextSection::MicEncryptedIdentity(c) => c
.try_deserialize(
+ arena,
identity_match,
&crypto_material.unsigned_verification_material::<P>(),
)
@@ -310,7 +284,8 @@ impl<'a, M: MatchedCredential<'a>> SectionsInProcessing<'a, M> {
self.deserialized_sections.push((
*section_idx,
V1DeserializedSection::Decrypted(WithMatchedCredential::new(
- cred.matched(),
+ match_data.clone(),
+ metadata_nonce,
s,
)),
));
@@ -325,56 +300,71 @@ impl<'a, M: MatchedCredential<'a>> SectionsInProcessing<'a, M> {
// the credential worked, but the section itself was bogus
self.malformed_sections_count += 1;
}
+ SectionDeserializeError::ArenaOutOfSpace => {
+ return Err(ArenaOutOfSpace)
+ }
},
}
// 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
}
}
}
+ Ok(())
}
/// Packages the current state of the deserialization process into a
- /// `V1AdvContents` representing a fully-deserialized V1 advertisement.
+ /// `V1AdvertisementContents` representing a fully-deserialized V1 advertisement.
///
/// This method should only be called after all sections were either successfully
/// decrypted or have had all relevant credentials checked against
/// them without obtaining a successful identity-match and/or subsequent
/// cryptographic verification of the section contents.
- fn finished_with_decryption_attempts(mut self) -> V1AdvContents<'a, M> {
+ fn finished_with_decryption_attempts(mut self) -> V1AdvertisementContents<'adv, M> {
// Invalid sections = malformed sections + number of encrypted sections
// which we could not manage to decrypt with any of our credentials
let invalid_sections_count = self.malformed_sections_count + self.encrypted_sections.len();
// Put the deserialized sections back into the original ordering for
- // the returned `V1AdvContents`
- self.deserialized_sections.sort_by_key(|(idx, _section)| *idx);
+ // the returned `V1AdvertisementContents`
+ // (Note: idx is unique, so unstable sort is ok)
+ self.deserialized_sections.sort_unstable_by_key(|(idx, _section)| *idx);
let ordered_sections = self.deserialized_sections.into_iter().map(|(_idx, s)| s).collect();
- V1AdvContents::new(ordered_sections, invalid_sections_count)
+ V1AdvertisementContents::new(ordered_sections, invalid_sections_count)
}
}
/// Deserialize and decrypt the contents of a v1 adv after the version header
-fn deser_decrypt_v1<'s, C, S, P>(
- cred_source: &'s S,
- remaining: &'s [u8],
+fn deser_decrypt_v1<'adv, 'cred, B, P>(
+ arena: DeserializationArena<'adv>,
+ cred_book: &'cred B,
+ remaining: &'adv [u8],
header: V1Header,
-) -> Result<V1AdvertisementContents<'s, C>, AdvDeserializationError>
+) -> Result<V1AdvertisementContents<'adv, B::Matched>, AdvDeserializationError>
where
- C: V1Credential,
- S: CredentialSource<C>,
+ B: CredentialBook<'cred>,
P: CryptoProvider,
{
let mut sections_in_processing =
- SectionsInProcessing::from_advertisement_contents::<P>(header, remaining)?;
+ SectionsInProcessing::<'_, B::Matched>::from_advertisement_contents::<P>(
+ header, remaining,
+ )?;
+
+ let mut allocator = arena.into_allocator();
// Hot loop
// We assume that iterating credentials is more expensive than iterating sections
- for cred in cred_source.iter() {
- sections_in_processing.try_decrypt_with_credential::<C, P>(cred);
+ for (crypto_material, match_data) in cred_book.v1_iter() {
+ sections_in_processing
+ .try_decrypt_with_credential::<_, P>(&mut allocator, crypto_material, match_data)
+ .expect(concat!(
+ "Should not run out of space because DeserializationArenaAllocator is big ",
+ "enough to hold a single advertisement, and we exit immediately upon ",
+ "successful decryption",
+ ));
if sections_in_processing.resolved_all_identities() {
// No need to consider the other credentials
break;
@@ -383,31 +373,29 @@ where
Ok(sections_in_processing.finished_with_decryption_attempts())
}
-type V0AdvertisementContents<'s, C> = V0AdvContents<'s, MatchedCredFromCred<'s, C>>;
-
/// Deserialize and decrypt the contents of a v0 adv after the version header
-fn deser_decrypt_v0<'s, C, S, P>(
- cred_source: &'s S,
- remaining: &[u8],
-) -> Result<V0AdvertisementContents<'s, C>, AdvDeserializationError>
+fn deser_decrypt_v0<'adv, 'cred, B, P>(
+ cred_book: &'cred B,
+ remaining: &'adv [u8],
+) -> Result<V0AdvertisementContents<'adv, B::Matched>, AdvDeserializationError>
where
- C: V0Credential,
- S: CredentialSource<C>,
+ B: CredentialBook<'cred>,
P: CryptoProvider,
{
let contents = legacy::deserialize::deserialize_adv_contents::<P>(remaining)?;
- return match contents {
- IntermediateAdvContents::Plaintext(p) => Ok(V0AdvContents::Plaintext(p)),
+ match contents {
+ IntermediateAdvContents::Plaintext(p) => Ok(V0AdvertisementContents::Plaintext(p)),
IntermediateAdvContents::Ciphertext(c) => {
- for cred in cred_source.iter() {
- let cm = cred.crypto_material();
- let ldt = cm.ldt_adv_cipher::<P>();
+ for (crypto_material, matched) in cred_book.v0_iter() {
+ let ldt = crypto_material.ldt_adv_cipher::<P>();
match c.try_decrypt(&ldt) {
Ok(c) => {
- return Ok(V0AdvContents::Decrypted(WithMatchedCredential::new(
- cred.matched(),
+ let metadata_nonce = crypto_material.metadata_nonce::<P>();
+ return Ok(V0AdvertisementContents::Decrypted(WithMatchedCredential::new(
+ matched,
+ metadata_nonce,
c,
- )))
+ )));
}
Err(e) => match e {
DecryptError::DecryptOrVerifyError => continue,
@@ -417,10 +405,11 @@ where
},
}
}
- Ok(V0AdvContents::NoMatchingCredentials)
+ Ok(V0AdvertisementContents::NoMatchingCredentials)
}
- };
+ }
}
+
/// Parse a NP advertisement header.
///
/// This can be used on all versions of advertisements since it's the header that determines the
@@ -444,88 +433,204 @@ fn parse_adv_header(adv: &[u8]) -> nom::IResult<&[u8], AdvHeader> {
_ => unreachable!(),
}
}
+
#[derive(Debug, PartialEq, Eq, Clone)]
pub(crate) enum AdvHeader {
V0,
V1(V1Header),
}
+
/// An NP advertisement with its header parsed.
+#[allow(clippy::large_enum_variant)]
#[derive(Debug, PartialEq, Eq)]
-pub enum DeserializedAdvertisement<'m, M: MatchedCredential<'m>> {
+pub enum DeserializedAdvertisement<'adv, M: MatchedCredential> {
/// V0 header has all reserved bits, so there is no data to represent other than the version
/// itself.
- V0(V0AdvContents<'m, M>),
+ V0(V0AdvertisementContents<'adv, M>),
/// V1 advertisement
- V1(V1AdvContents<'m, M>),
+ V1(V1AdvertisementContents<'adv, M>),
+}
+
+impl<'adv, M: MatchedCredential> DeserializedAdvertisement<'adv, M> {
+ /// Attempts to cast this deserialized advertisement into the `V0AdvertisementContents`
+ /// variant. If the underlying advertisement is not V0, this will instead return `None`.
+ pub fn into_v0(self) -> Option<V0AdvertisementContents<'adv, M>> {
+ match self {
+ Self::V0(x) => Some(x),
+ _ => None,
+ }
+ }
+ /// Attempts to cast this deserialized advertisement into the `V1AdvertisementContents`
+ /// variant. If the underlying advertisement is not V1, this will instead return `None`.
+ pub fn into_v1(self) -> Option<V1AdvertisementContents<'adv, M>> {
+ match self {
+ Self::V1(x) => Some(x),
+ _ => None,
+ }
+ }
}
+
/// The contents of a deserialized and decrypted V1 advertisement.
#[derive(Debug, PartialEq, Eq)]
-pub struct V1AdvContents<'m, M: MatchedCredential<'m>> {
- sections: Vec<V1DeserializedSection<'m, M>>,
+pub struct V1AdvertisementContents<'adv, M: MatchedCredential> {
+ sections: ArrayVecOption<V1DeserializedSection<'adv, M>, NP_V1_ADV_MAX_ENCRYPTED_SECTION_COUNT>,
invalid_sections: usize,
}
-impl<'m, M: MatchedCredential<'m>> V1AdvContents<'m, M> {
- fn new(sections: Vec<V1DeserializedSection<'m, M>>, invalid_sections: usize) -> Self {
+
+impl<'adv, M: MatchedCredential> V1AdvertisementContents<'adv, M> {
+ fn new(
+ sections: ArrayVecOption<
+ V1DeserializedSection<'adv, M>,
+ NP_V1_ADV_MAX_ENCRYPTED_SECTION_COUNT,
+ >,
+ invalid_sections: usize,
+ ) -> Self {
Self { sections, invalid_sections }
}
+
/// Destructures this V1 advertisement into just the sections
/// which could be successfully deserialized and decrypted
- pub fn into_valid_sections(self) -> Vec<V1DeserializedSection<'m, M>> {
+ pub fn into_sections(
+ self,
+ ) -> ArrayVecOption<V1DeserializedSection<'adv, M>, NP_V1_ADV_MAX_ENCRYPTED_SECTION_COUNT> {
self.sections
}
+
/// The sections that could be successfully deserialized and decrypted
- pub fn sections(&self) -> impl Iterator<Item = &V1DeserializedSection<M>> {
+ pub fn sections(&self) -> impl ExactSizeIterator<Item = &V1DeserializedSection<M>> {
self.sections.iter()
}
+
/// The number of sections that could not be parsed or decrypted.
pub fn invalid_sections_count(&self) -> usize {
self.invalid_sections
}
}
+
/// Advertisement content that was either already plaintext or has been decrypted.
#[derive(Debug, PartialEq, Eq)]
-pub enum V0AdvContents<'m, M: MatchedCredential<'m>> {
+pub enum V0AdvertisementContents<'adv, M: MatchedCredential> {
/// Contents of an originally plaintext advertisement
- Plaintext(PlaintextAdvContents),
+ Plaintext(PlaintextAdvContents<'adv>),
/// Contents that was ciphertext in the original advertisement, and has been decrypted
/// with the credential in the [MatchedCredential]
- Decrypted(WithMatchedCredential<'m, M, DecryptedAdvContents>),
+ Decrypted(WithMatchedCredential<M, DecryptedAdvContents>),
/// The advertisement was encrypted, but no credentials matched
NoMatchingCredentials,
}
+
/// Advertisement content that was either already plaintext or has been decrypted.
#[derive(Debug, PartialEq, Eq)]
-pub enum V1DeserializedSection<'m, M: MatchedCredential<'m>> {
+pub enum V1DeserializedSection<'adv, M: MatchedCredential> {
/// Section that was plaintext in the original advertisement
- Plaintext(PlaintextSection),
+ Plaintext(PlaintextSection<'adv>),
/// Section that was ciphertext in the original advertisement, and has been decrypted
/// with the credential in the [MatchedCredential]
- Decrypted(WithMatchedCredential<'m, M, DecryptedSection>),
+ Decrypted(WithMatchedCredential<M, DecryptedSection<'adv>>),
}
-impl<'m, M> Section for V1DeserializedSection<'m, M>
+
+impl<'adv, M> Section<'adv, DataElementParseError> for V1DeserializedSection<'adv, M>
where
- M: MatchedCredential<'m>,
+ M: MatchedCredential,
{
- type Iterator<'d> = DataElements<'d> where Self: 'd;
- fn data_elements(&'_ self) -> Self::Iterator<'_> {
+ type Iterator = DataElementParsingIterator<'adv>;
+
+ fn iter_data_elements(&self) -> Self::Iterator {
match self {
- V1DeserializedSection::Plaintext(p) => p.data_elements(),
- V1DeserializedSection::Decrypted(d) => d.contents.data_elements(),
+ V1DeserializedSection::Plaintext(p) => p.iter_data_elements(),
+ V1DeserializedSection::Decrypted(d) => d.contents.iter_data_elements(),
}
}
}
+
+/// 16-byte metadata keys, as employed for metadata decryption.
+#[derive(Debug, Clone, Copy, Hash, Eq, PartialEq)]
+pub struct MetadataKey(pub [u8; 16]);
+
+impl AsRef<[u8]> for MetadataKey {
+ fn as_ref(&self) -> &[u8] {
+ &self.0
+ }
+}
+
+/// Common trait to deserialized, decrypted V0 advs and V1 sections which
+/// exposes relevant data about matched identities.
+pub trait HasIdentityMatch {
+ /// The protocol version for which this advertisement
+ /// content has an identity-match.
+ type Version: ProtocolVersion;
+
+ /// Gets the decrypted plaintext version-specific
+ /// metadata key for the associated identity.
+ 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)]
+pub enum MatchedMetadataDecryptionError<M: MatchedCredential> {
+ /// Retrieving the encrypted metadata failed for one reason
+ /// or another, so we didn't get a chance to try decryption.
+ RetrievalFailed(<M as MatchedCredential>::EncryptedMetadataFetchError),
+ /// The encrypted metadata could be retrieved, but it did
+ /// not successfully decrypt against the matched identity.
+ /// This could be an indication of data corruption or
+ /// of malformed crypto on the sender-side.
+ DecryptionFailed,
+}
+
/// Decrypted advertisement content with the [MatchedCredential] from the credential that decrypted
-/// it.
+/// it, along with any other information which is relevant to the identity-match.
#[derive(Debug, PartialEq, Eq)]
-pub struct WithMatchedCredential<'m, M: MatchedCredential<'m>, T> {
+pub struct WithMatchedCredential<M: MatchedCredential, T: HasIdentityMatch> {
matched: M,
+ /// The 12-byte metadata nonce as derived from the key-seed HKDF
+ /// to be used for decrypting the encrypted metadata in the attached
+ /// matched-credential.
+ metadata_nonce: [u8; 12],
contents: T,
- // the compiler sees 'm as unused
- marker: marker::PhantomData<&'m ()>,
}
-impl<'m, M: MatchedCredential<'m>, T> WithMatchedCredential<'m, M, T> {
- fn new(matched: M, contents: T) -> Self {
- Self { matched, contents, marker: marker::PhantomData }
+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 {
@@ -535,12 +640,41 @@ impl<'m, M: MatchedCredential<'m>, T> WithMatchedCredential<'m, M, T> {
pub fn contents(&self) -> &T {
&self.contents
}
+
+ #[cfg(any(test, feature = "alloc"))]
+ fn decrypt_metadata_from_fetch<C: CryptoProvider>(
+ &self,
+ encrypted_metadata: &[u8],
+ ) -> Result<Vec<u8>, MatchedMetadataDecryptionError<M>> {
+ let metadata_key = self.contents.metadata_key();
+ <<T as HasIdentityMatch>::Version as ProtocolVersion>::decrypt_metadata::<C>(
+ self.metadata_nonce,
+ metadata_key,
+ encrypted_metadata,
+ )
+ .map_err(|_| MatchedMetadataDecryptionError::DecryptionFailed)
+ }
+
+ #[cfg(any(test, feature = "alloc"))]
+ /// Attempts to decrypt the encrypted metadata
+ /// associated with the matched credential
+ /// based on the details of the identity-match.
+ pub fn decrypt_metadata<C: CryptoProvider>(
+ &self,
+ ) -> Result<Vec<u8>, MatchedMetadataDecryptionError<M>> {
+ self.matched
+ .fetch_encrypted_metadata()
+ .map_err(|e| MatchedMetadataDecryptionError::RetrievalFailed(e))
+ .and_then(|x| Self::decrypt_metadata_from_fetch::<C>(self, x.as_ref()))
+ }
}
+
/// Data in a V1 advertisement header.
#[derive(Debug, PartialEq, Eq, Clone, Copy)]
pub(crate) struct V1Header {
header_byte: u8,
}
+
const PROTOCOL_VERSION_LEGACY: u8 = 0;
const PROTOCOL_VERSION_EXTENDED: u8 = 1;
@@ -582,6 +716,8 @@ pub enum AdvDeserializationErrorDetailsHazmat {
InvalidDataElementHierarchy,
/// Must have an identity DE
MissingIdentity,
+ /// Non-identity DE contents must not be empty
+ NoPublicDataElements,
}
impl From<AdvDeserializeError> for AdvDeserializationError {
@@ -593,27 +729,18 @@ impl From<AdvDeserializeError> for AdvDeserializationError {
AdvDeserializationErrorDetailsHazmat::AdvertisementDeserializeError,
}
}
- AdvDeserializeError::DataElementDeserializeError(e) => {
- AdvDeserializationError::ParseError {
- details_hazmat:
- AdvDeserializationErrorDetailsHazmat::V0DataElementDeserializeError(e),
- }
- }
AdvDeserializeError::TooManyTopLevelDataElements => {
AdvDeserializationError::ParseError {
details_hazmat:
AdvDeserializationErrorDetailsHazmat::TooManyTopLevelDataElements,
}
}
- AdvDeserializeError::InvalidDataElementHierarchy => {
- AdvDeserializationError::ParseError {
- details_hazmat:
- AdvDeserializationErrorDetailsHazmat::InvalidDataElementHierarchy,
- }
- }
AdvDeserializeError::MissingIdentity => AdvDeserializationError::ParseError {
details_hazmat: AdvDeserializationErrorDetailsHazmat::MissingIdentity,
},
+ AdvDeserializeError::NoPublicDataElements => AdvDeserializationError::ParseError {
+ details_hazmat: AdvDeserializationErrorDetailsHazmat::NoPublicDataElements,
+ },
}
}
}
@@ -634,4 +761,4 @@ pub enum PlaintextIdentityMode {
///
/// Used when serializing V0 advertisements or V1 sections.
#[derive(Default, Debug)]
-pub struct PublicIdentity {}
+pub struct PublicIdentity;
diff --git a/nearby/presence/np_adv/tests/examples_v0.rs b/nearby/presence/np_adv/tests/examples_v0.rs
index e30e12f..f7b7d5e 100644
--- a/nearby/presence/np_adv/tests/examples_v0.rs
+++ b/nearby/presence/np_adv/tests/examples_v0.rs
@@ -1,5 +1,4 @@
// 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
@@ -12,57 +11,103 @@
// 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;
use np_adv::{
credential::{
- simple::SimpleV0Credential, source::SliceCredentialSource,
- v0::MinimumFootprintV0CryptoMaterial,
+ book::CredentialBookBuilder,
+ v0::{V0DiscoveryCredential, V0},
+ EmptyMatchedCredential, MatchableCredential, MetadataMatchedCredential,
+ SimpleBroadcastCryptoMaterial,
},
de_type::*,
- deserialize_v0_advertisement,
- legacy::deserialize::*,
+ legacy::{deserialize::*, ShortMetadataKey},
shared_data::*,
*,
};
+use serde::{Deserialize, Serialize};
#[test]
fn v0_deser_plaintext() {
- let creds =
- SliceCredentialSource::<SimpleV0Credential<MinimumFootprintV0CryptoMaterial, ()>>::new(&[]);
- let adv = deserialize_v0_advertisement::<_, _, CryptoProviderImpl>(
+ let cred_book = CredentialBookBuilder::<EmptyMatchedCredential>::build_cached_slice_book::<
+ 0,
+ 0,
+ CryptoProviderImpl,
+ >(&[], &[]);
+ let arena = deserialization_arena!();
+ let adv = deserialize_advertisement::<_, CryptoProviderImpl>(
+ arena,
&[
0x00, // adv header
0x03, // public identity
0x15, 0x03, // Length 1 Tx Power DE with value 3
],
- &creds,
+ &cred_book,
)
- .unwrap();
+ .expect("Should be a valid advertisement")
+ .into_v0()
+ .expect("Should be V0");
match adv {
- V0AdvContents::Plaintext(p) => {
+ V0AdvertisementContents::Plaintext(p) => {
assert_eq!(PlaintextIdentityMode::Public, p.identity());
assert_eq!(
- vec![&PlainDataElement::TxPower(TxPowerDataElement::from(
+ vec![PlainDataElement::TxPower(TxPowerDataElement::from(
TxPower::try_from(3).unwrap()
- )),],
- p.data_elements().collect::<Vec<_>>()
+ ))],
+ p.data_elements().collect::<Result<Vec<_>, _>>().unwrap()
);
}
_ => panic!("this example is plaintext"),
}
}
+/// Sample contents for some encrypted identity metadata
+/// which includes just a name and an e-mail address.
+#[derive(Debug, Eq, PartialEq, Serialize, Deserialize)]
+struct IdentityMetadata {
+ name: String,
+ email: String,
+}
+
+impl IdentityMetadata {
+ /// Serialize this identity metadata to a JSON byte-string.
+ fn to_bytes(&self) -> Vec<u8> {
+ serde_json::to_vec(&self).expect("serialization should always succeed")
+ }
+ /// Attempt to deserialize identity metadata from a JSON byte-string.
+ fn try_from_bytes(serialized: &[u8]) -> Option<Self> {
+ serde_json::from_slice(serialized).ok()
+ }
+}
+
#[test]
fn v0_deser_ciphertext() {
+ // These are kept fixed in this example for reproducibility.
+ // In practice, these should instead be derived from a cryptographically-secure
+ // random number generator.
let key_seed = [0x11_u8; 32];
let metadata_key: [u8; NP_LEGACY_METADATA_KEY_LEN] = [0x33; NP_LEGACY_METADATA_KEY_LEN];
+ let metadata_key = ShortMetadataKey(metadata_key);
+
+ let broadcast_cm = SimpleBroadcastCryptoMaterial::<V0>::new(key_seed, metadata_key);
let hkdf = np_hkdf::NpKeySeedHkdf::<CryptoProviderImpl>::new(&key_seed);
let metadata_key_hmac: [u8; 32] =
- hkdf.legacy_metadata_key_hmac_key().calculate_hmac(&metadata_key);
+ hkdf.legacy_metadata_key_hmac_key().calculate_hmac(&metadata_key.0);
+
+ // Serialize and encrypt some identity metadata (sender-side)
+ let sender_metadata =
+ IdentityMetadata { name: "Alice".to_string(), email: "alice@gmail.com".to_string() };
+ let sender_metadata_bytes = sender_metadata.to_bytes();
+ let encrypted_sender_metadata = MetadataMatchedCredential::<Vec<u8>>::encrypt_from_plaintext::<
+ _,
+ _,
+ CryptoProviderImpl,
+ >(&broadcast_cm, &sender_metadata_bytes);
// output of building a packet using AdvBuilder
let adv = &[
@@ -74,28 +119,45 @@ fn v0_deser_ciphertext() {
0xF9,
];
- let credentials: [SimpleV0Credential<_, [u8; 32]>; 1] = [SimpleV0Credential::new(
- MinimumFootprintV0CryptoMaterial::new(key_seed, metadata_key_hmac),
- key_seed,
- )];
- let cred_source = SliceCredentialSource::new(credentials.as_slice());
+ let discovery_credential = V0DiscoveryCredential::new(key_seed, metadata_key_hmac);
+
+ let credentials: [MatchableCredential<V0, MetadataMatchedCredential<_>>; 1] =
+ [MatchableCredential { discovery_credential, match_data: encrypted_sender_metadata }];
+
+ let cred_book = CredentialBookBuilder::build_cached_slice_book::<0, 0, CryptoProviderImpl>(
+ &credentials,
+ &[],
+ );
- let matched = match deserialize_v0_advertisement::<_, _, CryptoProviderImpl>(adv, &cred_source)
- .unwrap()
+ let matched = match deserialize_advertisement::<_, CryptoProviderImpl>(
+ deserialization_arena!(),
+ adv,
+ &cred_book,
+ )
+ .expect("Should be a valid advertisement")
+ .into_v0()
+ .expect("Should be V0")
{
- V0AdvContents::Decrypted(c) => c,
+ V0AdvertisementContents::Decrypted(c) => c,
_ => panic!("this examples is ciphertext"),
};
- assert_eq!(&key_seed, matched.matched_credential().matched_data());
+ let decrypted_metadata_bytes = matched
+ .decrypt_metadata::<CryptoProviderImpl>()
+ .expect("Sender metadata should be decryptable");
+ let decrypted_metadata = IdentityMetadata::try_from_bytes(&decrypted_metadata_bytes)
+ .expect("Sender metadata should be deserializable");
+
+ assert_eq!(sender_metadata, decrypted_metadata);
+
let decrypted = matched.contents();
assert_eq!(EncryptedIdentityDataElementType::Private, decrypted.identity_type());
- assert_eq!(&metadata_key, decrypted.metadata_key());
+ assert_eq!(metadata_key, decrypted.metadata_key());
assert_eq!(
- vec![&PlainDataElement::TxPower(TxPowerDataElement::from(TxPower::try_from(3).unwrap())),],
- decrypted.data_elements().collect::<Vec<_>>()
+ vec![PlainDataElement::TxPower(TxPowerDataElement::from(TxPower::try_from(3).unwrap())),],
+ decrypted.data_elements().collect::<Result<Vec<_>, _>>().unwrap(),
);
}
diff --git a/nearby/presence/np_adv/tests/examples_v1.rs b/nearby/presence/np_adv/tests/examples_v1.rs
index ddb7acf..6e93a3d 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;
@@ -20,11 +22,11 @@ use np_adv::extended::NP_V1_ADV_MAX_PUBLIC_SECTION_COUNT;
use np_adv::shared_data::TxPower;
use np_adv::{
credential::{
- simple::SimpleV1Credential, source::SliceCredentialSource,
- v1::MinimumFootprintV1CryptoMaterial,
+ book::CredentialBookBuilder,
+ v1::{SimpleSignedBroadcastCryptoMaterial, V1DiscoveryCredential, V1},
+ EmptyMatchedCredential, MatchableCredential, MetadataMatchedCredential,
},
de_type::*,
- deserialize_v1_advertisement,
extended::{
deserialize::{Section, VerificationMode},
serialize::{AdvBuilder, SignedEncryptedSectionEncoder},
@@ -32,6 +34,7 @@ use np_adv::{
PlaintextIdentityMode, *,
};
use np_hkdf::v1_salt;
+use serde::{Deserialize, Serialize};
#[test]
fn v1_deser_plaintext() {
@@ -43,10 +46,18 @@ fn v1_deser_plaintext() {
section_builder.add_to_advertisement();
let adv = adv_builder.into_advertisement();
- let creds =
- SliceCredentialSource::<SimpleV1Credential<MinimumFootprintV1CryptoMaterial, ()>>::new(&[]);
+ let cred_book = CredentialBookBuilder::<EmptyMatchedCredential>::build_cached_slice_book::<
+ 0,
+ 0,
+ CryptoProviderImpl,
+ >(&[], &[]);
+
+ let arena = deserialization_arena!();
let contents =
- deserialize_v1_advertisement::<_, _, CryptoProviderImpl>(adv.as_slice(), &creds).unwrap();
+ deserialize_advertisement::<_, CryptoProviderImpl>(arena, adv.as_slice(), &cred_book)
+ .expect("Should be a valid advertisemement")
+ .into_v1()
+ .expect("Should be V1");
assert_eq!(0, contents.invalid_sections_count());
@@ -59,7 +70,7 @@ fn v1_deser_plaintext() {
_ => panic!("this is a plaintext adv"),
};
assert_eq!(PlaintextIdentityMode::Public, section.identity());
- let data_elements = section.data_elements().collect::<Vec<_>>();
+ let data_elements = section.iter_data_elements().collect::<Result<Vec<_>, _>>().unwrap();
assert_eq!(1, data_elements.len());
let de = &data_elements[0];
@@ -68,24 +79,61 @@ fn v1_deser_plaintext() {
assert_eq!(&[6], de.contents());
}
+/// Sample contents for some encrypted identity metadata
+/// which consists of a UUID together with a display name
+/// and a general location.
+#[derive(Debug, Eq, PartialEq, Serialize, Deserialize)]
+struct IdentityMetadata {
+ uuid: String,
+ display_name: String,
+ location: String,
+}
+
+impl IdentityMetadata {
+ /// Serialize this identity metadata to a json byte-string.
+ fn to_bytes(&self) -> Vec<u8> {
+ serde_json::to_vec(self).expect("Identity metadata serialization is infallible")
+ }
+ /// Attempt to deserialize identity metadata from a json byte-string.
+ fn try_from_bytes(serialized: &[u8]) -> Option<Self> {
+ serde_json::from_slice(serialized).ok()
+ }
+}
+
#[test]
fn v1_deser_ciphertext() {
// identity material
let mut rng = <CryptoProviderImpl as CryptoProvider>::CryptoRng::new();
let metadata_key: [u8; 16] = rng.gen();
+ let metadata_key = MetadataKey(metadata_key);
let key_pair = np_ed25519::KeyPair::<CryptoProviderImpl>::generate();
let key_seed = rng.gen();
let hkdf = np_hkdf::NpKeySeedHkdf::<CryptoProviderImpl>::new(&key_seed);
+ let broadcast_cm =
+ SimpleSignedBroadcastCryptoMaterial::new(key_seed, metadata_key, key_pair.private_key());
+
+ // Serialize and encrypt some identity metadata (sender-side)
+ let sender_metadata = IdentityMetadata {
+ uuid: "378845e1-2616-420d-86f5-674177a7504d".to_string(),
+ display_name: "Alice".to_string(),
+ location: "Wonderland".to_string(),
+ };
+ let sender_metadata_bytes = sender_metadata.to_bytes();
+ let encrypted_sender_metadata = MetadataMatchedCredential::<Vec<u8>>::encrypt_from_plaintext::<
+ _,
+ _,
+ CryptoProviderImpl,
+ >(&broadcast_cm, &sender_metadata_bytes);
+
// prepare advertisement
let mut adv_builder = AdvBuilder::new(AdvertisementType::Encrypted);
+
let mut section_builder = adv_builder
- .section_builder(SignedEncryptedSectionEncoder::new_random_salt(
+ .section_builder(SignedEncryptedSectionEncoder::<CryptoProviderImpl>::new_random_salt(
&mut rng,
EncryptedIdentityDataElementType::Private,
- &metadata_key,
- &key_pair,
- &hkdf,
+ &broadcast_cm,
))
.unwrap();
section_builder
@@ -94,37 +142,50 @@ fn v1_deser_ciphertext() {
section_builder.add_to_advertisement();
let adv = adv_builder.into_advertisement();
- let cred_array: [SimpleV1Credential<_, [u8; 32]>; 1] = [SimpleV1Credential::new(
- MinimumFootprintV1CryptoMaterial::new(
- 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),
- key_pair.public(),
- ),
+ let discovery_credential = V1DiscoveryCredential::new(
key_seed,
- )];
- let creds = SliceCredentialSource::new(&cred_array);
+ [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().to_bytes(),
+ );
+
+ let credentials: [MatchableCredential<V1, MetadataMatchedCredential<_>>; 1] =
+ [MatchableCredential { discovery_credential, match_data: encrypted_sender_metadata }];
+ let cred_book = CredentialBookBuilder::build_cached_slice_book::<0, 0, CryptoProviderImpl>(
+ &[],
+ &credentials,
+ );
+ let arena = deserialization_arena!();
let contents =
- deserialize_v1_advertisement::<_, _, CryptoProviderImpl>(adv.as_slice(), &creds).unwrap();
+ deserialize_advertisement::<_, CryptoProviderImpl>(arena, adv.as_slice(), &cred_book)
+ .expect("Should be a valid advertisement")
+ .into_v1()
+ .expect("Should be V1");
assert_eq!(0, contents.invalid_sections_count());
let sections = contents.sections().collect::<Vec<_>>();
assert_eq!(1, sections.len());
- let matched_credential = match &sections[0] {
+ let matched: &WithMatchedCredential<_, _> = match &sections[0] {
V1DeserializedSection::Decrypted(d) => d,
_ => panic!("this is a ciphertext adv"),
};
- assert_eq!(&key_seed, matched_credential.matched_credential().matched_data());
- let section = matched_credential.contents();
+ let decrypted_metadata_bytes = matched
+ .decrypt_metadata::<CryptoProviderImpl>()
+ .expect("Sender metadata should be decryptable");
+ let decrypted_metadata = IdentityMetadata::try_from_bytes(&decrypted_metadata_bytes)
+ .expect("Sender metadata should be deserializable");
+ assert_eq!(sender_metadata, decrypted_metadata);
+
+ let section = matched.contents();
assert_eq!(EncryptedIdentityDataElementType::Private, section.identity_type());
assert_eq!(VerificationMode::Signature, section.verification_mode());
- assert_eq!(&metadata_key, section.metadata_key());
+ assert_eq!(metadata_key, section.metadata_key());
- let data_elements = section.data_elements().collect::<Vec<_>>();
+ let data_elements = section.iter_data_elements().collect::<Result<Vec<_>, _>>().unwrap();
assert_eq!(1, data_elements.len());
let de = &data_elements[0];
@@ -137,10 +198,14 @@ fn v1_deser_ciphertext() {
fn v1_deser_no_section() {
let adv_builder = AdvBuilder::new(AdvertisementType::Plaintext);
let adv = adv_builder.into_advertisement();
- let creds =
- SliceCredentialSource::<SimpleV1Credential<MinimumFootprintV1CryptoMaterial, ()>>::new(&[]);
+ let cred_book = CredentialBookBuilder::<EmptyMatchedCredential>::build_cached_slice_book::<
+ 0,
+ 0,
+ CryptoProviderImpl,
+ >(&[], &[]);
+ let arena = deserialization_arena!();
let v1_deserialize_error =
- deserialize_v1_advertisement::<_, _, CryptoProviderImpl>(adv.as_slice(), &creds)
+ deserialize_advertisement::<_, CryptoProviderImpl>(arena, adv.as_slice(), &cred_book)
.expect_err(" Expected an error");
assert_eq!(
v1_deserialize_error,
@@ -170,10 +235,14 @@ fn v1_deser_plaintext_over_max_sections() {
]
.as_slice(),
);
- let creds =
- SliceCredentialSource::<SimpleV1Credential<MinimumFootprintV1CryptoMaterial, ()>>::new(&[]);
+ let cred_book = CredentialBookBuilder::<EmptyMatchedCredential>::build_cached_slice_book::<
+ 0,
+ 0,
+ CryptoProviderImpl,
+ >(&[], &[]);
+ let arena = deserialization_arena!();
assert_eq!(
- deserialize_v1_advertisement::<_, _, CryptoProviderImpl>(adv.as_slice(), &creds)
+ deserialize_advertisement::<_, CryptoProviderImpl>(arena, adv.as_slice(), &cred_book)
.unwrap_err(),
AdvDeserializationError::ParseError {
details_hazmat: AdvDeserializationErrorDetailsHazmat::AdvertisementDeserializeError
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..5e1328c
--- /dev/null
+++ b/nearby/presence/np_adv_dynamic/src/extended.rs
@@ -0,0 +1,350 @@
+// 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: Box<AdvBuilder>,
+}
+
+impl From<AdvBuilder> for BoxedAdvBuilder {
+ fn from(adv_builder: AdvBuilder) -> Self {
+ let adv_builder = Box::new(adv_builder);
+ 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_owning_section_builder<C: CryptoProvider, S: Into<BoxedSectionBuilder<AdvBuilder, C>>>(
+ maybe_section_builder: Result<S, (AdvBuilder, AddSectionError)>,
+) -> Result<BoxedSectionBuilder<AdvBuilder, C>, (BoxedAdvBuilder, BoxedAddSectionError)> {
+ match maybe_section_builder {
+ Ok(section_builder) => Ok(section_builder.into()),
+ Err((adv_builder, err)) => Err((adv_builder.into(), err.into())),
+ }
+}
+
+fn wrap_mut_ref_section_builder<
+ 'a,
+ C: CryptoProvider,
+ S: Into<BoxedSectionBuilder<&'a mut AdvBuilder, C>>,
+>(
+ maybe_section_builder: Result<S, AddSectionError>,
+) -> Result<BoxedSectionBuilder<&'a mut AdvBuilder, C>, BoxedAddSectionError> {
+ let section_builder = maybe_section_builder?;
+ Ok(section_builder.into())
+}
+
+impl BoxedAdvBuilder {
+ /// Gets the current count of sections which have been added
+ /// to this advertisement builder. Does not count currently-
+ /// outstanding section builders.
+ pub fn section_count(&self) -> usize {
+ self.adv_builder.section_count()
+ }
+
+ /// Create a section builder using the given identity,
+ /// taking ownership of this advertisement builder.
+ ///
+ /// Unlike `BoxedAdvBuilder#section_builder`, the returned
+ /// section builder will take ownership of this advertisement
+ /// builder, if the operation was successful. Otherwise,
+ /// this advertisement builder will be returned back to the
+ /// caller unaltered as part of the `Err` arm.
+ pub fn into_section_builder<C: CryptoProvider>(
+ self,
+ identity: BoxedIdentity<C>,
+ ) -> Result<BoxedSectionBuilder<AdvBuilder, C>, (Self, BoxedAddSectionError)> {
+ match identity {
+ BoxedIdentity::PublicIdentity => wrap_owning_section_builder(
+ self.adv_builder.into_section_builder(PublicSectionEncoder::default()),
+ ),
+ BoxedIdentity::MicEncrypted(ident) => {
+ wrap_owning_section_builder(self.adv_builder.into_section_builder(ident))
+ }
+ BoxedIdentity::SignedEncrypted(ident) => {
+ wrap_owning_section_builder(self.adv_builder.into_section_builder(ident))
+ }
+ }
+ }
+
+ /// 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<&mut AdvBuilder, C>, BoxedAddSectionError> {
+ match identity {
+ BoxedIdentity::PublicIdentity => wrap_mut_ref_section_builder(
+ self.adv_builder.section_builder(PublicSectionEncoder::default()),
+ ),
+ BoxedIdentity::MicEncrypted(ident) => {
+ wrap_mut_ref_section_builder(self.adv_builder.section_builder(ident))
+ }
+ BoxedIdentity::SignedEncrypted(ident) => {
+ wrap_mut_ref_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<R: AsMut<AdvBuilder>, C: CryptoProvider> {
+ /// A builder for a public section.
+ Public(Box<SectionBuilder<R, PublicSectionEncoder>>),
+ /// A builder for a MIC-verified section.
+ MicEncrypted(Box<SectionBuilder<R, MicEncryptedSectionEncoder<C>>>),
+ /// A builder for a signature-verified section.
+ SignedEncrypted(Box<SectionBuilder<R, SignedEncryptedSectionEncoder<C>>>),
+}
+
+impl<C: CryptoProvider> BoxedSectionBuilder<AdvBuilder, C> {
+ /// Gets the 0-based index of the section currently under construction
+ /// in the context of the containing advertisement.
+ pub fn section_index(&self) -> usize {
+ match self {
+ BoxedSectionBuilder::Public(x) => x.section_index(),
+ BoxedSectionBuilder::MicEncrypted(x) => x.section_index(),
+ BoxedSectionBuilder::SignedEncrypted(x) => x.section_index(),
+ }
+ }
+ /// Add this builder to the advertisement that created it,
+ /// returning the containing advertisement builder.
+ pub fn add_to_advertisement(self) -> BoxedAdvBuilder {
+ let adv_builder = match self {
+ BoxedSectionBuilder::Public(x) => x.add_to_advertisement(),
+ BoxedSectionBuilder::MicEncrypted(x) => x.add_to_advertisement(),
+ BoxedSectionBuilder::SignedEncrypted(x) => x.add_to_advertisement(),
+ };
+ BoxedAdvBuilder::from(adv_builder)
+ }
+}
+
+impl<'a, C: CryptoProvider> BoxedSectionBuilder<&'a mut AdvBuilder, C> {
+ /// 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(),
+ }
+ }
+}
+
+impl<R: AsMut<AdvBuilder>, C: CryptoProvider> BoxedSectionBuilder<R, 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,
+ }
+ }
+ /// Gets the derived salt of the next DE to be added to the section,
+ /// if this section-builder corresponds to an encrypted section.
+ /// Otherwise, returns nothing.
+ ///
+ /// Suitable for scenarios (like FFI) where a closure would be inappropriate
+ /// for DE construction, and interaction with the client is preferred.
+ pub fn next_de_salt(&self) -> Option<DeSalt<C>> {
+ match self {
+ BoxedSectionBuilder::Public(_) => None,
+ BoxedSectionBuilder::MicEncrypted(x) => Some(x.next_de_salt()),
+ BoxedSectionBuilder::SignedEncrypted(x) => Some(x.next_de_salt()),
+ }
+ }
+
+ /// 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<R: AsMut<AdvBuilder>, C: CryptoProvider> From<SectionBuilder<R, PublicSectionEncoder>>
+ for BoxedSectionBuilder<R, C>
+{
+ fn from(section_builder: SectionBuilder<R, PublicSectionEncoder>) -> Self {
+ BoxedSectionBuilder::Public(Box::new(section_builder))
+ }
+}
+
+impl<R: AsMut<AdvBuilder>, C: CryptoProvider> From<SectionBuilder<R, MicEncryptedSectionEncoder<C>>>
+ for BoxedSectionBuilder<R, C>
+{
+ fn from(section_builder: SectionBuilder<R, MicEncryptedSectionEncoder<C>>) -> Self {
+ BoxedSectionBuilder::MicEncrypted(Box::new(section_builder))
+ }
+}
+
+impl<R: AsMut<AdvBuilder>, C: CryptoProvider>
+ From<SectionBuilder<R, SignedEncryptedSectionEncoder<C>>> for BoxedSectionBuilder<R, C>
+{
+ fn from(section_builder: SectionBuilder<R, SignedEncryptedSectionEncoder<C>>) -> Self {
+ BoxedSectionBuilder::SignedEncrypted(Box::new(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 6f79c82..bce44c6 100644
--- a/nearby/presence/np_c_ffi/Cargo.lock
+++ b/nearby/presence/np_c_ffi/Cargo.lock
@@ -25,38 +25,35 @@ dependencies = [
]
[[package]]
-name = "aes-gcm-siv"
-version = "0.11.1"
+name = "aes-gcm"
+version = "0.10.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "ae0784134ba9375416d469ec31e7c5f9fa94405049cf08c5ce5b4698be673e0d"
+checksum = "831010a0f742e1209b3bcea8fab6a8e149051ba6099432c8cb2cc117dec3ead1"
dependencies = [
"aead",
"aes",
"cipher",
"ctr",
- "polyval",
+ "ghash",
"subtle",
- "zeroize",
]
[[package]]
-name = "ahash"
-version = "0.8.3"
+name = "aes-gcm-siv"
+version = "0.11.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "2c99f64d1e06488f620f932677e24bc6e2897582980441ae90a671415bd7ec2f"
+checksum = "ae0784134ba9375416d469ec31e7c5f9fa94405049cf08c5ce5b4698be673e0d"
dependencies = [
- "cfg-if",
- "once_cell",
- "version_check",
+ "aead",
+ "aes",
+ "cipher",
+ "ctr",
+ "polyval",
+ "subtle",
+ "zeroize",
]
[[package]]
-name = "allocator-api2"
-version = "0.2.16"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "0942ffc6dcaadf03badf6e6a2d0228460359d5e34b57ccdc720b7382dfbd5ec5"
-
-[[package]]
name = "array_ref"
version = "0.1.0"
@@ -70,7 +67,7 @@ version = "0.2.14"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d9b39be18770d11421cdb1b9947a45dd3f37e93092cbf377614828a319d5fee8"
dependencies = [
- "hermit-abi 0.1.19",
+ "hermit-abi",
"libc",
"winapi",
]
@@ -88,12 +85,24 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4c7f02d4ea65f2c1853089ffd8d2787bdbc63de2f0d29dedbcf8ccdfa0ccd4cf"
[[package]]
+name = "base64ct"
+version = "1.6.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "8c3c1a368f70d6cf7302d78f8f7093da241fb8e8807c05cc9e51a125895a6d5b"
+
+[[package]]
name = "bitflags"
version = "1.3.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a"
[[package]]
+name = "bitflags"
+version = "2.4.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "327762f6e5a765692301e5bb513e0d9fef63be86bbc14528052b1cd3e6f03e07"
+
+[[package]]
name = "block-buffer"
version = "0.10.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -112,10 +121,21 @@ dependencies = [
]
[[package]]
+name = "bssl-crypto"
+version = "0.1.0"
+dependencies = [
+ "bssl-sys",
+]
+
+[[package]]
+name = "bssl-sys"
+version = "0.1.0"
+
+[[package]]
name = "bytes"
-version = "1.4.0"
+version = "1.5.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "89b2fd2a0dcf38d7971e2194b6b6eebab45ae01067456a7fd93d5547a61b70be"
+checksum = "a2bd12c1caf447e69cd4528f47f94d203fd2582878ecb9e9465484c4148a8223"
[[package]]
name = "cbc"
@@ -146,12 +166,6 @@ dependencies = [
]
[[package]]
-name = "cc"
-version = "1.0.79"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "50d30906286121d95be3d479533b458f87493b30a4b5f79a607db8f5d11aa91f"
-
-[[package]]
name = "cfg-if"
version = "1.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -174,7 +188,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4ea181bf566f71cb9a5d17a59e1871af638180a18fb0035c92ae62b705207123"
dependencies = [
"atty",
- "bitflags",
+ "bitflags 1.3.2",
"clap_lex",
"indexmap",
"strsim",
@@ -193,24 +207,24 @@ dependencies = [
[[package]]
name = "const-oid"
-version = "0.9.3"
+version = "0.9.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "6340df57935414636969091153f35f68d9f00bbc8fb4a9c6054706c213e6c6bc"
+checksum = "28c122c3980598d243d63d9a704629a2d748d101f278052ff068be5a4423ab6f"
[[package]]
name = "cpufeatures"
-version = "0.2.9"
+version = "0.2.11"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "a17b76ff3a4162b0b27f354a0c87015ddad39d35f9c0c36607a3bdd175dde1f1"
+checksum = "ce420fe07aecd3e67c5f910618fe65e94158f6dcc0adf44e00d69ce2bdfe0fd0"
dependencies = [
"libc",
]
[[package]]
name = "crypto-bigint"
-version = "0.5.2"
+version = "0.5.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "cf4c2f4e1afd912bc40bfd6fed5d9dc1f288e0ba01bfcc835cc5bc3eb13efe15"
+checksum = "740fe28e594155f10cfc383984cbefd529d7396050557148f79cb0f621204124"
dependencies = [
"generic-array",
"rand_core",
@@ -232,6 +246,17 @@ dependencies = [
[[package]]
name = "crypto_provider"
version = "0.1.0"
+dependencies = [
+ "tinyvec",
+]
+
+[[package]]
+name = "crypto_provider_boringssl"
+version = "0.1.0"
+dependencies = [
+ "bssl-crypto",
+ "crypto_provider",
+]
[[package]]
name = "crypto_provider_default"
@@ -239,6 +264,7 @@ version = "0.1.0"
dependencies = [
"cfg-if",
"crypto_provider",
+ "crypto_provider_boringssl",
"crypto_provider_rustcrypto",
]
@@ -248,6 +274,7 @@ version = "0.1.0"
dependencies = [
"aead",
"aes",
+ "aes-gcm",
"aes-gcm-siv",
"cbc",
"cfg-if",
@@ -277,9 +304,9 @@ dependencies = [
[[package]]
name = "curve25519-dalek"
-version = "4.0.0-rc.3"
+version = "4.1.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "436ace70fc06e06f7f689d2624dc4e2f0ea666efb5aa704215f7249ae6e047a7"
+checksum = "e89b8c6a2e4b1f45971ad09761aafb85514a84744b67a95e32c3cc1352d1f65c"
dependencies = [
"cfg-if",
"cpufeatures",
@@ -289,24 +316,25 @@ dependencies = [
"platforms",
"rustc_version",
"subtle",
+ "zeroize",
]
[[package]]
name = "curve25519-dalek-derive"
-version = "0.1.0"
+version = "0.1.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "83fdaf97f4804dcebfa5862639bc9ce4121e82140bec2a987ac5140294865b5b"
+checksum = "f46882e17999c6cc590af592290432be3bce0428cb0d5f8b6715e4dc7b383eb3"
dependencies = [
"proc-macro2",
"quote",
- "syn 2.0.23",
+ "syn 2.0.38",
]
[[package]]
name = "der"
-version = "0.7.7"
+version = "0.7.8"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "0c7ed52955ce76b1554f509074bb357d3fb8ac9b51288a65a3fd480d1dfba946"
+checksum = "fffa369a668c8af7dbf8b5e56c9f744fbd399949ed171606040001947de40b1c"
dependencies = [
"const-oid",
"zeroize",
@@ -325,30 +353,34 @@ dependencies = [
[[package]]
name = "ed25519"
-version = "2.2.1"
+version = "2.2.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "5fb04eee5d9d907f29e80ee6b0e78f7e2c82342c63e3580d8c4f69d9d5aad963"
+checksum = "115531babc129696a58c64a4fef0a8bf9e9698629fb97e9e40767d235cfbcd53"
dependencies = [
+ "pkcs8",
"signature",
]
[[package]]
name = "ed25519-dalek"
-version = "2.0.0-rc.3"
+version = "2.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "faa8e9049d5d72bfc12acbc05914731b5322f79b5e2f195e9f2d705fca22ab4c"
+checksum = "1f628eaec48bfd21b865dc2950cfa014450c01d2fa2b69a86c2fd5844ec523c0"
dependencies = [
"curve25519-dalek",
"ed25519",
"rand_core",
+ "serde",
"sha2",
+ "subtle",
+ "zeroize",
]
[[package]]
name = "elliptic-curve"
-version = "0.13.5"
+version = "0.13.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "968405c8fdc9b3bf4df0a6638858cc0b52462836ab6b1c87377785dd09cf1c0b"
+checksum = "d97ca172ae9dc9f9b779a6e3a65d308f2af74e5b8c921299075bdb4a0370e914"
dependencies = [
"base16ct",
"crypto-bigint",
@@ -365,33 +397,19 @@ dependencies = [
[[package]]
name = "errno"
-version = "0.3.1"
+version = "0.3.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "4bcfec3a70f97c962c307b2d2c56e358cf1d00b558d74262b5f929ee8cc7e73a"
+checksum = "ac3e13f66a2f95e32a39eaa81f6b95d42878ca0e1db0c7543723dfe12557e860"
dependencies = [
- "errno-dragonfly",
"libc",
"windows-sys",
]
[[package]]
-name = "errno-dragonfly"
-version = "0.1.2"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "aa68f1b12764fab894d2755d2518754e71b4fd80ecfb822714a1206c2aab39bf"
-dependencies = [
- "cc",
- "libc",
-]
-
-[[package]]
name = "fastrand"
-version = "1.9.0"
+version = "2.0.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "e51093e27b0797c359783294ca4f0a911c270184cb10f85783b118614a1501be"
-dependencies = [
- "instant",
-]
+checksum = "25cbce373ec4653f1a01a31e8a5e5ec0c622dc27ff9c4e6606eefef5cbbed4a5"
[[package]]
name = "ff"
@@ -405,9 +423,9 @@ dependencies = [
[[package]]
name = "fiat-crypto"
-version = "0.1.20"
+version = "0.2.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "e825f6987101665dea6ec934c09ec6d721de7bc1bf92248e1d5810c8cd636b77"
+checksum = "a481586acf778f1b1455424c343f71124b048ffa5f4fc3f8f6ae9dc432dcb3c7"
[[package]]
name = "generic-array"
@@ -432,6 +450,16 @@ dependencies = [
]
[[package]]
+name = "ghash"
+version = "0.5.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d930750de5717d2dd0b8c0d42c076c0e884c81a73e6cab859bbd2339c71e3e40"
+dependencies = [
+ "opaque-debug",
+ "polyval",
+]
+
+[[package]]
name = "group"
version = "0.13.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -446,11 +474,7 @@ dependencies = [
name = "handle_map"
version = "0.1.0"
dependencies = [
- "crypto_provider",
- "hashbrown 0.14.0",
- "lock_api",
- "portable-atomic",
- "spin 0.9.8",
+ "lock_adapter",
]
[[package]]
@@ -460,16 +484,6 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8a9ee70c43aaf417c914396645a0fa852624801b24ebb7ae78fe8272889ac888"
[[package]]
-name = "hashbrown"
-version = "0.14.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "2c6201b9ff9fd90a5a3bac2e56a830d0caa509576f0e503818ee82c181b3437a"
-dependencies = [
- "ahash",
- "allocator-api2",
-]
-
-[[package]]
name = "heck"
version = "0.4.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -485,12 +499,6 @@ dependencies = [
]
[[package]]
-name = "hermit-abi"
-version = "0.3.2"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "443144c8cdadd93ebf52ddb4056d257f5b52c04d3c804e657d19eb73fc33668b"
-
-[[package]]
name = "hkdf"
version = "0.12.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -515,7 +523,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bd070e393353796e801d209ad339e89596eb4c8d430d18ede6a1cced8fafbd99"
dependencies = [
"autocfg",
- "hashbrown 0.12.3",
+ "hashbrown",
]
[[package]]
@@ -529,30 +537,10 @@ dependencies = [
]
[[package]]
-name = "instant"
-version = "0.1.12"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "7a5bbe824c507c5da5956355e86a746d82e0e1464f65d862cc5e71da70e94b2c"
-dependencies = [
- "cfg-if",
-]
-
-[[package]]
-name = "io-lifetimes"
-version = "1.0.11"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "eae7b9aee968036d54dce06cebaefd919e4472e753296daccd6d344e3e2df0c2"
-dependencies = [
- "hermit-abi 0.3.2",
- "libc",
- "windows-sys",
-]
-
-[[package]]
name = "itoa"
-version = "1.0.8"
+version = "1.0.9"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "62b02a5381cc465bd3041d84623d0fa3b66738b52b8e2fc3bab8ad63ab032f4a"
+checksum = "af150ab688ff2122fcef229be89cb50dd66af9e01a4ff320cc137eecc9bacc38"
[[package]]
name = "lazy_static"
@@ -560,7 +548,7 @@ version = "1.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646"
dependencies = [
- "spin 0.5.2",
+ "spin",
]
[[package]]
@@ -592,43 +580,31 @@ dependencies = [
[[package]]
name = "libc"
-version = "0.2.147"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "b4668fb0ea861c1df094127ac5f1da3409a82116a4ba74fca2e58ef927159bb3"
-
-[[package]]
-name = "libc_alloc"
-version = "1.0.4"
+version = "0.2.149"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "6a090348b66d90d8507e30f0d2bd88e5a5c454bd1733fc6d617cbc3471bf69ea"
+checksum = "a08173bc88b7955d1b3145aa561539096c421ac8debde8cbc3612ec635fee29b"
[[package]]
name = "linux-raw-sys"
-version = "0.3.8"
+version = "0.4.10"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "ef53942eb7bf7ff43a617b3e2c1c4a5ecf5944a7c1bc12d7ee39bbb15e5c1519"
+checksum = "da2479e8c062e40bf0066ffa0bc823de0a9368974af99c9f6df941d2c231e03f"
[[package]]
-name = "lock_api"
-version = "0.4.10"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "c1cc9717a20b1bb222f333e6a92fd32f7d8a18ddc5a3191a11af45dcbf4dcd16"
-dependencies = [
- "autocfg",
- "scopeguard",
-]
+name = "lock_adapter"
+version = "0.1.0"
[[package]]
name = "log"
-version = "0.4.19"
+version = "0.4.20"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "b06a4cde4c0f271a446782e3eff8de789548ce57dbc8eca9292c27f4a42004b4"
+checksum = "b5e6163cb8c49088c2c36f57875e58ccd8c87c7427f7fbd50ea6710b2f3f2e8f"
[[package]]
name = "memchr"
-version = "2.5.0"
+version = "2.6.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "2dffe52ecf27772e601905b7522cb4ef790d2cc203488bbd0e2fe85fcb74566d"
+checksum = "f665ee40bc4a3c5590afb1e9677db74a508659dfd71e126420da8274909a0167"
[[package]]
name = "minimal-lexical"
@@ -658,7 +634,6 @@ dependencies = [
"nom",
"np_ed25519",
"np_hkdf",
- "rand",
"sink",
"strum",
"strum_macros",
@@ -667,15 +642,23 @@ dependencies = [
]
[[package]]
+name = "np_adv_dynamic"
+version = "0.1.0"
+dependencies = [
+ "array_view",
+ "crypto_provider",
+ "np_adv",
+ "sink",
+ "thiserror",
+]
+
+[[package]]
name = "np_c_ffi"
version = "0.1.0"
dependencies = [
"cbindgen",
- "crypto_provider_default",
- "libc_alloc",
+ "lock_adapter",
"np_ffi_core",
- "panic-abort",
- "spin 0.9.8",
]
[[package]]
@@ -696,8 +679,12 @@ dependencies = [
"crypto_provider",
"crypto_provider_default",
"handle_map",
+ "lazy_static",
+ "ldt_np_adv",
+ "lock_adapter",
"np_adv",
- "spin 0.9.8",
+ "np_adv_dynamic",
+ "np_hkdf",
]
[[package]]
@@ -710,12 +697,6 @@ dependencies = [
]
[[package]]
-name = "once_cell"
-version = "1.18.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "dd8b5dd2ae5ed71462c540258bedcb51965123ad7e7ccf4b9a8cafaa4a63576d"
-
-[[package]]
name = "opaque-debug"
version = "0.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -723,9 +704,9 @@ checksum = "624a8340c38c1b80fd549087862da4ba43e08858af025b236e509b6649fc13d5"
[[package]]
name = "os_str_bytes"
-version = "6.5.1"
+version = "6.6.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "4d5d9eb14b174ee9aa2ef96dc2b94637a2d4b6e7cb873c7e171f0c20c6cf3eac"
+checksum = "e2355d85b9a3786f481747ced0e0ff2ba35213a1f9bd406ed906554d7af805a1"
[[package]]
name = "p256"
@@ -738,16 +719,20 @@ dependencies = [
]
[[package]]
-name = "panic-abort"
-version = "0.3.2"
+name = "pkcs8"
+version = "0.10.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "4e20e6499bbbc412f280b04a42346b356c6fa0753d5fd22b7bd752ff34c778ee"
+checksum = "f950b2377845cebe5cf8b5165cb3cc1a5e0fa5cfa3e1f7f55707d8fd82e0a7b7"
+dependencies = [
+ "der",
+ "spki",
+]
[[package]]
name = "platforms"
-version = "3.0.2"
+version = "3.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "e3d7ddaed09e0eb771a79ab0fd64609ba0afb0a8366421957936ad14cbd13630"
+checksum = "14e6ab3f592e6fb464fc9712d8d6e6912de6473954635fd76a589d832cffcbb0"
[[package]]
name = "polyval"
@@ -762,12 +747,6 @@ dependencies = [
]
[[package]]
-name = "portable-atomic"
-version = "1.3.3"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "767eb9f07d4a5ebcb39bbf2d452058a93c011373abf6832e24194a1c3f004794"
-
-[[package]]
name = "ppv-lite86"
version = "0.2.17"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -784,18 +763,18 @@ dependencies = [
[[package]]
name = "proc-macro2"
-version = "1.0.63"
+version = "1.0.69"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "7b368fba921b0dce7e60f5e04ec15e565b3303972b42bcfde1d0713b881959eb"
+checksum = "134c189feb4956b20f6f547d2cf727d4c0fe06722b20a0eec87ed445a97f92da"
dependencies = [
"unicode-ident",
]
[[package]]
name = "quote"
-version = "1.0.29"
+version = "1.0.33"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "573015e8ab27661678357f27dc26460738fd2b6c86e46f386fde94cb5d913105"
+checksum = "5267fca4496028628a95160fc423a33e8b2e6af8a5302579e322e4b520293cae"
dependencies = [
"proc-macro2",
]
@@ -806,6 +785,8 @@ version = "0.8.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "34af8d1a0e25924bc5b7c43c079c942339d8f0a8b57c39049bef581b46327404"
dependencies = [
+ "libc",
+ "rand_chacha",
"rand_core",
]
@@ -830,11 +811,11 @@ dependencies = [
[[package]]
name = "redox_syscall"
-version = "0.3.5"
+version = "0.4.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "567664f262709473930a4bf9e51bf2ebf3348f2e748ccc50dea20646858f8f29"
+checksum = "4722d768eff46b75989dd134e5c353f0d6296e5aaa3132e776cbdb56be7731aa"
dependencies = [
- "bitflags",
+ "bitflags 1.3.2",
]
[[package]]
@@ -848,13 +829,12 @@ dependencies = [
[[package]]
name = "rustix"
-version = "0.37.23"
+version = "0.38.21"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "4d69718bf81c6127a49dc64e44a742e8bb9213c0ff8869a22c308f84c1d4ab06"
+checksum = "2b426b0506e5d50a7d8dafcf2e81471400deb602392c7dd110815afb4eaf02a3"
dependencies = [
- "bitflags",
+ "bitflags 2.4.1",
"errno",
- "io-lifetimes",
"libc",
"linux-raw-sys",
"windows-sys",
@@ -862,27 +842,21 @@ dependencies = [
[[package]]
name = "rustversion"
-version = "1.0.13"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "dc31bd9b61a32c31f9650d18add92aa83a49ba979c143eefd27fe7177b05bd5f"
-
-[[package]]
-name = "ryu"
version = "1.0.14"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "fe232bdf6be8c8de797b22184ee71118d63780ea42ac85b61d1baa6d3b782ae9"
+checksum = "7ffc183a10b4478d04cbbbfc96d0873219d962dd5accaff2ffbd4ceb7df837f4"
[[package]]
-name = "scopeguard"
-version = "1.1.0"
+name = "ryu"
+version = "1.0.15"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "d29ab0c6d3fc0ee92fe66e2d99f700eab17a8d57d1c1d3b748380fb20baa78cd"
+checksum = "1ad4cc8da4ef723ed60bced201181d83791ad433213d8c24efffda1eec85d741"
[[package]]
name = "sec1"
-version = "0.7.2"
+version = "0.7.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "f0aec48e813d6b90b15f0b8948af3c63483992dee44c03e9930b3eebdabe046e"
+checksum = "d3e97a565f76233a6003f9f5c54be1d9c5bdfa3eccfb189469f11ec4901c47dc"
dependencies = [
"base16ct",
"der",
@@ -893,35 +867,35 @@ dependencies = [
[[package]]
name = "semver"
-version = "1.0.17"
+version = "1.0.20"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "bebd363326d05ec3e2f532ab7660680f3b02130d780c299bca73469d521bc0ed"
+checksum = "836fa6a3e1e547f9a2c4040802ec865b5d85f4014efe00555d7090a3dcaa1090"
[[package]]
name = "serde"
-version = "1.0.166"
+version = "1.0.190"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "d01b7404f9d441d3ad40e6a636a7782c377d2abdbe4fa2440e2edcc2f4f10db8"
+checksum = "91d3c334ca1ee894a2c6f6ad698fe8c435b76d504b13d436f0685d648d6d96f7"
dependencies = [
"serde_derive",
]
[[package]]
name = "serde_derive"
-version = "1.0.166"
+version = "1.0.190"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "5dd83d6dde2b6b2d466e14d9d1acce8816dedee94f735eac6395808b3483c6d6"
+checksum = "67c5609f394e5c2bd7fc51efda478004ea80ef42fee983d5c67a65e34f32c0e3"
dependencies = [
"proc-macro2",
"quote",
- "syn 2.0.23",
+ "syn 2.0.38",
]
[[package]]
name = "serde_json"
-version = "1.0.100"
+version = "1.0.108"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "0f1e14e89be7aa4c4b78bdbdc9eb5bf8517829a600ae8eaa39a6e1d960b5185c"
+checksum = "3d1c7e3eac408d115102c4c24ad393e0821bb3a5df4d506a80f85f7a742a526b"
dependencies = [
"itoa",
"ryu",
@@ -930,9 +904,9 @@ dependencies = [
[[package]]
name = "sha2"
-version = "0.10.7"
+version = "0.10.8"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "479fb9d862239e610720565ca91403019f2f00410f1864c5aa7479b950a76ed8"
+checksum = "793db75ad2bcafc3ffa7c68b215fee268f537982cd901d132f89c6343f3a3dc8"
dependencies = [
"cfg-if",
"cpufeatures",
@@ -959,12 +933,13 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6e63cff320ae2c57904679ba7cb63280a3dc4613885beafb148ee7bf9aa9042d"
[[package]]
-name = "spin"
-version = "0.9.8"
+name = "spki"
+version = "0.7.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "6980e8d7511241f8acf4aebddbb1ff938df5eebe98691418c4468d0b72a96a67"
+checksum = "9d1e996ef02c474957d681f1b05213dfb0abab947b446a62d37770b23500184a"
dependencies = [
- "lock_api",
+ "base64ct",
+ "der",
]
[[package]]
@@ -975,21 +950,21 @@ checksum = "73473c0e59e6d5812c5dfe2a064a6444949f089e20eec9a2e5506596494e4623"
[[package]]
name = "strum"
-version = "0.24.1"
+version = "0.25.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "063e6045c0e62079840579a7e47a355ae92f60eb74daaf156fb1e84ba164e63f"
+checksum = "290d54ea6f91c969195bdbcd7442c8c2a2ba87da8bf60a7ee86a235d4bc1e125"
[[package]]
name = "strum_macros"
-version = "0.24.3"
+version = "0.25.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "1e385be0d24f186b4ce2f9982191e7101bb737312ad61c1f2f984f34bcf85d59"
+checksum = "23dc1fa9ac9c169a78ba62f0b841814b7abae11bdd047b9c58f893439e309ea0"
dependencies = [
"heck",
"proc-macro2",
"quote",
"rustversion",
- "syn 1.0.109",
+ "syn 2.0.38",
]
[[package]]
@@ -1011,9 +986,9 @@ dependencies = [
[[package]]
name = "syn"
-version = "2.0.23"
+version = "2.0.38"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "59fb7d6d8281a51045d62b8eb3a7d1ce347b76f312af50cd3dc0af39c87c1737"
+checksum = "e96b79aaa137db8f61e26363a0c9b47d8b4ec75da28b7d1d614c2303e232408b"
dependencies = [
"proc-macro2",
"quote",
@@ -1022,11 +997,10 @@ dependencies = [
[[package]]
name = "tempfile"
-version = "3.6.0"
+version = "3.8.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "31c0432476357e58790aaa47a8efb0c5138f137343f3b5f23bd36a27e3b0a6d6"
+checksum = "7ef1adac450ad7f4b3c28589471ade84f25f731a7a0fe30d71dfa9f60fd808e5"
dependencies = [
- "autocfg",
"cfg-if",
"fastrand",
"redox_syscall",
@@ -1036,9 +1010,9 @@ dependencies = [
[[package]]
name = "termcolor"
-version = "1.2.0"
+version = "1.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "be55cf8942feac5c765c2c993422806843c9a9a45d4d5c407ad6dd2ea95eb9b6"
+checksum = "6093bad37da69aab9d123a8091e4be0aa4a03e4d601ec641c327398315f62b64"
dependencies = [
"winapi-util",
]
@@ -1050,6 +1024,26 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "222a222a5bfe1bba4a77b45ec488a741b3cb8872e5e499451fd7d0129c9c7c3d"
[[package]]
+name = "thiserror"
+version = "1.0.51"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "f11c217e1416d6f036b870f14e0413d480dbf28edbee1f877abaf0206af43bb7"
+dependencies = [
+ "thiserror-impl",
+]
+
+[[package]]
+name = "thiserror-impl"
+version = "1.0.51"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "01742297787513b79cf8e29d1056ede1313e2420b7b3b15d0a768b4921f549df"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "syn 2.0.38",
+]
+
+[[package]]
name = "tinyvec"
version = "1.6.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -1066,15 +1060,15 @@ dependencies = [
[[package]]
name = "typenum"
-version = "1.16.0"
+version = "1.17.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "497961ef93d974e23eb6f433eb5fe1b7930b659f06d12dec6fc44a8f554c0bba"
+checksum = "42ff0bf0c66b8238c6f3b578df37d0b7848e55df8577b3f74f92a69acceeb825"
[[package]]
name = "unicode-ident"
-version = "1.0.10"
+version = "1.0.12"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "22049a19f4a68748a168c0fc439f9516686aa045927ff767eca0a85101fb6e73"
+checksum = "3354b9ac3fae1ff6755cb6db53683adb661634f67557942dea4facebec0fee4b"
[[package]]
name = "universal-hash"
@@ -1116,9 +1110,9 @@ checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6"
[[package]]
name = "winapi-util"
-version = "0.1.5"
+version = "0.1.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "70ec6ce85bb158151cae5e5c87f95a8e97d2c0c4b001223f33a334e3ce5de178"
+checksum = "f29e6f9198ba0d26b4c9f07dbe6f9ed633e1f3d5b8b414090084349e46a52596"
dependencies = [
"winapi",
]
@@ -1140,9 +1134,9 @@ dependencies = [
[[package]]
name = "windows-targets"
-version = "0.48.1"
+version = "0.48.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "05d4b17490f70499f20b9e791dcf6a299785ce8af4d709018206dc5b4953e95f"
+checksum = "9a2fa6e2155d7247be68c096456083145c183cbbbc2764150dda45a87197940c"
dependencies = [
"windows_aarch64_gnullvm",
"windows_aarch64_msvc",
@@ -1155,51 +1149,51 @@ dependencies = [
[[package]]
name = "windows_aarch64_gnullvm"
-version = "0.48.0"
+version = "0.48.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "91ae572e1b79dba883e0d315474df7305d12f569b400fcf90581b06062f7e1bc"
+checksum = "2b38e32f0abccf9987a4e3079dfb67dcd799fb61361e53e2882c3cbaf0d905d8"
[[package]]
name = "windows_aarch64_msvc"
-version = "0.48.0"
+version = "0.48.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "b2ef27e0d7bdfcfc7b868b317c1d32c641a6fe4629c171b8928c7b08d98d7cf3"
+checksum = "dc35310971f3b2dbbf3f0690a219f40e2d9afcf64f9ab7cc1be722937c26b4bc"
[[package]]
name = "windows_i686_gnu"
-version = "0.48.0"
+version = "0.48.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "622a1962a7db830d6fd0a69683c80a18fda201879f0f447f065a3b7467daa241"
+checksum = "a75915e7def60c94dcef72200b9a8e58e5091744960da64ec734a6c6e9b3743e"
[[package]]
name = "windows_i686_msvc"
-version = "0.48.0"
+version = "0.48.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "4542c6e364ce21bf45d69fdd2a8e455fa38d316158cfd43b3ac1c5b1b19f8e00"
+checksum = "8f55c233f70c4b27f66c523580f78f1004e8b5a8b659e05a4eb49d4166cca406"
[[package]]
name = "windows_x86_64_gnu"
-version = "0.48.0"
+version = "0.48.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "ca2b8a661f7628cbd23440e50b05d705db3686f894fc9580820623656af974b1"
+checksum = "53d40abd2583d23e4718fddf1ebec84dbff8381c07cae67ff7768bbf19c6718e"
[[package]]
name = "windows_x86_64_gnullvm"
-version = "0.48.0"
+version = "0.48.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "7896dbc1f41e08872e9d5e8f8baa8fdd2677f29468c4e156210174edc7f7b953"
+checksum = "0b7b52767868a23d5bab768e390dc5f5c55825b6d30b86c844ff2dc7414044cc"
[[package]]
name = "windows_x86_64_msvc"
-version = "0.48.0"
+version = "0.48.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "1a515f5799fe4961cb532f983ce2b23082366b898e52ffbce459c86f67c8378a"
+checksum = "ed94fce61571a4006852b7389a063ab983c02eb1bb37b47f8272ce92d06d9538"
[[package]]
name = "x25519-dalek"
-version = "2.0.0-rc.3"
+version = "2.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "ec7fae07da688e17059d5886712c933bb0520f15eff2e09cfa18e30968f4e63a"
+checksum = "fb66477291e7e8d2b0ff1bcb900bf29489a9692816d79874bea351e7a8b6de96"
dependencies = [
"curve25519-dalek",
"rand_core",
diff --git a/nearby/presence/np_c_ffi/Cargo.toml b/nearby/presence/np_c_ffi/Cargo.toml
index 2dede26..86bbd53 100644
--- a/nearby/presence/np_c_ffi/Cargo.toml
+++ b/nearby/presence/np_c_ffi/Cargo.toml
@@ -5,17 +5,17 @@ edition = "2021"
publish = false
[dependencies]
-# TODO: We need to make this configurable for this crate and for np_ffi below it.
-crypto_provider_default = { path = "../../crypto/crypto_provider_default", features = ["rustcrypto"] }
np_ffi_core = { path = "../np_ffi_core" }
-
-spin = "0.9.8"
-libc_alloc = "1.0.4"
-panic-abort = "0.3.2"
+lock_adapter = {path = "../../util/lock_adapter"}
[build-dependencies]
cbindgen = "0.24.5"
+[features]
+default = ["rustcrypto"]
+rustcrypto = ["np_ffi_core/rustcrypto"]
+boringssl = ["np_ffi_core/boringssl"]
+
[lib]
# boringssl and bssl-sys are built as a static lib, so we need to as well
crate-type = ["staticlib"]
diff --git a/nearby/presence/np_c_ffi/deny.toml b/nearby/presence/np_c_ffi/deny.toml
new file mode 100644
index 0000000..ca51e11
--- /dev/null
+++ b/nearby/presence/np_c_ffi/deny.toml
@@ -0,0 +1,213 @@
+# This template contains all of the possible sections and their default values
+
+# Note that all fields that take a lint level have these possible values:
+# * deny - An error will be produced and the check will fail
+# * warn - A warning will be produced, but the check will not fail
+# * allow - No warning or error will be produced, though in some cases a note
+# will be
+
+# The values provided in this template are the default values that will be used
+# when any section or field is not specified in your own configuration
+
+# If 1 or more target triples (and optionally, target_features) are specified,
+# only the specified targets will be checked when running `cargo deny check`.
+# This means, if a particular package is only ever used as a target specific
+# dependency, such as, for example, the `nix` crate only being used via the
+# `target_family = "unix"` configuration, that only having windows targets in
+# this list would mean the nix crate, as well as any of its exclusive
+# dependencies not shared by any other crates, would be ignored, as the target
+# list here is effectively saying which targets you are building for.
+targets = [
+ # The triple can be any string, but only the target triples built in to
+ # rustc (as of 1.40) can be checked against actual config expressions
+ #{ triple = "x86_64-unknown-linux-musl" },
+ # You can also specify which target_features you promise are enabled for a
+ # particular target. target_features are currently not validated against
+ # the actual valid features supported by the target architecture.
+ #{ triple = "wasm32-unknown-unknown", features = ["atomics"] },
+]
+
+# This section is considered when running `cargo deny check advisories`
+# More documentation for the advisories section can be found here:
+# https://embarkstudios.github.io/cargo-deny/checks/advisories/cfg.html
+[advisories]
+# The path where the advisory database is cloned/fetched into
+db-path = "~/.cargo/advisory-db"
+# The url(s) of the advisory databases to use
+db-urls = ["https://github.com/rustsec/advisory-db"]
+# The lint level for security vulnerabilities
+vulnerability = "deny"
+# The lint level for unmaintained crates
+unmaintained = "warn"
+# The lint level for crates that have been yanked from their source registry
+yanked = "warn"
+# The lint level for crates with security notices. Note that as of
+# 2019-12-17 there are no security notice advisories in
+# https://github.com/rustsec/advisory-db
+notice = "warn"
+# A list of advisory IDs to ignore. Note that ignored advisories will still
+# output a note when they are encountered.
+ignore = [
+ #"RUSTSEC-0000-0000",
+]
+# Threshold for security vulnerabilities, any vulnerability with a CVSS score
+# lower than the range specified will be ignored. Note that ignored advisories
+# will still output a note when they are encountered.
+# * None - CVSS Score 0.0
+# * Low - CVSS Score 0.1 - 3.9
+# * Medium - CVSS Score 4.0 - 6.9
+# * High - CVSS Score 7.0 - 8.9
+# * Critical - CVSS Score 9.0 - 10.0
+#severity-threshold =
+
+# If this is true, then cargo deny will use the git executable to fetch advisory database.
+# If this is false, then it uses a built-in git library.
+# Setting this to true can be helpful if you have special authentication requirements that cargo-deny does not support.
+# See Git Authentication for more information about setting up git authentication.
+#git-fetch-with-cli = true
+
+# This section is considered when running `cargo deny check licenses`
+# More documentation for the licenses section can be found here:
+# https://embarkstudios.github.io/cargo-deny/checks/licenses/cfg.html
+[licenses]
+# The lint level for crates which do not have a detectable license
+unlicensed = "deny"
+# List of explicitly allowed licenses
+# See https://spdx.org/licenses/ for list of possible licenses
+# [possible values: any SPDX 3.11 short identifier (+ optional exception)].
+allow = [
+ "MIT",
+ "Apache-2.0",
+ "BSD-3-Clause",
+ "Unicode-DFS-2016",
+]
+# List of explicitly disallowed licenses
+# See https://spdx.org/licenses/ for list of possible licenses
+# [possible values: any SPDX 3.11 short identifier (+ optional exception)].
+deny = [
+ #"Nokia",
+]
+# Lint level for licenses considered copyleft
+copyleft = "warn"
+# Blanket approval or denial for OSI-approved or FSF Free/Libre licenses
+# * both - The license will be approved if it is both OSI-approved *AND* FSF
+# * either - The license will be approved if it is either OSI-approved *OR* FSF
+# * osi-only - The license will be approved if is OSI-approved *AND NOT* FSF
+# * fsf-only - The license will be approved if is FSF *AND NOT* OSI-approved
+# * neither - This predicate is ignored and the default lint level is used
+allow-osi-fsf-free = "neither"
+# Lint level used when no other predicates are matched
+# 1. License isn't in the allow or deny lists
+# 2. License isn't copyleft
+# 3. License isn't OSI/FSF, or allow-osi-fsf-free = "neither"
+default = "deny"
+# The confidence threshold for detecting a license from license text.
+# The higher the value, the more closely the license text must be to the
+# canonical license text of a valid SPDX license file.
+# [possible values: any between 0.0 and 1.0].
+confidence-threshold = 0.8
+# Allow 1 or more licenses on a per-crate basis, so that particular licenses
+# aren't accepted for every possible crate as with the normal allow list
+exceptions = [
+ # Each entry is the crate and version constraint, and its specific allow
+ # list
+ #{ allow = ["Zlib"], name = "adler32", version = "*" },
+]
+
+# Some crates don't have (easily) machine readable licensing information,
+# adding a clarification entry for it allows you to manually specify the
+# licensing information
+#[[licenses.clarify]]
+# The name of the crate the clarification applies to
+#name = "ring"
+# The optional version constraint for the crate
+#version = "*"
+# The SPDX expression for the license requirements of the crate
+#expression = "MIT AND ISC AND OpenSSL"
+# One or more files in the crate's source used as the "source of truth" for
+# the license expression. If the contents match, the clarification will be used
+# when running the license check, otherwise the clarification will be ignored
+# and the crate will be checked normally, which may produce warnings or errors
+# depending on the rest of your configuration
+#license-files = [
+ # Each entry is a crate relative path, and the (opaque) hash of its contents
+ #{ path = "LICENSE", hash = 0xbd0eed23 }
+#]
+
+[[licenses.clarify]]
+name = "ring"
+version = "*"
+expression = "MIT AND ISC AND OpenSSL"
+license-files = [
+ # Each entry is a crate relative path, and the (opaque) hash of its contents
+ { path = "LICENSE", hash = 0xbd0eed23 }
+]
+
+[licenses.private]
+# If true, ignores workspace crates that aren't published, or are only
+# published to private registries.
+# To see how to mark a crate as unpublished (to the official registry),
+# visit https://doc.rust-lang.org/cargo/reference/manifest.html#the-publish-field.
+ignore = true
+# One or more private registries that you might publish crates to, if a crate
+# is only published to private registries, and ignore is true, the crate will
+# not have its license(s) checked
+registries = [
+ #"https://sekretz.com/registry
+]
+
+# This section is considered when running `cargo deny check bans`.
+# More documentation about the 'bans' section can be found here:
+# https://embarkstudios.github.io/cargo-deny/checks/bans/cfg.html
+[bans]
+# Lint level for when multiple versions of the same crate are detected
+multiple-versions = "allow"
+# Lint level for when a crate version requirement is `*`
+wildcards = "allow"
+# The graph highlighting used when creating dotgraphs for crates
+# with multiple versions
+# * lowest-version - The path to the lowest versioned duplicate is highlighted
+# * simplest-path - The path to the version with the fewest edges is highlighted
+# * all - Both lowest-version and simplest-path are used
+highlight = "all"
+# List of crates that are allowed. Use with care!
+allow = [
+ #{ name = "ansi_term", version = "=0.11.0" },
+]
+# List of crates to deny
+deny = [
+ # Each entry the name of a crate and a version range. If version is
+ # not specified, all versions will be matched.
+ #{ name = "ansi_term", version = "=0.11.0" },
+ #
+ # Wrapper crates can optionally be specified to allow the crate when it
+ # is a direct dependency of the otherwise banned crate
+ #{ name = "ansi_term", version = "=0.11.0", wrappers = [] },
+]
+# Certain crates/versions that will be skipped when doing duplicate detection.
+skip = [
+ #{ name = "ansi_term", version = "=0.11.0" },
+]
+# Similarly to `skip` allows you to skip certain crates during duplicate
+# detection. Unlike skip, it also includes the entire tree of transitive
+# dependencies starting at the specified crate, up to a certain depth, which is
+# by default infinite
+skip-tree = [
+ #{ name = "ansi_term", version = "=0.11.0", depth = 20 },
+]
+
+# This section is considered when running `cargo deny check sources`.
+# More documentation about the 'sources' section can be found here:
+# https://embarkstudios.github.io/cargo-deny/checks/sources/cfg.html
+[sources]
+# Lint level for what to happen when a crate from a crate registry that is not
+# in the allow list is encountered
+unknown-registry = "warn"
+# Lint level for what to happen when a crate from a git repository that is not
+# in the allow list is encountered
+unknown-git = "warn"
+# List of URLs for allowed crate registries. Defaults to the crates.io index
+# if not specified. If it is specified but empty, no registries are allowed.
+allow-registry = ["https://github.com/rust-lang/crates.io-index"]
+# List of URLs for allowed Git repositories
+allow-git = [] \ No newline at end of file
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..aab2a30 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 @@ typedef uint8_t np_ffi_BooleanActionType;
*/
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.
*/
@@ -79,6 +116,24 @@ typedef enum {
} np_ffi_DeallocateResult;
/**
+ * Discriminant for `DecryptMetadataResult`.
+ */
+enum np_ffi_DecryptMetadataResultKind {
+ /**
+ * The attempt to decrypt the metadata of the associated credential succeeded
+ * The associated payload may be obtained via
+ * `DecryptMetadataResult#into_success`.
+ */
+ NP_FFI_DECRYPT_METADATA_RESULT_KIND_SUCCESS,
+ /**
+ * The attempt to decrypt the metadata failed, either the payload had no matching identity
+ * ie it was a public advertisement OR the decrypt attempt itself was unsuccessful
+ */
+ NP_FFI_DECRYPT_METADATA_RESULT_KIND_ERROR,
+};
+typedef uint8_t np_ffi_DecryptMetadataResultKind;
+
+/**
* Discriminant for `DeserializeAdvertisementResult`.
*/
enum np_ffi_DeserializeAdvertisementResultKind {
@@ -122,16 +177,8 @@ enum np_ffi_DeserializedV0AdvertisementKind {
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 {
/**
@@ -162,6 +209,34 @@ enum np_ffi_DeserializedV1IdentityKind {
typedef uint8_t np_ffi_DeserializedV1IdentityKind;
/**
+ * The DE type for an encrypted identity
+ */
+enum np_ffi_EncryptedIdentityType {
+ /**
+ * Identity for broadcasts to nearby devices with the same
+ * logged-in-account (for some account).
+ */
+ NP_FFI_ENCRYPTED_IDENTITY_TYPE_PRIVATE = 1,
+ /**
+ * Identity for broadcasts to nearby devices which this
+ * device has declared to trust.
+ */
+ NP_FFI_ENCRYPTED_IDENTITY_TYPE_TRUSTED = 2,
+ /**
+ * Identity for broadcasts to devices which have been provisioned
+ * offline with this device.
+ */
+ NP_FFI_ENCRYPTED_IDENTITY_TYPE_PROVISIONED = 4,
+};
+typedef uint8_t np_ffi_EncryptedIdentityType;
+
+enum np_ffi_GetMetadataBufferPartsResultKind {
+ NP_FFI_GET_METADATA_BUFFER_PARTS_RESULT_KIND_SUCCESS = 0,
+ NP_FFI_GET_METADATA_BUFFER_PARTS_RESULT_KIND_ERROR = 1,
+};
+typedef uint8_t np_ffi_GetMetadataBufferPartsResultKind;
+
+/**
* Discriminant of `GetV0DEResult`.
*/
enum np_ffi_GetV0DEResultKind {
@@ -182,6 +257,47 @@ enum np_ffi_GetV0DEResultKind {
typedef uint8_t np_ffi_GetV0DEResultKind;
/**
+ * Discriminant for `GetV0IdentityDetailsResult`
+ */
+enum np_ffi_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.
+ */
+ NP_FFI_GET_V0_IDENTITY_DETAILS_RESULT_KIND_ERROR = 0,
+ /**
+ * The attempt to get the identity details succeeded.
+ * The wrapped identity details may be obtained via
+ * `GetV0IdentityDetailsResult#into_success`.
+ */
+ NP_FFI_GET_V0_IDENTITY_DETAILS_RESULT_KIND_SUCCESS = 1,
+};
+typedef uint8_t np_ffi_GetV0IdentityDetailsResultKind;
+
+/**
+ * Discriminant for `GetV1DE16ByteSaltResult`.
+ */
+enum np_ffi_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.)
+ */
+ NP_FFI_GET_V1DE16_BYTE_SALT_RESULT_KIND_ERROR = 0,
+ /**
+ * A 16-byte salt for the given DE offset was successfully
+ * derived.
+ */
+ NP_FFI_GET_V1DE16_BYTE_SALT_RESULT_KIND_SUCCESS = 1,
+};
+typedef uint8_t np_ffi_GetV1DE16ByteSaltResultKind;
+
+/**
* Discriminant for the `GetV1DEResult` enum.
*/
enum np_ffi_GetV1DEResultKind {
@@ -200,6 +316,27 @@ enum np_ffi_GetV1DEResultKind {
typedef uint8_t np_ffi_GetV1DEResultKind;
/**
+ * Discriminant for `GetV1IdentityDetailsResult`
+ */
+enum np_ffi_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.
+ */
+ NP_FFI_GET_V1_IDENTITY_DETAILS_RESULT_KIND_ERROR = 0,
+ /**
+ * The attempt to get the identity details succeeded.
+ * The wrapped identity details may be obtained via
+ * `GetV1IdentityDetailsResult#into_success`.
+ */
+ NP_FFI_GET_V1_IDENTITY_DETAILS_RESULT_KIND_SUCCESS = 1,
+};
+typedef uint8_t np_ffi_GetV1IdentityDetailsResultKind;
+
+/**
* Discriminant for `GetV1SectionResult`
*/
enum np_ffi_GetV1SectionResultKind {
@@ -267,6 +404,23 @@ enum np_ffi_V0DataElementKind {
typedef uint8_t np_ffi_V0DataElementKind;
/**
+ * Information about the verification scheme used
+ * for verifying the integrity of the contents
+ * of a decrypted section.
+ */
+enum np_ffi_V1VerificationMode {
+ /**
+ * Message integrity code verification.
+ */
+ NP_FFI_V1_VERIFICATION_MODE_MIC = 0,
+ /**
+ * Signature verification.
+ */
+ NP_FFI_V1_VERIFICATION_MODE_SIGNATURE = 1,
+};
+typedef uint8_t np_ffi_V1VerificationMode;
+
+/**
*A `#[repr(C)]` handle to a value of type `super::CredentialBookInternals`.
*/
typedef struct {
@@ -277,8 +431,9 @@ typedef struct {
* 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 +446,125 @@ typedef union {
} 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::DecryptedMetadataInternals`.
+ */
+typedef struct {
+ uint64_t handle_id;
+} np_ffi_DecryptedMetadata;
+
+/**
+ * The result of decrypting metadata from either a V0Payload or DeserializedV1Section
+ */
+typedef enum {
+ NP_FFI_DECRYPT_METADATA_RESULT_SUCCESS,
+ NP_FFI_DECRYPT_METADATA_RESULT_ERROR,
+} np_ffi_DecryptMetadataResult_Tag;
+
+typedef struct {
+ np_ffi_DecryptMetadataResult_Tag tag;
+ union {
+ struct {
+ np_ffi_DecryptedMetadata success;
+ };
+ };
+} np_ffi_DecryptMetadataResult;
+
+/**
+ * The pointer and length of the decrypted metadata byte buffer
+ */
+typedef struct {
+ const uint8_t *ptr;
+ uintptr_t len;
+} np_ffi_MetadataBufferParts;
+
+typedef enum {
+ NP_FFI_GET_METADATA_BUFFER_PARTS_RESULT_SUCCESS,
+ NP_FFI_GET_METADATA_BUFFER_PARTS_RESULT_ERROR,
+} np_ffi_GetMetadataBufferPartsResult_Tag;
+
+typedef struct {
+ np_ffi_GetMetadataBufferPartsResult_Tag tag;
+ union {
+ struct {
+ np_ffi_MetadataBufferParts success;
+ };
+ };
+} np_ffi_GetMetadataBufferPartsResult;
+
+/**
*A `#[repr(C)]` handle to a value of type `super::V0PayloadInternals`.
*/
typedef struct {
@@ -303,7 +577,7 @@ typedef struct {
typedef struct {
uint8_t num_des;
np_ffi_V0Payload payload;
- np_ffi_DeserializedV0Identity identity;
+ np_ffi_DeserializedV0IdentityKind identity_kind;
} np_ffi_LegibleDeserializedV0Advertisement;
/**
@@ -473,6 +747,49 @@ typedef struct {
} np_ffi_GetV0DEResult;
/**
+ * Information about the identity which matched a
+ * decrypted V0 advertisement.
+ */
+typedef struct {
+ /**
+ * The identity type (private/provisioned/trusted)
+ */
+ np_ffi_EncryptedIdentityType identity_type;
+ /**
+ * The ID of the credential which
+ * matched the deserialized adv
+ */
+ uint32_t cred_id;
+ /**
+ * The 14-byte legacy metadata key
+ */
+ uint8_t metadata_key[14];
+ /**
+ * The 2-byte advertisement salt
+ */
+ uint8_t salt[2];
+} np_ffi_DeserializedV0IdentityDetails;
+
+/**
+ * The result of attempting to get the identity details
+ * for a V0 advertisement via
+ * `DeserializedV0Advertisement#get_identity_details`.
+ */
+typedef enum {
+ NP_FFI_GET_V0_IDENTITY_DETAILS_RESULT_ERROR,
+ NP_FFI_GET_V0_IDENTITY_DETAILS_RESULT_SUCCESS,
+} np_ffi_GetV0IdentityDetailsResult_Tag;
+
+typedef struct {
+ np_ffi_GetV0IdentityDetailsResult_Tag tag;
+ union {
+ struct {
+ np_ffi_DeserializedV0IdentityDetails success;
+ };
+ };
+} np_ffi_GetV0IdentityDetailsResult;
+
+/**
* Handle to a deserialized V1 section
*/
typedef struct {
@@ -527,6 +844,10 @@ typedef struct {
*/
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;
@@ -576,6 +897,75 @@ typedef struct {
} np_ffi_GetV1DEResult;
/**
+ * Information about the identity which matched
+ * a decrypted V1 section.
+ */
+typedef struct {
+ /**
+ * The identity type (private/provisioned/trusted)
+ */
+ np_ffi_EncryptedIdentityType identity_type;
+ /**
+ * The verification mode (MIC/Signature) which
+ * was used to verify the decrypted adv contents.
+ */
+ np_ffi_V1VerificationMode verification_mode;
+ /**
+ * The ID of the credential which
+ * matched the deserialized section.
+ */
+ uint32_t cred_id;
+ /**
+ * The 16-byte metadata key.
+ */
+ uint8_t metadata_key[16];
+} np_ffi_DeserializedV1IdentityDetails;
+
+/**
+ * The result of attempting to get the identity details
+ * for a V1 advertisement section via
+ * `DeserializedV1Advertisement#get_identity_details`.
+ */
+typedef enum {
+ NP_FFI_GET_V1_IDENTITY_DETAILS_RESULT_ERROR,
+ NP_FFI_GET_V1_IDENTITY_DETAILS_RESULT_SUCCESS,
+} np_ffi_GetV1IdentityDetailsResult_Tag;
+
+typedef struct {
+ np_ffi_GetV1IdentityDetailsResult_Tag tag;
+ union {
+ struct {
+ np_ffi_DeserializedV1IdentityDetails success;
+ };
+ };
+} np_ffi_GetV1IdentityDetailsResult;
+
+/**
+ * A FFI safe wrapper of a fixed size array
+ */
+typedef struct {
+ uint8_t _0[16];
+} np_ffi_FixedSizeArray_16;
+
+/**
+ * The result of attempting to get a derived 16-byte salt
+ * for a given DE within a section.
+ */
+typedef enum {
+ NP_FFI_GET_V1DE16_BYTE_SALT_RESULT_ERROR,
+ NP_FFI_GET_V1DE16_BYTE_SALT_RESULT_SUCCESS,
+} np_ffi_GetV1DE16ByteSaltResult_Tag;
+
+typedef struct {
+ np_ffi_GetV1DE16ByteSaltResult_Tag tag;
+ union {
+ struct {
+ np_ffi_FixedSizeArray_16 success;
+ };
+ };
+} np_ffi_GetV1DE16ByteSaltResult;
+
+/**
* Overrides the global panic handler to be used when NP C FFI calls panic.
* This method will only have an effect on the global panic-handler
* the first time it's called, and this method will return `true`
@@ -616,6 +1006,22 @@ bool np_ffi_global_config_panic_handler(void (*handler)(np_ffi_PanicReason));
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 +1072,10 @@ void np_ffi_global_config_set_max_num_deserialized_v0_advertisements(uint32_t ma
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 +1089,90 @@ np_ffi_CreateCredentialBookResultKind np_ffi_CreateCredentialBookResult_kind(np_
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);
+
+/**
+ * Frees the underlying resources of the decrypted metadata buffer
+ */
+np_ffi_DeallocateResult np_ffi_deallocate_DecryptedMetadata(np_ffi_DecryptedMetadata metadata);
+
+/**
+ * Gets the tag of a `DecryptMetadataResult` tagged-union. On success the wrapped identity
+ * details may be obtained via `DecryptMetadataResult#into_success`.
+ */
+np_ffi_DecryptMetadataResultKind np_ffi_DecryptMetadataResult_kind(np_ffi_DecryptMetadataResult result);
+
+/**
+ * Casts a `DecryptMetadataResult` to the `Success` variant, panicking in the
+ * case where the passed value is of a different enum variant.
+ */
+np_ffi_DecryptedMetadata np_ffi_DecryptMetadataResult_into_SUCCESS(np_ffi_DecryptMetadataResult result);
+
+/**
+ * Gets the pointer and length of the heap allocated byte buffer of decrypted metadata
+ */
+np_ffi_GetMetadataBufferPartsResult np_ffi_DecryptedMetadata_get_metadata_buffer_parts(np_ffi_DecryptedMetadata metadata);
+
+/**
+ * Gets the tag of a `GetMetadataBufferPartsResult` tagged-union. On success the wrapped identity
+ * details may be obtained via `GetMetadataBufferPartsResult#into_success`.
+ */
+np_ffi_GetMetadataBufferPartsResultKind np_ffi_GetMetadataBufferPartsResult_kind(np_ffi_GetMetadataBufferPartsResult result);
+
+/**
+ * Casts a `GetMetadataBufferPartsResult` to the `Success` variant, panicking in the
+ * case where the passed value is of a different enum variant. This returns the pointer and length
+ * of the byte buffer containing the decrypted metadata. There can be a data-race between attempts
+ * to access the contents of the buffer and attempts to free the handle from different threads.
+ */
+np_ffi_MetadataBufferParts np_ffi_GetMetadataBufferPartsResult_into_SUCCESS(np_ffi_GetMetadataBufferPartsResult result);
+
+/**
* 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 +1237,9 @@ uint8_t np_ffi_LegibleDeserializedV0Advertisement_get_num_des(np_ffi_LegibleDese
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,14 +1247,32 @@ np_ffi_DeserializedV0Identity np_ffi_LegibleDeserializedV0Advertisement_into_ide
np_ffi_DeallocateResult np_ffi_deallocate_legible_v0_advertisement(np_ffi_LegibleDeserializedV0Advertisement adv);
/**
- * Gets the tag of the `DeserializedV0Identity` tagged-union.
+ * Attempts to get the data-element with the given index in the passed v0 adv payload
*/
-np_ffi_DeserializedV0IdentityKind np_ffi_DeserializedV0Identity_kind(np_ffi_DeserializedV0Identity identity);
+np_ffi_GetV0DEResult np_ffi_V0Payload_get_de(np_ffi_V0Payload payload, uint8_t index);
/**
- * Attempts to get the data-element with the given index in the passed v0 adv payload
+ * Attempts to decrypt the metadata for the matched credential for this V0 payload (if any)
*/
-np_ffi_GetV0DEResult np_ffi_V0Payload_get_de(np_ffi_V0Payload payload, uint8_t index);
+np_ffi_DecryptMetadataResult np_ffi_V0Payload_decrypt_metadata(np_ffi_V0Payload payload);
+
+/**
+ * Gets the identity details for this V0 payload, or returns an error if this payload does not have
+ * any associated identity (public advertisement)
+ */
+np_ffi_GetV0IdentityDetailsResult np_ffi_V0Payload_get_identity_details(np_ffi_V0Payload payload);
+
+/**
+ * Gets the tag of a `GetV0IdentityDetailsResult` tagged-union. On success the wrapped identity
+ * details may be obtained via `GetV0IdentityDetailsResult#into_success`.
+ */
+np_ffi_GetV0IdentityDetailsResultKind np_ffi_GetV0IdentityDetailsResult_kind(np_ffi_GetV0IdentityDetailsResult result);
+
+/**
+ * Casts a `GetV0IdentityDetailsResult` to the `Success` variant, panicking in the
+ * case where the passed value is of a different enum variant.
+ */
+np_ffi_DeserializedV0IdentityDetails np_ffi_GetV0IdentityDetailsResult_into_SUCCESS(np_ffi_GetV0IdentityDetailsResult result);
/**
* Deallocates any internal data of a `V0Payload`
@@ -867,12 +1371,55 @@ np_ffi_GetV1DEResult np_ffi_DeserializedV1Section_get_de(np_ffi_DeserializedV1Se
uint8_t de_index);
/**
+ * Gets the identity details used to decrypt this V1 section, or returns an error if this payload
+ * does not have any associated identity (public advertisement)
+ */
+np_ffi_GetV1IdentityDetailsResult np_ffi_DeserializedV1Section_get_identity_details(np_ffi_DeserializedV1Section section);
+
+/**
+ * Gets the tag of a `GetV1IdentityDetailsResult` tagged-union. On success the wrapped identity
+ * details may be obtained via `GetV0IdentityDetailsResult#into_success`.
+ */
+np_ffi_GetV1IdentityDetailsResultKind np_ffi_GetV1IdentityDetailsResult_kind(np_ffi_GetV1IdentityDetailsResult result);
+
+/**
+ * Casts a `GetV1IdentityDetailsResult` to the `Success` variant, panicking in the
+ * case where the passed value is of a different enum variant.
+ */
+np_ffi_DeserializedV1IdentityDetails np_ffi_GetV1IdentityDetailsResult_into_SUCCESS(np_ffi_GetV1IdentityDetailsResult result);
+
+/**
+ * Attempts to decrypt the metadata for the matched credential for this V0 payload (if any)
+ */
+np_ffi_DecryptMetadataResult np_ffi_DeserializedV1Section_decrypt_metadata(np_ffi_DeserializedV1Section section);
+
+/**
+ * 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.
+ */
+np_ffi_GetV1DE16ByteSaltResult np_ffi_DeserializedV1Section_derive_16_byte_salt_for_offset(np_ffi_DeserializedV1Section section,
+ uint8_t offset);
+
+/**
+ * Gets the tag of a `GetV1DE16ByteSaltResult` tagged-union. On success the wrapped identity
+ * details may be obtained via `GetV1DE16ByteSaltResult#into_success`.
+ */
+np_ffi_GetV1DE16ByteSaltResultKind np_ffi_GetV1DE16ByteSaltResult_kind(np_ffi_GetV1DE16ByteSaltResult result);
+
+/**
+ * Casts a `GetV1DE16ByteSaltResult` to the `Success` variant, panicking in the
+ * case where the passed value is of a different enum variant.
+ */
+np_ffi_FixedSizeArray_16 np_ffi_GetV1DE16ByteSaltResult_into_SUCCESS(np_ffi_GetV1DE16ByteSaltResult result);
+
+/**
* Gets the tag of the `GetV1DEResult` tagged-union.
*/
np_ffi_GetV1DEResultKind np_ffi_GetV1DEResult_kind(np_ffi_GetV1DEResult result);
/**
- * Casts a `GetV1DEResult` to the `Success` vartiant, panicking in the
+ * Casts a `GetV1DEResult` to the `Success` variant, panicking in the
* case where the passed value is of a different enum variant.
*/
np_ffi_V1DataElement np_ffi_GetV1DEResult_into_SUCCESS(np_ffi_GetV1DEResult result);
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..ffd63b0 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 @@ bool np_ffi_global_config_panic_handler(void (*handler)(PanicReason));
/// 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 @@ void np_ffi_global_config_set_max_num_deserialized_v0_advertisements(uint32_t ma
/// 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,64 @@ CreateCredentialBookResultKind np_ffi_CreateCredentialBookResult_kind(CreateCred
/// 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);
+
+/// Frees the underlying resources of the decrypted metadata buffer
+DeallocateResult np_ffi_deallocate_DecryptedMetadata(DecryptedMetadata metadata);
+
+/// Gets the tag of a `DecryptMetadataResult` tagged-union. On success the wrapped identity
+/// details may be obtained via `DecryptMetadataResult#into_success`.
+DecryptMetadataResultKind np_ffi_DecryptMetadataResult_kind(DecryptMetadataResult result);
+
+/// Casts a `DecryptMetadataResult` to the `Success` variant, panicking in the
+/// case where the passed value is of a different enum variant.
+DecryptedMetadata np_ffi_DecryptMetadataResult_into_SUCCESS(DecryptMetadataResult result);
+
+/// Gets the pointer and length of the heap allocated byte buffer of decrypted metadata
+GetMetadataBufferPartsResult np_ffi_DecryptedMetadata_get_metadata_buffer_parts(DecryptedMetadata metadata);
+
+/// Gets the tag of a `GetMetadataBufferPartsResult` tagged-union. On success the wrapped identity
+/// details may be obtained via `GetMetadataBufferPartsResult#into_success`.
+GetMetadataBufferPartsResultKind np_ffi_GetMetadataBufferPartsResult_kind(GetMetadataBufferPartsResult result);
+
+/// Casts a `GetMetadataBufferPartsResult` to the `Success` variant, panicking in the
+/// case where the passed value is of a different enum variant. This returns the pointer and length
+/// of the byte buffer containing the decrypted metadata. There can be a data-race between attempts
+/// to access the contents of the buffer and attempts to free the handle from different threads.
+MetadataBufferParts np_ffi_GetMetadataBufferPartsResult_into_SUCCESS(GetMetadataBufferPartsResult result);
+
/// 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,18 +244,30 @@ uint8_t np_ffi_LegibleDeserializedV0Advertisement_get_num_des(LegibleDeserialize
/// 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);
+/// Attempts to decrypt the metadata for the matched credential for this V0 payload (if any)
+DecryptMetadataResult np_ffi_V0Payload_decrypt_metadata(V0Payload payload);
+
+/// Gets the identity details for this V0 payload, or returns an error if this payload does not have
+/// any associated identity (public advertisement)
+GetV0IdentityDetailsResult np_ffi_V0Payload_get_identity_details(V0Payload payload);
+
+/// Gets the tag of a `GetV0IdentityDetailsResult` tagged-union. On success the wrapped identity
+/// details may be obtained via `GetV0IdentityDetailsResult#into_success`.
+GetV0IdentityDetailsResultKind np_ffi_GetV0IdentityDetailsResult_kind(GetV0IdentityDetailsResult result);
+
+/// Casts a `GetV0IdentityDetailsResult` to the `Success` variant, panicking in the
+/// case where the passed value is of a different enum variant.
+DeserializedV0IdentityDetails np_ffi_GetV0IdentityDetailsResult_into_SUCCESS(GetV0IdentityDetailsResult result);
+
/// Deallocates any internal data of a `V0Payload`
DeallocateResult np_ffi_deallocate_v0_payload(V0Payload payload);
@@ -247,10 +329,39 @@ DeserializedV1IdentityKind np_ffi_DeserializedV1Section_get_identity_kind(Deseri
/// Gets the data-element with the given index in the passed section.
GetV1DEResult np_ffi_DeserializedV1Section_get_de(DeserializedV1Section section, uint8_t de_index);
+/// Gets the identity details used to decrypt this V1 section, or returns an error if this payload
+/// does not have any associated identity (public advertisement)
+GetV1IdentityDetailsResult np_ffi_DeserializedV1Section_get_identity_details(DeserializedV1Section section);
+
+/// Gets the tag of a `GetV1IdentityDetailsResult` tagged-union. On success the wrapped identity
+/// details may be obtained via `GetV0IdentityDetailsResult#into_success`.
+GetV1IdentityDetailsResultKind np_ffi_GetV1IdentityDetailsResult_kind(GetV1IdentityDetailsResult result);
+
+/// Casts a `GetV1IdentityDetailsResult` to the `Success` variant, panicking in the
+/// case where the passed value is of a different enum variant.
+DeserializedV1IdentityDetails np_ffi_GetV1IdentityDetailsResult_into_SUCCESS(GetV1IdentityDetailsResult result);
+
+/// Attempts to decrypt the metadata for the matched credential for this V0 payload (if any)
+DecryptMetadataResult np_ffi_DeserializedV1Section_decrypt_metadata(DeserializedV1Section section);
+
+/// 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.
+GetV1DE16ByteSaltResult np_ffi_DeserializedV1Section_derive_16_byte_salt_for_offset(DeserializedV1Section section,
+ uint8_t offset);
+
+/// Gets the tag of a `GetV1DE16ByteSaltResult` tagged-union. On success the wrapped identity
+/// details may be obtained via `GetV1DE16ByteSaltResult#into_success`.
+GetV1DE16ByteSaltResultKind np_ffi_GetV1DE16ByteSaltResult_kind(GetV1DE16ByteSaltResult result);
+
+/// Casts a `GetV1DE16ByteSaltResult` to the `Success` variant, panicking in the
+/// case where the passed value is of a different enum variant.
+FixedSizeArray<16> np_ffi_GetV1DE16ByteSaltResult_into_SUCCESS(GetV1DE16ByteSaltResult result);
+
/// Gets the tag of the `GetV1DEResult` tagged-union.
GetV1DEResultKind np_ffi_GetV1DEResult_kind(GetV1DEResult result);
-/// Casts a `GetV1DEResult` to the `Success` vartiant, panicking in the
+/// Casts a `GetV1DEResult` to the `Success` variant, panicking in the
/// case where the passed value is of a different enum variant.
V1DataElement np_ffi_GetV1DEResult_into_SUCCESS(GetV1DEResult result);
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..b5908ef 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 @@ enum class BooleanActionType : uint8_t {
/// 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,
};
@@ -66,6 +87,17 @@ enum class DeallocateResult {
Success = 1,
};
+/// Discriminant for `DecryptMetadataResult`.
+enum class DecryptMetadataResultKind : uint8_t {
+ /// The attempt to decrypt the metadata of the associated credential succeeded
+ /// The associated payload may be obtained via
+ /// `DecryptMetadataResult#into_success`.
+ Success,
+ /// The attempt to decrypt the metadata failed, either the payload had no matching identity
+ /// ie it was a public advertisement OR the decrypt attempt itself was unsuccessful
+ Error,
+};
+
/// Discriminant for `DeserializeAdvertisementResult`.
enum class DeserializeAdvertisementResultKind : uint8_t {
/// Deserializing the advertisement failed, for some reason or another.
@@ -93,14 +125,8 @@ enum class DeserializedV0AdvertisementKind : uint8_t {
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,
@@ -117,6 +143,24 @@ enum class DeserializedV1IdentityKind : uint8_t {
Decrypted = 1,
};
+/// The DE type for an encrypted identity
+enum class EncryptedIdentityType : uint8_t {
+ /// 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,
+};
+
+enum class GetMetadataBufferPartsResultKind : uint8_t {
+ Success = 0,
+ Error = 1,
+};
+
/// Discriminant of `GetV0DEResult`.
enum class GetV0DEResultKind : uint8_t {
/// The attempt to get the DE succeeded.
@@ -130,6 +174,33 @@ enum class GetV0DEResultKind : uint8_t {
Error = 1,
};
+/// Discriminant for `GetV0IdentityDetailsResult`
+enum class GetV0IdentityDetailsResultKind : uint8_t {
+ /// 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,
+};
+
+/// Discriminant for `GetV1DE16ByteSaltResult`.
+enum class GetV1DE16ByteSaltResultKind : uint8_t {
+ /// 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,
+};
+
/// Discriminant for the `GetV1DEResult` enum.
enum class GetV1DEResultKind : uint8_t {
/// Attempting to get the DE at the given position failed,
@@ -141,6 +212,20 @@ enum class GetV1DEResultKind : uint8_t {
Success = 1,
};
+/// Discriminant for `GetV1IdentityDetailsResult`
+enum class GetV1IdentityDetailsResultKind : uint8_t {
+ /// 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,
+};
+
/// Discriminant for `GetV1SectionResult`
enum class GetV1SectionResultKind : uint8_t {
/// The attempt to get the section failed,
@@ -185,6 +270,16 @@ enum class V0DataElementKind : uint8_t {
Actions = 1,
};
+/// Information about the verification scheme used
+/// for verifying the integrity of the contents
+/// of a decrypted section.
+enum class V1VerificationMode : uint8_t {
+ /// Message integrity code verification.
+ Mic = 0,
+ /// Signature verification.
+ Signature = 1,
+};
+
///A `#[repr(C)]` handle to a value of type `super::CredentialBookInternals`.
struct CredentialBook {
uint64_t handle_id;
@@ -193,8 +288,9 @@ struct CredentialBook {
/// 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 +304,111 @@ union CreateCredentialBookResult {
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::DecryptedMetadataInternals`.
+struct DecryptedMetadata {
+ uint64_t handle_id;
+};
+
+/// The result of decrypting metadata from either a V0Payload or DeserializedV1Section
+struct DecryptMetadataResult {
+ enum class Tag {
+ Success,
+ Error,
+ };
+
+ struct Success_Body {
+ DecryptedMetadata _0;
+ };
+
+ Tag tag;
+ union {
+ Success_Body success;
+ };
+};
+
+/// The pointer and length of the decrypted metadata byte buffer
+struct MetadataBufferParts {
+ const uint8_t *ptr;
+ uintptr_t len;
+};
+
+struct GetMetadataBufferPartsResult {
+ enum class Tag {
+ Success,
+ Error,
+ };
+
+ struct Success_Body {
+ MetadataBufferParts _0;
+ };
+
+ Tag tag;
+ union {
+ Success_Body success;
+ };
+};
+
///A `#[repr(C)]` handle to a value of type `super::V0PayloadInternals`.
struct V0Payload {
uint64_t handle_id;
@@ -217,7 +418,7 @@ struct V0Payload {
struct LegibleDeserializedV0Advertisement {
uint8_t num_des;
V0Payload payload;
- DeserializedV0Identity identity;
+ DeserializedV0IdentityKind identity_kind;
};
/// Represents a deserialized V0 advertisement
@@ -372,6 +573,39 @@ struct GetV0DEResult {
};
};
+/// Information about the identity which matched a
+/// decrypted V0 advertisement.
+struct DeserializedV0IdentityDetails {
+ /// The identity type (private/provisioned/trusted)
+ EncryptedIdentityType identity_type;
+ /// The ID of the credential which
+ /// matched the deserialized adv
+ uint32_t cred_id;
+ /// The 14-byte legacy metadata key
+ uint8_t metadata_key[14];
+ /// The 2-byte advertisement salt
+ uint8_t salt[2];
+};
+
+/// The result of attempting to get the identity details
+/// for a V0 advertisement via
+/// `DeserializedV0Advertisement#get_identity_details`.
+struct GetV0IdentityDetailsResult {
+ enum class Tag {
+ Error,
+ Success,
+ };
+
+ struct Success_Body {
+ DeserializedV0IdentityDetails _0;
+ };
+
+ Tag tag;
+ union {
+ Success_Body success;
+ };
+};
+
/// Handle to a deserialized V1 section
struct DeserializedV1Section {
LegibleV1Sections legible_sections_handle;
@@ -409,6 +643,8 @@ struct V1DEType {
/// 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
@@ -452,5 +688,63 @@ struct GetV1DEResult {
};
};
+/// Information about the identity which matched
+/// a decrypted V1 section.
+struct DeserializedV1IdentityDetails {
+ /// The identity type (private/provisioned/trusted)
+ EncryptedIdentityType identity_type;
+ /// The verification mode (MIC/Signature) which
+ /// was used to verify the decrypted adv contents.
+ V1VerificationMode verification_mode;
+ /// The ID of the credential which
+ /// matched the deserialized section.
+ uint32_t cred_id;
+ /// The 16-byte metadata key.
+ uint8_t metadata_key[16];
+};
+
+/// The result of attempting to get the identity details
+/// for a V1 advertisement section via
+/// `DeserializedV1Advertisement#get_identity_details`.
+struct GetV1IdentityDetailsResult {
+ enum class Tag {
+ Error,
+ Success,
+ };
+
+ struct Success_Body {
+ DeserializedV1IdentityDetails _0;
+ };
+
+ Tag tag;
+ union {
+ Success_Body success;
+ };
+};
+
+/// A FFI safe wrapper of a fixed size array
+template<uintptr_t N>
+struct FixedSizeArray {
+ uint8_t _0[N];
+};
+
+/// The result of attempting to get a derived 16-byte salt
+/// for a given DE within a section.
+struct GetV1DE16ByteSaltResult {
+ enum class Tag {
+ Error,
+ Success,
+ };
+
+ struct Success_Body {
+ FixedSizeArray<16> _0;
+ };
+
+ Tag tag;
+ union {
+ Success_Body success;
+ };
+};
+
} // namespace internal
} // namespace np_ffi
diff --git a/nearby/presence/np_c_ffi/src/credentials.rs b/nearby/presence/np_c_ffi/src/credentials.rs
index 6b60545..3cbd76f 100644
--- a/nearby/presence/np_c_ffi/src/credentials.rs
+++ b/nearby/presence/np_c_ffi/src/credentials.rs
@@ -14,15 +14,25 @@
//! 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::deserialize::decrypted_metadata::DecryptedMetadata;
+use np_ffi_core::deserialize::{
+ DecryptMetadataResult, DecryptMetadataResultKind, GetMetadataBufferPartsResult,
+ GetMetadataBufferPartsResultKind, MetadataBufferParts,
+};
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 +52,14 @@ pub extern "C" fn np_ffi_CreateCredentialBookResult_into_SUCCESS(
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 +67,152 @@ pub extern "C" fn np_ffi_deallocate_credential_book(
) -> 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)
+}
+
+/// Frees the underlying resources of the decrypted metadata buffer
+#[no_mangle]
+pub extern "C" fn np_ffi_deallocate_DecryptedMetadata(
+ metadata: DecryptedMetadata,
+) -> DeallocateResult {
+ metadata.deallocate_metadata()
+}
+
+/// Gets the tag of a `DecryptMetadataResult` tagged-union. On success the wrapped identity
+/// details may be obtained via `DecryptMetadataResult#into_success`.
+#[no_mangle]
+pub extern "C" fn np_ffi_DecryptMetadataResult_kind(
+ result: DecryptMetadataResult,
+) -> DecryptMetadataResultKind {
+ result.kind()
+}
+
+/// Casts a `DecryptMetadataResult` 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_DecryptMetadataResult_into_SUCCESS(
+ result: DecryptMetadataResult,
+) -> DecryptedMetadata {
+ unwrap(result.into_success(), PanicReason::EnumCastFailed)
+}
+
+/// Gets the pointer and length of the heap allocated byte buffer of decrypted metadata
+#[no_mangle]
+pub extern "C" fn np_ffi_DecryptedMetadata_get_metadata_buffer_parts(
+ metadata: DecryptedMetadata,
+) -> GetMetadataBufferPartsResult {
+ metadata.get_metadata_buffer_parts()
+}
+
+/// Gets the tag of a `GetMetadataBufferPartsResult` tagged-union. On success the wrapped identity
+/// details may be obtained via `GetMetadataBufferPartsResult#into_success`.
+#[no_mangle]
+pub extern "C" fn np_ffi_GetMetadataBufferPartsResult_kind(
+ result: GetMetadataBufferPartsResult,
+) -> GetMetadataBufferPartsResultKind {
+ result.kind()
+}
+
+/// Casts a `GetMetadataBufferPartsResult` to the `Success` variant, panicking in the
+/// case where the passed value is of a different enum variant. This returns the pointer and length
+/// of the byte buffer containing the decrypted metadata. There can be a data-race between attempts
+/// to access the contents of the buffer and attempts to free the handle from different threads.
+#[no_mangle]
+pub extern "C" fn np_ffi_GetMetadataBufferPartsResult_into_SUCCESS(
+ result: GetMetadataBufferPartsResult,
+) -> MetadataBufferParts {
+ unwrap(result.into_success(), PanicReason::EnumCastFailed)
+}
diff --git a/nearby/presence/np_c_ffi/src/deserialize/v0.rs b/nearby/presence/np_c_ffi/src/deserialize/v0.rs
index 877cee5..7a460a2 100644
--- a/nearby/presence/np_c_ffi/src/deserialize/v0.rs
+++ b/nearby/presence/np_c_ffi/src/deserialize/v0.rs
@@ -16,6 +16,7 @@ use crate::{panic, unwrap, PanicReason};
use np_ffi_core::common::DeallocateResult;
use np_ffi_core::deserialize::v0::v0_payload::V0Payload;
use np_ffi_core::deserialize::v0::*;
+use np_ffi_core::deserialize::DecryptMetadataResult;
use np_ffi_core::utils::FfiEnum;
/// Gets the tag of a `DeserializedV0Advertisement` tagged-union.
@@ -49,15 +50,15 @@ pub extern "C" fn np_ffi_LegibleDeserializedV0Advertisement_get_num_des(
pub extern "C" fn np_ffi_LegibleDeserializedV0Advertisement_into_payload(
adv: LegibleDeserializedV0Advertisement,
) -> V0Payload {
- adv.into_payload()
+ 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.into_identity()
+) -> DeserializedV0IdentityKind {
+ adv.identity_kind()
}
/// Deallocates any internal data of a `LegibleDeserializedV0Advertisement`
@@ -68,20 +69,45 @@ pub extern "C" fn np_ffi_deallocate_legible_v0_advertisement(
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 {
payload.get_de(index)
}
+/// Attempts to decrypt the metadata for the matched credential for this V0 payload (if any)
+#[no_mangle]
+pub extern "C" fn np_ffi_V0Payload_decrypt_metadata(payload: V0Payload) -> DecryptMetadataResult {
+ payload.decrypt_metadata()
+}
+
+/// Gets the identity details for this V0 payload, or returns an error if this payload does not have
+/// any associated identity (public advertisement)
+#[no_mangle]
+pub extern "C" fn np_ffi_V0Payload_get_identity_details(
+ payload: V0Payload,
+) -> GetV0IdentityDetailsResult {
+ payload.get_identity_details()
+}
+
+/// Gets the tag of a `GetV0IdentityDetailsResult` tagged-union. On success the wrapped identity
+/// details may be obtained via `GetV0IdentityDetailsResult#into_success`.
+#[no_mangle]
+pub extern "C" fn np_ffi_GetV0IdentityDetailsResult_kind(
+ result: GetV0IdentityDetailsResult,
+) -> GetV0IdentityDetailsResultKind {
+ result.kind()
+}
+
+/// Casts a `GetV0IdentityDetailsResult` 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_GetV0IdentityDetailsResult_into_SUCCESS(
+ result: GetV0IdentityDetailsResult,
+) -> DeserializedV0IdentityDetails {
+ unwrap(result.into_success(), PanicReason::EnumCastFailed)
+}
+
/// Deallocates any internal data of a `V0Payload`
#[no_mangle]
pub extern "C" fn np_ffi_deallocate_v0_payload(payload: V0Payload) -> DeallocateResult {
diff --git a/nearby/presence/np_c_ffi/src/deserialize/v1.rs b/nearby/presence/np_c_ffi/src/deserialize/v1.rs
index dc682a3..598ce9c 100644
--- a/nearby/presence/np_c_ffi/src/deserialize/v1.rs
+++ b/nearby/presence/np_c_ffi/src/deserialize/v1.rs
@@ -13,7 +13,9 @@
// limitations under the License.
use crate::{unwrap, PanicReason};
+use np_ffi_core::common::FixedSizeArray;
use np_ffi_core::deserialize::v1::*;
+use np_ffi_core::deserialize::DecryptMetadataResult;
use np_ffi_core::utils::FfiEnum;
/// Gets the number of legible sections on a deserialized V1 advertisement.
@@ -86,13 +88,77 @@ pub extern "C" fn np_ffi_DeserializedV1Section_get_de(
section.get_de(de_index)
}
+/// Gets the identity details used to decrypt this V1 section, or returns an error if this payload
+/// does not have any associated identity (public advertisement)
+#[no_mangle]
+pub extern "C" fn np_ffi_DeserializedV1Section_get_identity_details(
+ section: DeserializedV1Section,
+) -> GetV1IdentityDetailsResult {
+ section.get_identity_details()
+}
+
+/// Gets the tag of a `GetV1IdentityDetailsResult` tagged-union. On success the wrapped identity
+/// details may be obtained via `GetV0IdentityDetailsResult#into_success`.
+#[no_mangle]
+pub extern "C" fn np_ffi_GetV1IdentityDetailsResult_kind(
+ result: GetV1IdentityDetailsResult,
+) -> GetV1IdentityDetailsResultKind {
+ result.kind()
+}
+
+/// Casts a `GetV1IdentityDetailsResult` 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_GetV1IdentityDetailsResult_into_SUCCESS(
+ result: GetV1IdentityDetailsResult,
+) -> DeserializedV1IdentityDetails {
+ unwrap(result.into_success(), PanicReason::EnumCastFailed)
+}
+
+/// Attempts to decrypt the metadata for the matched credential for this V0 payload (if any)
+#[no_mangle]
+pub extern "C" fn np_ffi_DeserializedV1Section_decrypt_metadata(
+ section: DeserializedV1Section,
+) -> DecryptMetadataResult {
+ section.decrypt_metadata()
+}
+
+/// 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.
+#[no_mangle]
+pub extern "C" fn np_ffi_DeserializedV1Section_derive_16_byte_salt_for_offset(
+ section: DeserializedV1Section,
+ offset: u8,
+) -> GetV1DE16ByteSaltResult {
+ section.derive_16_byte_salt_for_offset(offset)
+}
+
+/// Gets the tag of a `GetV1DE16ByteSaltResult` tagged-union. On success the wrapped identity
+/// details may be obtained via `GetV1DE16ByteSaltResult#into_success`.
+#[no_mangle]
+pub extern "C" fn np_ffi_GetV1DE16ByteSaltResult_kind(
+ result: GetV1DE16ByteSaltResult,
+) -> GetV1DE16ByteSaltResultKind {
+ result.kind()
+}
+
+/// Casts a `GetV1DE16ByteSaltResult` 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_GetV1DE16ByteSaltResult_into_SUCCESS(
+ result: GetV1DE16ByteSaltResult,
+) -> FixedSizeArray<16> {
+ unwrap(result.into_success(), PanicReason::EnumCastFailed)
+}
+
/// Gets the tag of the `GetV1DEResult` tagged-union.
#[no_mangle]
pub extern "C" fn np_ffi_GetV1DEResult_kind(result: GetV1DEResult) -> GetV1DEResultKind {
result.kind()
}
-/// Casts a `GetV1DEResult` to the `Success` vartiant, panicking in the
+/// Casts a `GetV1DEResult` 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_GetV1DEResult_into_SUCCESS(result: GetV1DEResult) -> V1DataElement {
diff --git a/nearby/presence/np_c_ffi/src/lib.rs b/nearby/presence/np_c_ffi/src/lib.rs
index 836b467..50e74b6 100644
--- a/nearby/presence/np_c_ffi/src/lib.rs
+++ b/nearby/presence/np_c_ffi/src/lib.rs
@@ -13,28 +13,16 @@
// limitations under the License.
//! NP Rust C FFI
-// TODO: Remove usage of `lang_items` when ffi is no longer alloc
-#![allow(internal_features)]
-#![feature(lang_items)]
-#![cfg_attr(not(test), no_std)]
-#![allow(dead_code)]
-extern crate alloc;
-extern crate core;
pub mod credentials;
pub mod deserialize;
-#[global_allocator]
-static ALLOCATOR: libc_alloc::LibcAlloc = libc_alloc::LibcAlloc;
-
-extern crate panic_abort;
-
-#[lang = "eh_personality"]
-extern "C" fn eh_personality() {}
+use lock_adapter::std::RwLock;
+use lock_adapter::RwLock as _;
/// Structure for categorized reasons for why a NP C FFI call may
/// be panicking.
-#[derive(Clone, Copy)]
+#[derive(Clone, Copy, Debug)]
#[repr(u8)]
pub enum PanicReason {
/// Some enum cast to a variant failed. Utilized
@@ -90,27 +78,16 @@ impl PanicHandler {
}
Self::system_handler(panic_reason)
}
- #[cfg(feature = "std")]
+
fn system_handler(panic_reason: PanicReason) -> ! {
- eprintln!("NP FFI Panicked: {:?}", panic_reason);
+ std::eprintln!("NP FFI Panicked: {:?}", panic_reason);
let backtrace = std::backtrace::Backtrace::capture();
- eprintln!("Stack trace: {}", backtrace);
- std::process::abort!();
- }
- #[cfg(not(feature = "std"))]
- #[allow(clippy::empty_loop)]
- fn system_handler(_: PanicReason) -> ! {
- // Looping is the only platform-independent thing
- // that we can really do in this scenario.
- // (Even clippy's explanation for the empty-loop
- // lint mentions platform-specific intrinsics
- // as being the only true way to avoid this
- // in a no_std environment.)
- loop {}
+ std::eprintln!("Stack trace: {}", backtrace);
+ std::process::abort()
}
}
-static PANIC_HANDLER: spin::RwLock<PanicHandler> = spin::RwLock::new(PanicHandler::new());
+static PANIC_HANDLER: RwLock<PanicHandler> = RwLock::new(PanicHandler::new());
pub(crate) fn panic(reason: PanicReason) -> ! {
PANIC_HANDLER.read().panic(reason)
@@ -168,6 +145,23 @@ pub extern "C" fn np_ffi_global_config_set_num_shards(num_shards: u8) {
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 @@ class NpCppBenchmark : public benchmark::Fixture {
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 @@ class NpCBenchmark : public benchmark::Fixture {
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 @@ BENCHMARK_REGISTER_F(NpCBenchmark, V0PlaintextAdvertisement)
->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 @@ extern "C" int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size) {
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 @@ extern "C" int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size) {
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 @@ extern "C" int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size) {
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 @@ extern "C" int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size) {
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..cc02007 100644
--- a/nearby/presence/np_cpp_ffi/include/nearby_protocol.h
+++ b/nearby/presence/np_cpp_ffi/include/nearby_protocol.h
@@ -19,6 +19,8 @@
#include "absl/strings/str_format.h"
#include "np_cpp_ffi_types.h"
+#include <span>
+
// This namespace provides a C++ API surface to the Rust nearby protocol
// implementation. This is a wrapper over the np_ffi::internal namespace defined
// in the headers np_cpp_ffi_functions.h and np_cpp_ffi_types.h which are
@@ -50,28 +52,37 @@
namespace nearby_protocol {
// Re-exporting cbindgen generated types which are used in the public API
+using np_ffi::internal::AddCredentialToSlabResult;
using np_ffi::internal::BooleanActionType;
using np_ffi::internal::CreateCredentialBookResultKind;
+using np_ffi::internal::CreateCredentialSlabResultKind;
using np_ffi::internal::DeserializeAdvertisementResultKind;
using np_ffi::internal::DeserializedV0AdvertisementKind;
+using np_ffi::internal::DeserializedV0IdentityDetails;
using np_ffi::internal::DeserializedV0IdentityKind;
+using np_ffi::internal::DeserializedV1IdentityDetails;
using np_ffi::internal::DeserializedV1IdentityKind;
+using np_ffi::internal::EncryptedIdentityType;
using np_ffi::internal::GetV0DEResultKind;
using np_ffi::internal::PanicReason;
using np_ffi::internal::TxPower;
using np_ffi::internal::V0DataElementKind;
+using np_ffi::internal::V1VerificationMode;
template <uintptr_t N> using FfiByteBuffer = np_ffi::internal::ByteBuffer<N>;
// All of the types defined in this header
class RawAdvertisementPayload;
class CredentialBook;
+class CredentialSlab;
class Deserializer;
class DeserializeAdvertisementResult;
+class MatchedCredentialData;
+class V0MatchableCredential;
+class V1MatchableCredential;
// V0 Classes
class DeserializedV0Advertisement;
-class DeserializedV0Identity;
class LegibleDeserializedV0Advertisement;
class V0DataElement;
class V0Payload;
@@ -109,6 +120,12 @@ public:
// 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 +145,44 @@ public:
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();
+
+ // Adds a V0 credential to the slab
+ [[nodiscard]] absl::Status AddV0Credential(V0MatchableCredential v0_cred);
+
+ // Adds a V1 credential to the slab
+ [[nodiscard]] absl::Status AddV1Credential(V1MatchableCredential v1_cred);
+
+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 +203,12 @@ public:
// 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;
@@ -161,6 +219,64 @@ private:
bool moved_;
};
+// Holds data associated with a specific credential which will be returned to
+// the caller when it is successfully matched with an advertisement.
+class MatchedCredentialData {
+public:
+ // Creates matched credential data from a provided credential_id used to
+ // correlate the data back to its full credential data, and the metadata byte
+ // buffer as copied from the given span over bytes. After calling
+ // this the bytes are copied into the rust code, so the
+ // encrypted_metadata_bytes_buffer can be freed.
+ //
+ // Safety: this is safe if the span is over a valid buffer of bytes. The copy
+ // from the memory address isn't atomic, so concurrent modification of the
+ // array from another thread would cause undefined behavior.
+ [[nodiscard]] MatchedCredentialData(uint32_t cred_id,
+ std::span<uint8_t> metadata_bytes);
+
+private:
+ np_ffi::internal::FfiMatchedCredential data_;
+ friend class V0MatchableCredential;
+ friend class V1MatchableCredential;
+};
+
+// Holds the v0 credential data needed by the deserializer to decrypt
+// advertisements, along with some provided matched data that will be returned
+// back to the caller upon a successful credential match.
+class V0MatchableCredential {
+public:
+ // Creates a new V0MatchableCredential from a key seed, its calculated hmac
+ // value and some match data.
+ [[nodiscard]] V0MatchableCredential(
+ std::array<uint8_t, 32> key_seed,
+ std::array<uint8_t, 32> legacy_metadata_key_hmac,
+ MatchedCredentialData matched_credential_data);
+
+private:
+ friend class CredentialSlab;
+ np_ffi::internal::V0MatchableCredential internal_{};
+};
+
+// Holds the v1 credential data needed by the deserializer to decrypt
+// advertisements, along with some provided matched data that will be returned
+// back to the caller upon a successful credential match.
+class V1MatchableCredential {
+public:
+ // Creates a new V1MatchableCredential from key material, its calculated hmac
+ // value and some match data.
+ [[nodiscard]] V1MatchableCredential(
+ std::array<uint8_t, 32> key_seed,
+ std::array<uint8_t, 32> expected_unsigned_metadata_key_hmac,
+ std::array<uint8_t, 32> expected_signed_metadata_key_hmac,
+ std::array<uint8_t, 32> pub_key,
+ MatchedCredentialData matched_credential_data);
+
+private:
+ friend class CredentialSlab;
+ np_ffi::internal::V1MatchableCredential internal_;
+};
+
// Representation of a buffer of bytes returned from deserialization APIs
template <size_t N> class ByteBuffer {
public:
@@ -339,8 +455,9 @@ public:
// 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 +475,6 @@ private:
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:
@@ -391,6 +494,16 @@ public:
// element if it exists otherwise returns an Error status code
[[nodiscard]] absl::StatusOr<V0DataElement> TryGetDataElement(uint8_t index);
+ // Decrypts the metadata of the credential which matched with this
+ // advertisement, or returns an error if the metadata key is invalid and unable
+ // to successfully decrypt the metadata.
+ [[nodiscard]] absl::StatusOr<std::vector<uint8_t>> DecryptMetadata();
+
+ // Gets the details of the identity data element of this payload or returns an
+ // error if the payload does not have an identity (public advertisement)
+ [[nodiscard]] absl::StatusOr<DeserializedV0IdentityDetails>
+ GetIdentityDetails();
+
private:
friend class LegibleDeserializedV0Advertisement;
explicit V0Payload(np_ffi::internal::V0Payload v0_payload)
@@ -480,11 +593,29 @@ class DeserializedV1Section {
public:
// Returns the number of data elements present in the section
[[nodiscard]] uint8_t NumberOfDataElements();
+
// Returns the DeserializedV1IdentityKind of the identity
[[nodiscard]] DeserializedV1IdentityKind GetIdentityKind();
+
// Tries to get the data element in the section at the given index
[[nodiscard]] absl::StatusOr<V1DataElement> TryGetDataElement(uint8_t index);
+ // Decrypts the metadata of the credential which matched with this section
+ [[nodiscard]] absl::StatusOr<std::vector<uint8_t>> DecryptMetadata();
+
+ // Gets the details of the identity data element of this section or returns an
+ // error if the section does not conatin an identity (public section)
+ [[nodiscard]] absl::StatusOr<DeserializedV1IdentityDetails>
+ GetIdentityDetails();
+
+ // 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. The offset should come from a
+ // particular deserialized v1 de via `V1DataElement::GetOffset()`
+ [[nodiscard]] absl::StatusOr<std::array<uint8_t, 16>>
+ DeriveSaltForOffset(uint8_t offset);
+
private:
friend class DeserializedV1Advertisement;
explicit DeserializedV1Section(
@@ -505,6 +636,8 @@ public:
[[nodiscard]] uint32_t GetDataElementTypeCode() const;
// Yields the payload bytes of the data element
[[nodiscard]] ByteBuffer<127> GetPayload() const;
+ /// Gets the offset for this V1 data element.
+ [[nodiscard]] uint8_t GetOffset() const;
private:
friend class DeserializedV1Section;
diff --git a/nearby/presence/np_cpp_ffi/nearby_protocol.cc b/nearby/presence/np_cpp_ffi/nearby_protocol.cc
index 3ee5c63..c2f8566 100644
--- a/nearby/presence/np_cpp_ffi/nearby_protocol.cc
+++ b/nearby/presence/np_cpp_ffi/nearby_protocol.cc
@@ -27,7 +27,7 @@ namespace nearby_protocol {
static void panic_handler(PanicReason reason);
struct PanicHandler {
- void (*handler)(PanicReason);
+ void (* handler)(PanicReason);
bool set_by_client;
};
@@ -43,7 +43,7 @@ static void panic_handler(PanicReason reason) {
std::abort();
}
-static void _assert_panic(bool condition, const char *func, const char *file,
+static void _assert_panic(bool condition, const char* func, const char* file,
int line) {
if (!condition) {
std::cout << "Assert failed: \n function: " << func << "\n file: " << file
@@ -54,7 +54,7 @@ static void _assert_panic(bool condition, const char *func, const char *file,
#define assert_panic(e) _assert_panic(e, __func__, __ASSERT_FILE_NAME, __LINE__)
-bool GlobalConfig::SetPanicHandler(void (*handler)(PanicReason)) {
+bool GlobalConfig::SetPanicHandler(void (* handler)(PanicReason)) {
if (!gPanicHandler.set_by_client) {
gPanicHandler.handler = handler;
gPanicHandler.set_by_client = true;
@@ -67,6 +67,11 @@ void GlobalConfig::SetNumShards(uint8_t num_shards) {
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);
@@ -75,32 +80,118 @@ void GlobalConfig::SetMaxNumCredentialBooks(uint32_t max_num_credential_books) {
void GlobalConfig::SetMaxNumDeserializedV0Advertisements(
uint32_t max_num_deserialized_v0_advertisements) {
np_ffi::internal::
- np_ffi_global_config_set_max_num_deserialized_v0_advertisements(
- max_num_deserialized_v0_advertisements);
+ np_ffi_global_config_set_max_num_deserialized_v0_advertisements(
+ max_num_deserialized_v0_advertisements);
}
void GlobalConfig::SetMaxNumDeserializedV1Advertisements(
uint32_t max_num_deserialized_v1_advertisements) {
np_ffi::internal::
- np_ffi_global_config_set_max_num_deserialized_v1_advertisements(
- max_num_deserialized_v1_advertisements);
+ np_ffi_global_config_set_max_num_deserialized_v1_advertisements(
+ 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(
- result));
- return book;
+ case CreateCredentialSlabResultKind::Success: {
+ auto slab = CredentialSlab(
+ np_ffi::internal::np_ffi_CreateCredentialSlabResult_into_SUCCESS(
+ result));
+ return slab;
+ }
+ case CreateCredentialSlabResultKind::NoSpaceLeft: {
+ return absl::ResourceExhaustedError(
+ "No space left to create credential slab");
+ }
}
- case CreateCredentialBookResultKind::NoSpaceLeft: {
- return absl::ResourceExhaustedError(
- "No space left to create credential book");
+}
+
+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::Status CredentialSlab::AddV0Credential(V0MatchableCredential v0_cred) {
+ assert_panic(!this->moved_);
+ auto result = np_ffi::internal::np_ffi_CredentialSlab_add_v0_credential(
+ this->credential_slab_, v0_cred.internal_);
+ switch (result) {
+ case AddCredentialToSlabResult::Success: {
+ return absl::OkStatus();
+ }
+ case AddCredentialToSlabResult::InvalidHandle: {
+ return absl::InvalidArgumentError(
+ "invalid credential slab handle provided");
+ }
+ }
+}
+
+absl::Status CredentialSlab::AddV1Credential(V1MatchableCredential v1_cred) {
+ assert_panic(!this->moved_);
+ auto result = np_ffi::internal::np_ffi_CredentialSlab_add_v1_credential(
+ this->credential_slab_, v1_cred.internal_);
+ switch (result) {
+ case AddCredentialToSlabResult::Success: {
+ return absl::OkStatus();
+ }
+ case AddCredentialToSlabResult::InvalidHandle: {
+ return absl::InvalidArgumentError(
+ "invalid credential slab handle provided");
+ }
+ }
+}
+
+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.");
+ }
}
}
@@ -112,13 +203,13 @@ CredentialBook::~CredentialBook() {
}
}
-CredentialBook::CredentialBook(CredentialBook &&other) noexcept
+CredentialBook::CredentialBook(CredentialBook&& other) noexcept
: credential_book_(other.credential_book_), moved_(other.moved_) {
other.credential_book_ = {};
other.moved_ = true;
}
-CredentialBook &CredentialBook::operator=(CredentialBook &&other) noexcept {
+CredentialBook& CredentialBook::operator=(CredentialBook&& other) noexcept {
if (this != &other) {
if (!this->moved_) {
auto result = np_ffi::internal::np_ffi_deallocate_credential_book(
@@ -136,8 +227,8 @@ CredentialBook &CredentialBook::operator=(CredentialBook &&other) noexcept {
}
DeserializeAdvertisementResult
-Deserializer::DeserializeAdvertisement(RawAdvertisementPayload &payload,
- const CredentialBook &credential_book) {
+Deserializer::DeserializeAdvertisement(RawAdvertisementPayload& payload,
+ const CredentialBook& credential_book) {
assert_panic(!credential_book.moved_);
auto result = np_ffi::internal::np_ffi_deserialize_advertisement(
{payload.buffer_.internal_}, credential_book.credential_book_);
@@ -175,14 +266,14 @@ DeserializeAdvertisementResult::~DeserializeAdvertisementResult() {
}
DeserializeAdvertisementResult::DeserializeAdvertisementResult(
- DeserializeAdvertisementResult &&other) noexcept
+ DeserializeAdvertisementResult&& other) noexcept
: result_(other.result_), moved_(other.moved_) {
other.result_ = {};
other.moved_ = true;
}
-DeserializeAdvertisementResult &DeserializeAdvertisementResult::operator=(
- DeserializeAdvertisementResult &&other) noexcept {
+DeserializeAdvertisementResult& DeserializeAdvertisementResult::operator=(
+ DeserializeAdvertisementResult&& other) noexcept {
if (this != &other) {
if (!this->moved_) {
auto result =
@@ -201,14 +292,14 @@ DeserializeAdvertisementResult &DeserializeAdvertisementResult::operator=(
// V0 Stuff
DeserializedV0Advertisement::DeserializedV0Advertisement(
- DeserializedV0Advertisement &&other) noexcept
+ DeserializedV0Advertisement&& other) noexcept
: v0_advertisement_(other.v0_advertisement_), moved_(other.moved_) {
other.v0_advertisement_ = {};
other.moved_ = true;
}
-DeserializedV0Advertisement &DeserializedV0Advertisement::operator=(
- DeserializedV0Advertisement &&other) noexcept {
+DeserializedV0Advertisement& DeserializedV0Advertisement::operator=(
+ DeserializedV0Advertisement&& other) noexcept {
if (this != &other) {
if (!this->moved_) {
auto result =
@@ -252,16 +343,16 @@ LegibleDeserializedV0Advertisement DeserializedV0Advertisement::IntoLegible() {
}
LegibleDeserializedV0Advertisement::LegibleDeserializedV0Advertisement(
- LegibleDeserializedV0Advertisement &&other) noexcept
+ LegibleDeserializedV0Advertisement&& other) noexcept
: legible_v0_advertisement_(other.legible_v0_advertisement_),
moved_(other.moved_) {
other.moved_ = true;
other.legible_v0_advertisement_ = {};
}
-LegibleDeserializedV0Advertisement &
+LegibleDeserializedV0Advertisement&
LegibleDeserializedV0Advertisement::operator=(
- LegibleDeserializedV0Advertisement &&other) noexcept {
+ LegibleDeserializedV0Advertisement&& other) noexcept {
if (this != &other) {
if (!this->moved_) {
auto result =
@@ -286,19 +377,20 @@ LegibleDeserializedV0Advertisement::~LegibleDeserializedV0Advertisement() {
}
}
-DeserializedV0Identity LegibleDeserializedV0Advertisement::GetIdentity() {
+DeserializedV0IdentityKind
+LegibleDeserializedV0Advertisement::GetIdentityKind() {
assert_panic(!this->moved_);
- auto result =
- np_ffi::internal::np_ffi_LegibleDeserializedV0Advertisement_into_identity(
- legible_v0_advertisement_);
- return DeserializedV0Identity(result);
+ auto result = np_ffi::internal::
+ np_ffi_LegibleDeserializedV0Advertisement_get_identity_kind(
+ legible_v0_advertisement_);
+ return result;
}
uint8_t LegibleDeserializedV0Advertisement::GetNumberOfDataElements() {
assert_panic(!this->moved_);
return np_ffi::internal::
- np_ffi_LegibleDeserializedV0Advertisement_get_num_des(
- legible_v0_advertisement_);
+ np_ffi_LegibleDeserializedV0Advertisement_get_num_des(
+ legible_v0_advertisement_);
}
V0Payload LegibleDeserializedV0Advertisement::IntoPayload() {
@@ -309,17 +401,13 @@ V0Payload LegibleDeserializedV0Advertisement::IntoPayload() {
return V0Payload(result);
}
-np_ffi::internal::DeserializedV0IdentityKind DeserializedV0Identity::GetKind() {
- return np_ffi::internal::np_ffi_DeserializedV0Identity_kind(v0_identity_);
-}
-
-V0Payload::V0Payload(V0Payload &&other) noexcept
+V0Payload::V0Payload(V0Payload&& other) noexcept
: v0_payload_(other.v0_payload_), moved_(other.moved_) {
other.v0_payload_ = {};
other.moved_ = true;
}
-V0Payload &V0Payload::operator=(V0Payload &&other) noexcept {
+V0Payload& V0Payload::operator=(V0Payload&& other) noexcept {
if (this != &other) {
if (!this->moved_) {
auto result =
@@ -348,16 +436,75 @@ absl::StatusOr<V0DataElement> V0Payload::TryGetDataElement(uint8_t index) {
auto result = np_ffi::internal::np_ffi_V0Payload_get_de(v0_payload_, index);
auto kind = np_ffi::internal::np_ffi_GetV0DEResult_kind(result);
switch (kind) {
- case GetV0DEResultKind::Success: {
- auto de = np_ffi_GetV0DEResult_into_SUCCESS(result);
- return V0DataElement(de);
+ case GetV0DEResultKind::Success: {
+ auto de = np_ffi_GetV0DEResult_into_SUCCESS(result);
+ return V0DataElement(de);
+ }
+ case GetV0DEResultKind::Error: {
+ return absl::OutOfRangeError("Invalid Data Element index");
+ }
}
- case GetV0DEResultKind::Error: {
- return absl::OutOfRangeError("Invalid Data Element index");
+}
+
+static absl::StatusOr<std::vector<uint8_t>>
+MetadataResultToVec(np_ffi::internal::DecryptMetadataResult decrypt_result) {
+ auto kind =
+ np_ffi::internal::np_ffi_DecryptMetadataResult_kind(decrypt_result);
+ switch (kind) {
+ case np_ffi::internal::DecryptMetadataResultKind::Success: {
+ auto metadata =
+ np_ffi::internal::np_ffi_DecryptMetadataResult_into_SUCCESS(
+ decrypt_result);
+ auto parts_result =
+ np_ffi::internal::np_ffi_DecryptedMetadata_get_metadata_buffer_parts(
+ metadata);
+ // The handle is guaranteed to be valid by the C++ wrapper so this should
+ // never fail
+ assert_panic(np_ffi::internal::np_ffi_GetMetadataBufferPartsResult_kind(
+ parts_result) ==
+ np_ffi::internal::GetMetadataBufferPartsResultKind::Success);
+ auto parts =
+ np_ffi::internal::np_ffi_GetMetadataBufferPartsResult_into_SUCCESS(
+ parts_result);
+ std::vector<uint8_t> result(parts.ptr, parts.ptr + parts.len);
+
+ // Now that the contents have been copied into the vec, the underlying
+ // handle can be de-allocated
+ auto deallocate_result =
+ np_ffi::internal::np_ffi_deallocate_DecryptedMetadata(metadata);
+ assert_panic(deallocate_result ==
+ np_ffi::internal::DeallocateResult::Success);
+ return result;
+ }
+ case np_ffi::internal::DecryptMetadataResultKind::Error: {
+ return absl::InvalidArgumentError("Invalid V0 payload handle");
+ }
}
+}
+
+absl::StatusOr<DeserializedV0IdentityDetails> V0Payload::GetIdentityDetails() {
+ assert_panic(!this->moved_);
+ auto result = np_ffi::internal::np_ffi_V0Payload_get_identity_details(
+ this->v0_payload_);
+ auto kind = np_ffi::internal::np_ffi_GetV0IdentityDetailsResult_kind(result);
+ switch (kind) {
+ case np_ffi::internal::GetV0IdentityDetailsResultKind::Error: {
+ return absl::InvalidArgumentError("Invalid handle");
+ }
+ case np_ffi::internal::GetV0IdentityDetailsResultKind::Success: {
+ return np_ffi::internal::np_ffi_GetV0IdentityDetailsResult_into_SUCCESS(
+ result);
+ }
}
}
+absl::StatusOr<std::vector<uint8_t>> V0Payload::DecryptMetadata() {
+ assert_panic(!this->moved_);
+ auto decrypt_result =
+ np_ffi::internal::np_ffi_V0Payload_decrypt_metadata(this->v0_payload_);
+ return MetadataResultToVec(decrypt_result);
+}
+
V0DataElementKind V0DataElement::GetKind() {
return np_ffi::internal::np_ffi_V0DataElement_kind(v0_data_element_);
}
@@ -387,7 +534,7 @@ uint8_t V0Actions::GetContextSyncSequenceNumber() {
// This is called after all references to the shared_ptr have gone out of scope
auto DeallocateV1Adv(
- np_ffi::internal::DeserializedV1Advertisement *v1_advertisement) {
+ np_ffi::internal::DeserializedV1Advertisement* v1_advertisement) {
auto result =
np_ffi::internal::np_ffi_deallocate_deserialized_V1_advertisement(
*v1_advertisement);
@@ -404,11 +551,11 @@ DeserializedV1Advertisement::DeserializedV1Advertisement(
}
DeserializedV1Advertisement::DeserializedV1Advertisement(
- DeserializedV1Advertisement &&other) noexcept
+ DeserializedV1Advertisement&& other) noexcept
: v1_advertisement_(std::move(other.v1_advertisement_)) {}
-DeserializedV1Advertisement &DeserializedV1Advertisement::operator=(
- DeserializedV1Advertisement &&other) noexcept {
+DeserializedV1Advertisement& DeserializedV1Advertisement::operator=(
+ DeserializedV1Advertisement&& other) noexcept {
if (this != &other) {
this->v1_advertisement_ = std::move(other.v1_advertisement_);
}
@@ -419,15 +566,15 @@ DeserializedV1Advertisement &DeserializedV1Advertisement::operator=(
uint8_t DeserializedV1Advertisement::GetNumLegibleSections() {
assert_panic(this->v1_advertisement_ != nullptr);
return np_ffi::internal::
- np_ffi_DeserializedV1Advertisement_get_num_legible_sections(
- *v1_advertisement_);
+ np_ffi_DeserializedV1Advertisement_get_num_legible_sections(
+ *v1_advertisement_);
}
uint8_t DeserializedV1Advertisement::GetNumUndecryptableSections() {
assert_panic(this->v1_advertisement_ != nullptr);
return np_ffi::internal::
- np_ffi_DeserializedV1Advertisement_get_num_undecryptable_sections(
- *v1_advertisement_);
+ np_ffi_DeserializedV1Advertisement_get_num_undecryptable_sections(
+ *v1_advertisement_);
}
absl::StatusOr<DeserializedV1Section>
@@ -438,14 +585,14 @@ DeserializedV1Advertisement::TryGetSection(uint8_t section_index) {
*v1_advertisement_, section_index);
auto kind = np_ffi::internal::np_ffi_GetV1SectionResult_kind(result);
switch (kind) {
- case np_ffi::internal::GetV1SectionResultKind::Error: {
- return absl::OutOfRangeError("Invalid section index");
- }
- case np_ffi::internal::GetV1SectionResultKind::Success: {
- auto section =
- np_ffi::internal::np_ffi_GetV1SectionResult_into_SUCCESS(result);
- return DeserializedV1Section(section, v1_advertisement_);
- }
+ case np_ffi::internal::GetV1SectionResultKind::Error: {
+ return absl::OutOfRangeError("Invalid section index");
+ }
+ case np_ffi::internal::GetV1SectionResultKind::Success: {
+ auto section =
+ np_ffi::internal::np_ffi_GetV1SectionResult_into_SUCCESS(result);
+ return DeserializedV1Section(section, v1_advertisement_);
+ }
}
}
@@ -464,23 +611,112 @@ DeserializedV1Section::TryGetDataElement(uint8_t index) {
np_ffi::internal::np_ffi_DeserializedV1Section_get_de(section_, index);
auto kind = np_ffi::internal::np_ffi_GetV1DEResult_kind(result);
switch (kind) {
- case np_ffi::internal::GetV1DEResultKind::Error: {
- return absl::OutOfRangeError("Invalid data element index for this section");
+ case np_ffi::internal::GetV1DEResultKind::Error: {
+ return absl::OutOfRangeError("Invalid data element index for this section");
+ }
+ case np_ffi::internal::GetV1DEResultKind::Success: {
+ return V1DataElement(
+ np_ffi::internal::np_ffi_GetV1DEResult_into_SUCCESS(result));
+ }
}
- case np_ffi::internal::GetV1DEResultKind::Success: {
- return V1DataElement(
- np_ffi::internal::np_ffi_GetV1DEResult_into_SUCCESS(result));
+}
+
+absl::StatusOr<std::vector<uint8_t>> DeserializedV1Section::DecryptMetadata() {
+ assert_panic(this->owning_v1_advertisement_ != nullptr);
+ auto decrypt_result =
+ np_ffi::internal::np_ffi_DeserializedV1Section_decrypt_metadata(
+ this->section_);
+ return MetadataResultToVec(decrypt_result);
+}
+
+absl::StatusOr<DeserializedV1IdentityDetails>
+DeserializedV1Section::GetIdentityDetails() {
+ assert_panic(this->owning_v1_advertisement_ != nullptr);
+ auto result =
+ np_ffi::internal::np_ffi_DeserializedV1Section_get_identity_details(
+ this->section_);
+ auto kind = np_ffi::internal::np_ffi_GetV1IdentityDetailsResult_kind(result);
+ switch (kind) {
+ case np_ffi::internal::GetV1IdentityDetailsResultKind::Error: {
+ return absl::InvalidArgumentError("Invalid handle");
+ }
+ case np_ffi::internal::GetV1IdentityDetailsResultKind::Success: {
+ return np_ffi::internal::np_ffi_GetV1IdentityDetailsResult_into_SUCCESS(
+ result);
+ }
}
+}
+
+absl::StatusOr<std::array<uint8_t, 16>> DeserializedV1Section::DeriveSaltForOffset(
+ uint8_t offset) {
+ auto result =
+ np_ffi::internal::np_ffi_DeserializedV1Section_derive_16_byte_salt_for_offset(
+ this->section_,
+ offset);
+ auto kind = np_ffi::internal::np_ffi_GetV1DE16ByteSaltResult_kind(result);
+ switch (kind) {
+ case np_ffi::internal::GetV1DE16ByteSaltResultKind::Error: {
+ return absl::InvalidArgumentError("Failed to derive salt for offset");
+ }
+ case np_ffi::internal::GetV1DE16ByteSaltResultKind::Success: {
+ auto buffer = np_ffi::internal::np_ffi_GetV1DE16ByteSaltResult_into_SUCCESS(result);
+ return std::to_array(buffer._0);
+ }
}
}
uint32_t V1DataElement::GetDataElementTypeCode() const {
return np_ffi::internal::np_ffi_V1DEType_to_uint32_t(
- v1_data_element_.generic._0.de_type);
+ np_ffi::internal::np_ffi_V1DataElement_to_generic(this->v1_data_element_)
+ .de_type);
}
ByteBuffer<127> V1DataElement::GetPayload() const {
- return ByteBuffer(v1_data_element_.generic._0.payload);
+ return ByteBuffer(
+ np_ffi::internal::np_ffi_V1DataElement_to_generic(this->v1_data_element_)
+ .payload);
+}
+
+uint8_t V1DataElement::GetOffset() const {
+ return np_ffi::internal::np_ffi_V1DataElement_to_generic(
+ this->v1_data_element_)
+ .offset;
+}
+
+MatchedCredentialData::MatchedCredentialData(
+ uint32_t cred_id, std::span<uint8_t> metadata_bytes) {
+ this->data_ = {cred_id, metadata_bytes.data(), metadata_bytes.size()};
}
+template<typename T, size_t N>
+static void CopyToRawArray(T (& dest)[N], const std::array<T, N>& src) {
+ memcpy(dest, src.data(), sizeof(T) * N);
+}
+
+V0MatchableCredential::V0MatchableCredential(
+ std::array<uint8_t, 32> key_seed,
+ std::array<uint8_t, 32> legacy_metadata_key_hmac,
+ MatchedCredentialData matched_credential_data) {
+ np_ffi::internal::V0DiscoveryCredential discovery_cred{};
+ CopyToRawArray(discovery_cred.key_seed, key_seed);
+ CopyToRawArray(discovery_cred.legacy_metadata_key_hmac,
+ legacy_metadata_key_hmac);
+ this->internal_ = {discovery_cred, matched_credential_data.data_};
+}
+
+V1MatchableCredential::V1MatchableCredential(
+ std::array<uint8_t, 32> key_seed,
+ std::array<uint8_t, 32> expected_unsigned_metadata_key_hmac,
+ std::array<uint8_t, 32> expected_signed_metadata_key_hmac,
+ std::array<uint8_t, 32> pub_key,
+ MatchedCredentialData matched_credential_data) {
+ np_ffi::internal::V1DiscoveryCredential discovery_cred{};
+ CopyToRawArray(discovery_cred.key_seed, key_seed);
+ CopyToRawArray(discovery_cred.expected_unsigned_metadata_key_hmac,
+ expected_unsigned_metadata_key_hmac);
+ CopyToRawArray(discovery_cred.expected_signed_metadata_key_hmac,
+ expected_signed_metadata_key_hmac);
+ CopyToRawArray(discovery_cred.pub_key, pub_key);
+ this->internal_ = {discovery_cred, matched_credential_data.data_};
+}
} // namespace nearby_protocol
diff --git a/nearby/presence/np_cpp_ffi/sample/CMakeLists.txt b/nearby/presence/np_cpp_ffi/sample/CMakeLists.txt
index da33258..3987edd 100644
--- a/nearby/presence/np_cpp_ffi/sample/CMakeLists.txt
+++ b/nearby/presence/np_cpp_ffi/sample/CMakeLists.txt
@@ -12,7 +12,7 @@
# See the License for the specific language governing permissions and
# limitations under the License.
-add_executable(np_cpp_sample main.cpp)
+add_executable(np_cpp_sample main.cc)
target_link_libraries(np_cpp_sample nearby_protocol)
diff --git a/nearby/presence/np_cpp_ffi/sample/main.cpp b/nearby/presence/np_cpp_ffi/sample/main.cc
index 114ed64..1d273da 100644
--- a/nearby/presence/np_cpp_ffi/sample/main.cpp
+++ b/nearby/presence/np_cpp_ffi/sample/main.cc
@@ -25,7 +25,7 @@ void HandleAdvertisementResult(nearby_protocol::DeserializeAdvertisementResult);
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 @@ int main() {
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;
@@ -74,10 +81,8 @@ int main() {
auto v1_byte_string = "20" // V1 Advertisement header
"04" // Section Header
"03" // Public Identity DE header
- "260046" // Length 2 Actions DE
- "03" // Section Header
- "03" // Public Identity DE header
- "1505"; // Length 1 Tx Power DE with value 5
+ "260046";// Length 2 Actions DE
+
auto v1_bytes = absl::HexStringToBytes(v1_byte_string);
auto v1_buffer = nearby_protocol::ByteBuffer<255>::CopyFrom(v1_bytes);
nearby_protocol::RawAdvertisementPayload v1_payload(v1_buffer.value());
@@ -132,7 +137,7 @@ static void SamplePanicHandler(nearby_protocol::PanicReason reason) {
std::cout << "AssertFailed \n";
break;
}
- case np_ffi::internal::PanicReason::InvalidActionBits: {
+ case nearby_protocol::PanicReason::InvalidActionBits: {
std::cout << "InvalidActionBits \n";
break;
}
@@ -171,7 +176,7 @@ void HandleV0Adv(nearby_protocol::DeserializedV0Advertisement result) {
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 +192,8 @@ void HandleLegibleV0Adv(
}
}
-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/shared/shared_test_util.cc b/nearby/presence/np_cpp_ffi/shared/shared_test_util.cc
index 27e98a6..67ca828 100644
--- a/nearby/presence/np_cpp_ffi/shared/shared_test_util.cc
+++ b/nearby/presence/np_cpp_ffi/shared/shared_test_util.cc
@@ -14,6 +14,9 @@
#include "nearby_protocol.h"
+#include <cstdlib>
+#include <cstddef> // IWYU pragma: keep
+
std::string PanicReasonToString(nearby_protocol::PanicReason reason) {
switch (reason) {
case nearby_protocol::PanicReason::EnumCastFailed: {
@@ -22,7 +25,7 @@ std::string PanicReasonToString(nearby_protocol::PanicReason reason) {
case nearby_protocol::PanicReason::AssertFailed: {
return "AssertFailed";
}
- case np_ffi::internal::PanicReason::InvalidActionBits: {
+ case nearby_protocol::PanicReason::InvalidActionBits: {
return "InvalidActionBits";
}
}
diff --git a/nearby/presence/np_cpp_ffi/tests/CMakeLists.txt b/nearby/presence/np_cpp_ffi/tests/CMakeLists.txt
index bddda64..549a072 100644
--- a/nearby/presence/np_cpp_ffi/tests/CMakeLists.txt
+++ b/nearby/presence/np_cpp_ffi/tests/CMakeLists.txt
@@ -14,12 +14,16 @@
add_executable(
np_ffi_tests
- deserialize_result_tests.cc
- deserialize_v0_tests.cc
- deserialize_v1_tests.cc
- global_config_tests.cc
- credential_book_tests.cc
byte_buffer_tests.cc
+ credential_book_tests.cc
+ credential_slab_tests.cc
+ deserialize_result_tests.cc
+ np_cpp_test.h
+ np_cpp_test.cc
+ v0_private_identity_tests.cc
+ v0_public_identity_tests.cc
+ v1_private_identity_tests.cc
+ v1_public_identity_tests.cc
)
target_link_libraries(
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..7e1a048 100644
--- a/nearby/presence/np_cpp_ffi/tests/byte_buffer_tests.cc
+++ b/nearby/presence/np_cpp_ffi/tests/byte_buffer_tests.cc
@@ -13,12 +13,13 @@
// limitations under the License.
#include "nearby_protocol.h"
+#include "np_cpp_test.h"
#include "shared_test_util.h"
#include "absl/strings/escaping.h"
#include "gtest/gtest.h"
-TEST(ByteBufferTests, ByteBufferMaxLength) {
+TEST_F(NpCppTest, ByteBufferMaxLength) {
// Each hex byte takes up 2 characters so length 510 string = 255 bytes of hex
auto str_bytes = generate_hex_string(510);
auto bytes = absl::HexStringToBytes(str_bytes);
@@ -28,7 +29,7 @@ TEST(ByteBufferTests, ByteBufferMaxLength) {
ASSERT_EQ(bytes, string);
}
-TEST(ByteBufferTooLarge, ByteBufferInvalidLength) {
+TEST_F(NpCppTest, ByteBufferInvalidLength) {
// 256 bytes should fail
auto str_bytes = generate_hex_string(512);
auto bytes = absl::HexStringToBytes(str_bytes);
@@ -36,26 +37,26 @@ TEST(ByteBufferTooLarge, ByteBufferInvalidLength) {
ASSERT_FALSE(buffer.ok());
}
-TEST(ByteBufferTests, ByteBufferRoundTrip) {
+TEST_F(NpCppTest, ByteBufferRoundTrip) {
auto bytes = absl::HexStringToBytes("2003031503");
auto buffer = nearby_protocol::ByteBuffer<255>::CopyFrom(bytes);
auto string = buffer.value().ToString();
ASSERT_EQ(bytes, string);
}
-TEST(ByteBufferTests, ByteBufferPayloadWrongSize) {
+TEST_F(NpCppTest, ByteBufferPayloadWrongSize) {
auto bytes = absl::HexStringToBytes("1111111111111111111111");
auto buffer = nearby_protocol::ByteBuffer<10>::CopyFrom(bytes);
ASSERT_FALSE(buffer.ok());
}
-TEST(ByteBufferTests, ByteBufferEmptyString) {
+TEST_F(NpCppTest, ByteBufferEmptyString) {
auto bytes = absl::HexStringToBytes("");
auto buffer = nearby_protocol::ByteBuffer<10>::CopyFrom(bytes);
ASSERT_TRUE(buffer.ok());
}
-TEST(ByteBufferTests, ByteBufferToVector) {
+TEST_F(NpCppTest, ByteBufferToVector) {
auto bytes = absl::HexStringToBytes("1234567890");
auto buffer = nearby_protocol::ByteBuffer<100>::CopyFrom(bytes);
auto vec = buffer.value().ToVector();
@@ -63,16 +64,19 @@ TEST(ByteBufferTests, ByteBufferToVector) {
ASSERT_EQ(vec, expected);
}
-TEST(ByteBufferTests, ByteBufferEndToEndPayloadAsString) {
+TEST_F(NpCppTest, ByteBufferEndToEndPayloadAsString) {
std::string bytes = absl::HexStringToBytes("2003031503");
auto buffer = nearby_protocol::ByteBuffer<255>::CopyFrom(bytes);
ASSERT_TRUE(buffer.ok());
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 5eb9ce2..af8fc0d 100644
--- a/nearby/presence/np_cpp_ffi/tests/credential_book_tests.cc
+++ b/nearby/presence/np_cpp_ffi/tests/credential_book_tests.cc
@@ -14,13 +14,35 @@
#include "nearby_protocol.h"
#include "shared_test_util.h"
+#include "np_cpp_test.h"
#include "gtest/gtest.h"
-TEST(NpFfiCredentialBookTests, TestMoveConstructor) {
- auto book = nearby_protocol::CredentialBook::TryCreate().value();
+TEST_F(NpCppTest, TestSetMaxCredBooks) {
+ 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 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 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, TestBookMoveConstructor) {
+ auto slab = nearby_protocol::CredentialSlab::TryCreate().value();
+ auto book = nearby_protocol::CredentialBook::TryCreateFromSlab(slab).value();
auto deserialize_result =
- nearby_protocol::Deserializer::DeserializeAdvertisement(V0AdvEmpty, book);
+ nearby_protocol::Deserializer::DeserializeAdvertisement(V0AdvSimple,
+ book);
ASSERT_EQ(deserialize_result.GetKind(),
np_ffi::internal::DeserializeAdvertisementResultKind::V0);
@@ -28,7 +50,7 @@ TEST(NpFfiCredentialBookTests, TestMoveConstructor) {
// still result in success
nearby_protocol::CredentialBook next_book(std::move(book));
auto deserialize_result_moved =
- nearby_protocol::Deserializer::DeserializeAdvertisement(V0AdvEmpty,
+ nearby_protocol::Deserializer::DeserializeAdvertisement(V0AdvSimple,
next_book);
ASSERT_EQ(deserialize_result_moved.GetKind(),
np_ffi::internal::DeserializeAdvertisementResultKind::V0);
@@ -36,31 +58,34 @@ TEST(NpFfiCredentialBookTests, TestMoveConstructor) {
// The old object should now lead to use after moved assert failure
ASSERT_DEATH([[maybe_unused]] auto failure =
nearby_protocol::Deserializer::DeserializeAdvertisement(
- V0AdvEmpty, book), // NOLINT(bugprone-use-after-move)
+ V0AdvSimple, book), // NOLINT(bugprone-use-after-move)
"");
// moving again should still lead to a use after moved assert failure
nearby_protocol::CredentialBook another_moved_book(std::move(book));
ASSERT_DEATH([[maybe_unused]] auto failure =
nearby_protocol::Deserializer::DeserializeAdvertisement(
- V0AdvEmpty, another_moved_book),
+ V0AdvSimple, another_moved_book),
"");
}
-TEST(NpFfiCredentialBookTests, TestMoveAssignment) {
- auto book = nearby_protocol::CredentialBook::TryCreate().value();
+TEST_F(NpCppTest, TestBookMoveAssignment) {
+ auto slab = nearby_protocol::CredentialSlab::TryCreate().value();
+ auto book = nearby_protocol::CredentialBook::TryCreateFromSlab(slab).value();
auto deserialize_result =
- nearby_protocol::Deserializer::DeserializeAdvertisement(V0AdvEmpty, book);
+ nearby_protocol::Deserializer::DeserializeAdvertisement(V0AdvSimple,
+ book);
ASSERT_EQ(deserialize_result.GetKind(),
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
auto deserialize_result_other =
- nearby_protocol::Deserializer::DeserializeAdvertisement(V0AdvEmpty,
+ nearby_protocol::Deserializer::DeserializeAdvertisement(V0AdvSimple,
other_book);
ASSERT_EQ(deserialize_result_other.GetKind(),
np_ffi::internal::DeserializeAdvertisementResultKind::V0);
@@ -68,13 +93,13 @@ TEST(NpFfiCredentialBookTests, TestMoveAssignment) {
// The old object should now lead to use after moved assert failure
ASSERT_DEATH([[maybe_unused]] auto failure =
nearby_protocol::Deserializer::DeserializeAdvertisement(
- V0AdvEmpty, book), // NOLINT(bugprone-use-after-move)
+ V0AdvSimple, book), // NOLINT(bugprone-use-after-move)
"");
// moving again should still lead to a use after moved assert failure
auto another_moved_book = std::move(book);
ASSERT_DEATH([[maybe_unused]] auto failure =
nearby_protocol::Deserializer::DeserializeAdvertisement(
- V0AdvEmpty, another_moved_book),
+ 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..c8ddb3c
--- /dev/null
+++ b/nearby/presence/np_cpp_ffi/tests/credential_slab_tests.cc
@@ -0,0 +1,226 @@
+// 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 "np_cpp_test.h"
+#include "shared_test_util.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),
+ "");
+}
+
+TEST_F(NpCppTest, TestSlabDestructor) {
+ {
+ 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()));
+ }
+
+ // Now that the above variables have gone out of scope we should verify that
+ // the destructor succeeded in cleaning up those resources
+ auto slab_result = nearby_protocol::CredentialSlab::TryCreate();
+ ASSERT_TRUE(slab_result.ok());
+}
+
+TEST_F(NpCppTest, TestSlabMoveAssignment) {
+ auto slab_result = nearby_protocol::CredentialSlab::TryCreate();
+ ASSERT_TRUE(slab_result.ok());
+
+ // create a second slab
+ auto other_slab_result = nearby_protocol::CredentialSlab::TryCreate();
+ ASSERT_TRUE(other_slab_result.ok());
+
+ // move assignment should override currently assigned slab with new one,
+ // freeing the existing one.
+ auto other_slab = std::move(slab_result.value());
+ auto maybe_book =
+ nearby_protocol::CredentialBook::TryCreateFromSlab(other_slab);
+ ASSERT_TRUE(maybe_book.ok());
+
+ // The old object should now lead to use after moved assert failure
+ ASSERT_DEATH([[maybe_unused]] auto failure =
+ nearby_protocol::CredentialBook::TryCreateFromSlab(
+ slab_result.value()), // NOLINT(bugprone-use-after-move)
+ "");
+
+ // moving again should still lead to a use after moved assert failure
+ auto another_moved_book = std::move(slab_result.value());
+ ASSERT_DEATH([[maybe_unused]] auto failure =
+ nearby_protocol::CredentialBook::TryCreateFromSlab(
+ another_moved_book), // NOLINT(bugprone-use-after-move)
+ "");
+}
+
+TEST_F(NpCppTest, TestAddV0Credential) {
+ auto slab_result = nearby_protocol::CredentialSlab::TryCreate();
+ ASSERT_TRUE(slab_result.ok());
+
+ uint8_t metadata[] = {1, 2, 3};
+ std::span<uint8_t> metadata_span(metadata);
+
+ nearby_protocol::MatchedCredentialData match_data(111, metadata_span);
+ std::array<uint8_t, 32> key_seed{1, 2, 3};
+ std::array<uint8_t, 32> legacy_metadata_key_hmac{1, 2, 3};
+
+ nearby_protocol::V0MatchableCredential v0_cred(
+ key_seed, legacy_metadata_key_hmac, match_data);
+ auto add_result = slab_result.value().AddV0Credential(v0_cred);
+ ASSERT_EQ(add_result, absl::OkStatus());
+}
+
+TEST_F(NpCppTest, TestAddV0CredentialAfterMoved) {
+ auto slab_result = nearby_protocol::CredentialSlab::TryCreate();
+ ASSERT_TRUE(slab_result.ok());
+
+ // creating a book will move the slab
+ auto maybe_book =
+ nearby_protocol::CredentialBook::TryCreateFromSlab(slab_result.value());
+ ASSERT_TRUE(maybe_book.ok());
+
+ uint8_t metadata[] = {1, 2, 3};
+ std::span<uint8_t> metadata_span(metadata);
+ nearby_protocol::MatchedCredentialData match_data(111, metadata_span);
+ std::array<uint8_t, 32> key_seed{1, 2, 3};
+ std::array<uint8_t, 32> legacy_metadata_key_hmac{1, 2, 3};
+ nearby_protocol::V0MatchableCredential v0_cred(
+ key_seed, legacy_metadata_key_hmac, match_data);
+
+ ASSERT_DEATH([[maybe_unused]] auto add_result =
+ slab_result.value().AddV0Credential(v0_cred);
+ , "");
+}
+
+TEST_F(NpCppTest, TestAddV1Credential) {
+ auto slab_result = nearby_protocol::CredentialSlab::TryCreate();
+ ASSERT_TRUE(slab_result.ok());
+
+ uint8_t metadata[] = {1, 2, 3};
+ std::span<uint8_t> metadata_span(metadata);
+ nearby_protocol::MatchedCredentialData match_data(111, metadata_span);
+ std::array<uint8_t, 32> key_seed{1, 2, 3};
+ std::array<uint8_t, 32> expected_unsigned_metadata_key_hmac{1, 2, 3};
+ std::array<uint8_t, 32> expected_signed_metadata_key_hmac{1, 2, 3};
+ std::array<uint8_t, 32> pub_key{1, 2, 3};
+ nearby_protocol::V1MatchableCredential v1_cred(
+ key_seed, expected_unsigned_metadata_key_hmac,
+ expected_signed_metadata_key_hmac, pub_key, match_data);
+
+ auto add_result = slab_result.value().AddV1Credential(v1_cred);
+ ASSERT_EQ(add_result, absl::OkStatus());
+}
+
+TEST_F(NpCppTest, TestAddV1CredentialAfterMoved) {
+ auto slab_result = nearby_protocol::CredentialSlab::TryCreate();
+ ASSERT_TRUE(slab_result.ok());
+
+ // creating a book will move the slab
+ auto maybe_book =
+ nearby_protocol::CredentialBook::TryCreateFromSlab(slab_result.value());
+ ASSERT_TRUE(maybe_book.ok());
+
+ uint8_t metadata[] = {1, 2, 3};
+ std::span<uint8_t> metadata_span(metadata);
+ nearby_protocol::MatchedCredentialData match_data(111, metadata_span);
+ std::array<uint8_t, 32> key_seed{1, 2, 3};
+ std::array<uint8_t, 32> expected_unsigned_metadata_key_hmac{1, 2, 3};
+ std::array<uint8_t, 32> expected_signed_metadata_key_hmac{1, 2, 3};
+ std::array<uint8_t, 32> pub_key{1, 2, 3};
+ nearby_protocol::V1MatchableCredential v1_cred(
+ key_seed, expected_unsigned_metadata_key_hmac,
+ expected_signed_metadata_key_hmac, pub_key, match_data);
+
+ ASSERT_DEATH([[maybe_unused]] auto add_result =
+ slab_result.value().AddV1Credential(v1_cred);
+ , "");
+}
+
+// make sure the book can be populated with many credentials
+TEST_F(NpCppTest, TestAddManyCredentials) {
+ auto slab_result = nearby_protocol::CredentialSlab::TryCreate();
+ ASSERT_TRUE(slab_result.ok());
+
+ // Should be able to load the slab up with many credentials
+ for (int i = 0; i < 500; i++) {
+ uint8_t metadata[] = {1, 2, 3};
+ std::span<uint8_t> metadata_span(metadata);
+ nearby_protocol::MatchedCredentialData match_data(111, metadata_span);
+ std::array<uint8_t, 32> key_seed{1, 2, 3};
+ std::array<uint8_t, 32> legacy_metadata_key_hmac{1, 2, 3};
+ nearby_protocol::V0MatchableCredential v0_cred(
+ key_seed, legacy_metadata_key_hmac, match_data);
+ auto add_result = slab_result->AddV0Credential(v0_cred);
+ ASSERT_EQ(add_result, absl::OkStatus());
+
+ std::array<uint8_t, 32> v1_key_seed{1, 2, 3};
+ std::array<uint8_t, 32> v1_expected_unsigned_metadata_key_hmac{1, 2, 3};
+ std::array<uint8_t, 32> v1_expected_signed_metadata_key_hmac{1, 2, 3};
+ std::array<uint8_t, 32> v1_pub_key{1, 2, 3};
+ nearby_protocol::V1MatchableCredential v1_cred(
+ v1_key_seed, v1_expected_unsigned_metadata_key_hmac,
+ v1_expected_signed_metadata_key_hmac, v1_pub_key, match_data);
+
+ auto add_v1_result = slab_result->AddV1Credential(v1_cred);
+ ASSERT_EQ(add_v1_result, absl::OkStatus());
+ }
+ ASSERT_TRUE(
+ nearby_protocol::CredentialBook::TryCreateFromSlab(*slab_result).ok());
+}
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 e3a44d5..0469933 100644
--- a/nearby/presence/np_cpp_ffi/tests/deserialize_result_tests.cc
+++ b/nearby/presence/np_cpp_ffi/tests/deserialize_result_tests.cc
@@ -14,14 +14,16 @@
#include "nearby_protocol.h"
#include "shared_test_util.h"
+#include "np_cpp_test.h"
#include "absl/strings/escaping.h"
#include "gtest/gtest.h"
-TEST(NpFfiDeserializeResultTests, TestResultMoveConstructor) {
- auto book = nearby_protocol::CredentialBook::TryCreate().value();
- auto result =
- nearby_protocol::Deserializer::DeserializeAdvertisement(V0AdvEmpty, book);
+TEST_F(NpCppTest, TestResultMoveConstructor) {
+ 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(),
np_ffi::internal::DeserializeAdvertisementResultKind::V0);
@@ -49,15 +51,19 @@ TEST(NpFfiDeserializeResultTests, TestResultMoveConstructor) {
ASSERT_DEATH([[maybe_unused]] auto failure = moved_again.GetKind(), "");
}
-TEST(NpFfiDeserializeResultTests, DeserializeFromStringView) {
+TEST_F(NpCppTest, DeserializeFromStringView) {
auto bytes = absl::HexStringToBytes("00031503");
auto buffer = nearby_protocol::ByteBuffer<255>::CopyFrom(bytes);
ASSERT_TRUE(buffer.ok());
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());
@@ -69,8 +75,8 @@ TEST(NpFfiDeserializeResultTests, DeserializeFromStringView) {
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();
@@ -86,16 +92,17 @@ TEST(NpFfiDeserializeResultTests, DeserializeFromStringView) {
ASSERT_EQ(tx_power.tx_power, 3);
}
-TEST(NpFfiDeserializeResultTests, TestResultMoveAssignment) {
- auto book = nearby_protocol::CredentialBook::TryCreate().value();
- auto result =
- nearby_protocol::Deserializer::DeserializeAdvertisement(V0AdvEmpty, book);
+TEST_F(NpCppTest, TestResultMoveAssignment) {
+ 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(),
np_ffi::internal::DeserializeAdvertisementResultKind::V0);
// create a second result
- auto another_result =
- nearby_protocol::Deserializer::DeserializeAdvertisement(V0AdvEmpty, book);
+ auto another_result = nearby_protocol::Deserializer::DeserializeAdvertisement(
+ V0AdvSimple, book);
ASSERT_EQ(another_result.GetKind(),
np_ffi::internal::DeserializeAdvertisementResultKind::V0);
@@ -119,15 +126,17 @@ TEST(NpFfiDeserializeResultTests, TestResultMoveAssignment) {
ASSERT_DEATH([[maybe_unused]] auto failure = moved_again.GetKind(), "");
}
-TEST(NpFfiDeserializeResultTests, TestInvalidPayloadHeader) {
- ASSERT_TRUE(
- nearby_protocol::GlobalConfig::SetPanicHandler(test_panic_handler));
-
+TEST_F(NpCppTest, TestInvalidPayloadHeader) {
// An invalid header result should result in error
nearby_protocol::RawAdvertisementPayload InvalidHeaderPayload(
nearby_protocol::ByteBuffer<255>({1, {0xFF}}));
- 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());
@@ -141,11 +150,13 @@ TEST(NpFfiDeserializeResultTests, TestInvalidPayloadHeader) {
"");
}
-TEST(NpFfiDeserializeResultTests, TestInvalidV0Cast) {
- ASSERT_TRUE(
- nearby_protocol::GlobalConfig::SetPanicHandler(test_panic_handler));
+TEST_F(NpCppTest, TestInvalidV0Cast) {
+ 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 maybe_credential_book = nearby_protocol::CredentialBook::TryCreate();
auto deserialize_result =
nearby_protocol::Deserializer::DeserializeAdvertisement(
V1AdvSimple, maybe_credential_book.value());
@@ -156,16 +167,18 @@ TEST(NpFfiDeserializeResultTests, TestInvalidV0Cast) {
"");
}
-TEST(NpFfiDeserializeResultTests, TestInvalidV1Cast) {
- ASSERT_TRUE(
- nearby_protocol::GlobalConfig::SetPanicHandler(test_panic_handler));
-
+TEST_F(NpCppTest, TestInvalidV1Cast) {
// Create an empty credential book and verify that is is successful
- auto maybe_credential_book = nearby_protocol::CredentialBook::TryCreate();
+ 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(
- V0AdvEmpty, maybe_credential_book.value());
+ V0AdvSimple, maybe_credential_book.value());
ASSERT_EQ(deserialize_result.GetKind(),
nearby_protocol::DeserializeAdvertisementResultKind::V0);
@@ -173,13 +186,16 @@ TEST(NpFfiDeserializeResultTests, TestInvalidV1Cast) {
"");
}
-TEST(NpFfiDeserializeResultTests, V0UseResultTwice) {
- auto book_result = nearby_protocol::CredentialBook::TryCreate();
- ASSERT_TRUE(book_result.ok());
+TEST_F(NpCppTest, V0UseResultTwice) {
+ 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(
- V0AdvEmpty, book_result.value());
+ V0AdvSimple, maybe_credential_book.value());
ASSERT_EQ(deserialize_result.GetKind(),
np_ffi::internal::DeserializeAdvertisementResultKind::V0);
@@ -191,13 +207,16 @@ TEST(NpFfiDeserializeResultTests, V0UseResultTwice) {
"");
}
-TEST(NpFfiDeserializeResultTests, V1UseResultTwice) {
- auto book_result = nearby_protocol::CredentialBook::TryCreate();
- ASSERT_TRUE(book_result.ok());
+TEST_F(NpCppTest, V1UseResultTwice) {
+ 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);
@@ -209,13 +228,16 @@ TEST(NpFfiDeserializeResultTests, V1UseResultTwice) {
"");
}
-TEST(NpFfiDeserializeResultTests, IntoV0AfterOutOfScope) {
- auto book_result = nearby_protocol::CredentialBook::TryCreate();
- ASSERT_TRUE(book_result.ok());
+TEST_F(NpCppTest, IntoV0AfterOutOfScope) {
+ 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(
- V0AdvEmpty, book_result.value());
+ V0AdvSimple, maybe_credential_book.value());
ASSERT_EQ(deserialize_result.GetKind(),
np_ffi::internal::DeserializeAdvertisementResultKind::V0);
@@ -228,13 +250,16 @@ TEST(NpFfiDeserializeResultTests, IntoV0AfterOutOfScope) {
"");
}
-TEST(NpFfiDeserializeResultTests, IntoV1AfterOutOfScope) {
- auto book_result = nearby_protocol::CredentialBook::TryCreate();
- ASSERT_TRUE(book_result.ok());
+TEST_F(NpCppTest, IntoV1AfterOutOfScope) {
+ 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);
@@ -247,13 +272,16 @@ TEST(NpFfiDeserializeResultTests, IntoV1AfterOutOfScope) {
"");
}
-TEST(NpFfiDeserializeV0Tests, V0ResultKindAfterOutOfScope) {
- auto book_result = nearby_protocol::CredentialBook::TryCreate();
- ASSERT_TRUE(book_result.ok());
+TEST_F(NpCppTest, V0ResultKindAfterOutOfScope) {
+ 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(
- V0AdvEmpty, book_result.value());
+ V0AdvSimple, maybe_credential_book.value());
ASSERT_EQ(deserialize_result.GetKind(),
np_ffi::internal::DeserializeAdvertisementResultKind::V0);
@@ -266,13 +294,16 @@ TEST(NpFfiDeserializeV0Tests, V0ResultKindAfterOutOfScope) {
{ [[maybe_unused]] auto failure = deserialize_result.GetKind(); }, "");
}
-TEST(NpFfiDeserializeResultTests, V1ResultKindAfterOutOfScope) {
- auto book_result = nearby_protocol::CredentialBook::TryCreate();
- ASSERT_TRUE(book_result.ok());
+TEST_F(NpCppTest, V1ResultKindAfterOutOfScope) {
+ 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);
@@ -283,4 +314,4 @@ TEST(NpFfiDeserializeResultTests, V1ResultKindAfterOutOfScope) {
// 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/global_config_tests.cc b/nearby/presence/np_cpp_ffi/tests/global_config_tests.cc
deleted file mode 100644
index e120aee..0000000
--- a/nearby/presence/np_cpp_ffi/tests/global_config_tests.cc
+++ /dev/null
@@ -1,205 +0,0 @@
-// Copyright 2023 Google LLC
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-// http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-#include "nearby_protocol.h"
-#include "shared_test_util.h"
-
-#include <iostream>
-
-#include "gtest/gtest.h"
-
-TEST(NpFfiGlobalConfigTests, TestPanicHandler) {
- ASSERT_TRUE(
- nearby_protocol::GlobalConfig::SetPanicHandler(test_panic_handler));
- auto book = nearby_protocol::CredentialBook::TryCreate().value();
- auto deserialize_result =
- nearby_protocol::Deserializer::DeserializeAdvertisement(V0AdvEmpty, book);
- ASSERT_EQ(deserialize_result.GetKind(),
- np_ffi::internal::DeserializeAdvertisementResultKind::V0);
-
- // Now try to cast the result into the wrong type and verify the process
- // aborts
- ASSERT_DEATH({ [[maybe_unused]] auto failure = deserialize_result.IntoV1(); },
- "");
-}
-
-TEST(NpFfiGlobalConfigTests, TestPanicHandlerTwice) {
- ASSERT_TRUE(
- nearby_protocol::GlobalConfig::SetPanicHandler(test_panic_handler));
-
- // Second time trying to set should fail
- ASSERT_FALSE(
- nearby_protocol::GlobalConfig::SetPanicHandler(test_panic_handler));
-}
-
-// There is not much we can actually test here since this will affect memory
-// consumption. This is more of just a simple check that things still work after
-// configuring this
-TEST(NpFfiGlobalConfigTests, TestSetMaxShardsDefault) {
- // 0 should still work as default behavior
- nearby_protocol::GlobalConfig::SetNumShards(0);
-
- auto book = nearby_protocol::CredentialBook::TryCreate().value();
- auto book2 = nearby_protocol::CredentialBook::TryCreate().value();
- auto book3 = nearby_protocol::CredentialBook::TryCreate().value();
- auto deserialize_result =
- nearby_protocol::Deserializer::DeserializeAdvertisement(V0AdvEmpty, book);
-
- // Should still work
- ASSERT_EQ(deserialize_result.GetKind(),
- np_ffi::internal::DeserializeAdvertisementResultKind::V0);
-
- // Call again with a lower number, should have no effect. books 2 and 3 should
- // still work.
- nearby_protocol::GlobalConfig::SetNumShards(1);
- auto deserialize_result_2 =
- nearby_protocol::Deserializer::DeserializeAdvertisement(V0AdvEmpty,
- book2);
- ASSERT_EQ(deserialize_result_2.GetKind(),
- np_ffi::internal::DeserializeAdvertisementResultKind::V0);
- auto deserialize_result_3 =
- nearby_protocol::Deserializer::DeserializeAdvertisement(V0AdvEmpty,
- book3);
- ASSERT_EQ(deserialize_result_3.GetKind(),
- np_ffi::internal::DeserializeAdvertisementResultKind::V0);
-}
-
-TEST(NpFfiGlobalConfigTests, TestSetMaxShardsSmall) {
- nearby_protocol::GlobalConfig::SetNumShards(1);
- auto book = nearby_protocol::CredentialBook::TryCreate().value();
-
- // should still be able to parse 2 payloads with only one shard
- auto deserialize_result1 =
- nearby_protocol::Deserializer::DeserializeAdvertisement(V0AdvEmpty, book);
- ASSERT_EQ(deserialize_result1.GetKind(),
- np_ffi::internal::DeserializeAdvertisementResultKind::V0);
- auto deserialize_result2 =
- nearby_protocol::Deserializer::DeserializeAdvertisement(V0AdvEmpty, book);
- ASSERT_EQ(deserialize_result2.GetKind(),
- np_ffi::internal::DeserializeAdvertisementResultKind::V0);
-}
-
-TEST(NpFfiGlobalConfigTests, TestSetMaxCredBooks) {
- nearby_protocol::GlobalConfig::SetMaxNumCredentialBooks(1);
- auto book1_result = nearby_protocol::CredentialBook::TryCreate();
- ASSERT_TRUE(book1_result.ok());
-
- auto book2_result = nearby_protocol::CredentialBook::TryCreate();
- ASSERT_FALSE(book2_result.ok());
- ASSERT_TRUE(absl::IsResourceExhausted(book2_result.status()));
-}
-
-TEST(NpFfiGlobalConfigTests, TestSetMaxCredBooksAfterFirstCall) {
- auto book = nearby_protocol::CredentialBook::TryCreate().value();
- auto book2 = nearby_protocol::CredentialBook::TryCreate().value();
- auto book3 = nearby_protocol::CredentialBook::TryCreate().value();
-
- // setting this after books have already been created should have no affect
- nearby_protocol::GlobalConfig::SetMaxNumCredentialBooks(1);
- auto book4_result = nearby_protocol::CredentialBook::TryCreate();
- ASSERT_TRUE(book4_result.ok());
-}
-
-TEST(NpFfiGlobalConfigTests, TestSetMaxV0Advs) {
- nearby_protocol::GlobalConfig::SetMaxNumDeserializedV0Advertisements(1);
- auto book_result = nearby_protocol::CredentialBook::TryCreate();
- ASSERT_TRUE(book_result.ok());
-
- {
- auto deserialize_result =
- nearby_protocol::Deserializer::DeserializeAdvertisement(
- V0AdvEmpty, book_result.value());
- ASSERT_EQ(deserialize_result.GetKind(),
- np_ffi::internal::DeserializeAdvertisementResultKind::V0);
-
- // Going over max amount should result in error
- auto deserialize_result2 =
- nearby_protocol::Deserializer::DeserializeAdvertisement(
- V0AdvEmpty, book_result.value());
- ASSERT_EQ(deserialize_result2.GetKind(),
- np_ffi::internal::DeserializeAdvertisementResultKind::Error);
- }
-
- // Now that the first v0 adv is out of scope, it will be de-allocated which
- // will create room for one more to be created.
- auto deserialize_result3 =
- nearby_protocol::Deserializer::DeserializeAdvertisement(
- V0AdvEmpty, book_result.value());
- ASSERT_EQ(deserialize_result3.GetKind(),
- np_ffi::internal::DeserializeAdvertisementResultKind::V0);
-}
-
-TEST(NpFfiGlobalConfigTests, TestSetMaxV1Advs) {
- nearby_protocol::GlobalConfig::SetMaxNumDeserializedV1Advertisements(1);
- auto book_result = nearby_protocol::CredentialBook::TryCreate();
- ASSERT_TRUE(book_result.ok());
-
- {
- auto deserialize_result =
- nearby_protocol::Deserializer::DeserializeAdvertisement(
- V1AdvSimple, book_result.value());
- ASSERT_EQ(deserialize_result.GetKind(),
- np_ffi::internal::DeserializeAdvertisementResultKind::V1);
-
- // Going over max amount should result in error
- auto deserialize_result2 =
- nearby_protocol::Deserializer::DeserializeAdvertisement(
- V1AdvSimple, book_result.value());
- ASSERT_EQ(deserialize_result2.GetKind(),
- np_ffi::internal::DeserializeAdvertisementResultKind::Error);
- }
-
- // Now that the first v1 adv is out of scope, it will be de-allocated which
- // will create room for one more to be created.
- auto deserialize_result3 =
- nearby_protocol::Deserializer::DeserializeAdvertisement(
- V1AdvSimple, book_result.value());
- ASSERT_EQ(deserialize_result3.GetKind(),
- np_ffi::internal::DeserializeAdvertisementResultKind::V1);
-}
-
-// Same test case as above, but verifies that the de-allocation still succeeds
-// after calling IntoV1() and that no double frees occur.
-TEST(NpFfiGlobalConfigTests, TestSetMaxV1AdvsFreeAfterInto) {
- nearby_protocol::GlobalConfig::SetMaxNumDeserializedV1Advertisements(1);
- auto book_result = nearby_protocol::CredentialBook::TryCreate();
- ASSERT_TRUE(book_result.ok());
-
- {
- auto deserialize_result =
- nearby_protocol::Deserializer::DeserializeAdvertisement(
- V1AdvSimple, book_result.value());
- ASSERT_EQ(deserialize_result.GetKind(),
- np_ffi::internal::DeserializeAdvertisementResultKind::V1);
-
- // Going over max amount should result in error
- auto deserialize_result2 =
- nearby_protocol::Deserializer::DeserializeAdvertisement(
- V1AdvSimple, book_result.value());
- ASSERT_EQ(deserialize_result2.GetKind(),
- np_ffi::internal::DeserializeAdvertisementResultKind::Error);
-
- // Calling IntoV1() should move the underlying resources into the v0 object
- // when both go out of scope only one should be freed
- auto v0_adv = deserialize_result.IntoV1();
- }
-
- // Now that the first v1 adv is out of scope, it will be de-allocated which
- // will create room for one more to be created.
- auto deserialize_result3 =
- nearby_protocol::Deserializer::DeserializeAdvertisement(
- V1AdvSimple, book_result.value());
- ASSERT_EQ(deserialize_result3.GetKind(),
- np_ffi::internal::DeserializeAdvertisementResultKind::V1);
-} \ No newline at end of file
diff --git a/nearby/crypto/crypto_provider/src/aead/aes_gcm_siv.rs b/nearby/presence/np_cpp_ffi/tests/np_cpp_test.cc
index 3be7db3..52049e7 100644
--- a/nearby/crypto/crypto_provider/src/aead/aes_gcm_siv.rs
+++ b/nearby/presence/np_cpp_ffi/tests/np_cpp_test.cc
@@ -12,12 +12,6 @@
// See the License for the specific language governing permissions and
// limitations under the License.
-//! Traits for AES-GCM-SIV.
+#include "np_cpp_test.h"
-extern crate alloc;
-use crate::aead::Aead;
-
-/// An implementation of AES-GCM-SIV.
-///
-/// An AesGcmSiv impl may be used for encryption and decryption.
-pub trait AesGcmSiv: Aead<Nonce = [u8; 12]> {}
+bool NpCppTest::panic_handler_set = false; \ No newline at end of file
diff --git a/nearby/presence/np_cpp_ffi/tests/np_cpp_test.h b/nearby/presence/np_cpp_ffi/tests/np_cpp_test.h
new file mode 100644
index 0000000..1787c35
--- /dev/null
+++ b/nearby/presence/np_cpp_ffi/tests/np_cpp_test.h
@@ -0,0 +1,42 @@
+// Copyright 2023 Google LLC
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#ifndef NEARBY_PRESENCE_NP_CPP_FFI_TESTS_NP_CPP_TEST_H_
+#define NEARBY_PRESENCE_NP_CPP_FFI_TESTS_NP_CPP_TEST_H_
+
+#include "nearby_protocol.h"
+#include "shared_test_util.h"
+
+#include "gtest/gtest.h"
+
+class NpCppTest : public testing::Test {
+protected:
+ static void SetUpTestSuite() {
+ if (!panic_handler_set) {
+ ASSERT_TRUE(
+ nearby_protocol::GlobalConfig::SetPanicHandler(test_panic_handler));
+ panic_handler_set = true;
+ nearby_protocol::GlobalConfig::SetMaxNumDeserializedV0Advertisements(2);
+ nearby_protocol::GlobalConfig::SetMaxNumDeserializedV1Advertisements(2);
+ nearby_protocol::GlobalConfig::SetMaxNumCredentialSlabs(3);
+ nearby_protocol::GlobalConfig::SetMaxNumCredentialBooks(2);
+ } else {
+ ASSERT_FALSE(
+ nearby_protocol::GlobalConfig::SetPanicHandler(test_panic_handler));
+ }
+ }
+ static bool panic_handler_set;
+};
+
+#endif // NEARBY_PRESENCE_NP_CPP_FFI_TESTS_NP_CPP_TEST_H_
diff --git a/nearby/presence/np_cpp_ffi/tests/v0_private_identity_tests.cc b/nearby/presence/np_cpp_ffi/tests/v0_private_identity_tests.cc
new file mode 100644
index 0000000..e2a60f2
--- /dev/null
+++ b/nearby/presence/np_cpp_ffi/tests/v0_private_identity_tests.cc
@@ -0,0 +1,211 @@
+// 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 "np_cpp_test.h"
+#include "gtest/gtest.h"
+
+#include <algorithm>
+
+static nearby_protocol::RawAdvertisementPayload
+ V0AdvPrivateIdentity(nearby_protocol::ByteBuffer<255>(
+ {20,
+ {
+ 0x00, // Adv Header
+ 0x21, // private DE w/ a 2 byte payload
+ 0x22, 0x22, // salt
+ 0x85, 0xBF, 0xA8, // encrypted de contents, Tx Power with value 3
+ 0x83, 0x58, 0x7C, 0x50, 0xCF, 0x98, 0x38,
+ 0xA7, 0x8A, 0xC0, 0x1C, 0x96, 0xF9,
+ }}));
+
+static uint8_t encrypted_metadata[] = {
+ 0x26, 0xC5, 0xEA, 0xD4, 0xED, 0x58, 0xF8, 0xFC, 0xE8, 0xF4, 0xAB, 0x0C,
+ 0x93, 0x2B, 0x75, 0xAA, 0x74, 0x39, 0x67, 0xDB, 0x1E, 0xF2, 0x33, 0xB5,
+ 0x43, 0xCC, 0x94, 0xAA, 0xA3, 0xBB, 0xB9, 0x4C, 0xBF, 0x57, 0x77, 0xD0,
+ 0x43, 0x0C, 0x7F, 0xF7, 0x36, 0x03, 0x29, 0xE0, 0x57, 0xBA, 0x97, 0x7F,
+ 0xF2, 0xD1, 0x51, 0xDB, 0xC9, 0x01, 0x47, 0xE7, 0x48, 0x36,
+};
+
+static std::array<uint8_t, 32> legacy_metadata_key_hmac = {
+ 0x88, 0x33, 0xDE, 0xD5, 0x4D, 0x00, 0x92, 0xE8, 0x80, 0x70, 0xD5,
+ 0x1F, 0x18, 0xEC, 0x22, 0x45, 0x75, 0x7C, 0x24, 0xDF, 0xE3, 0x8C,
+ 0xB2, 0xDE, 0x77, 0xB6, 0x78, 0x85, 0xFC, 0xA5, 0x67, 0x4D,
+};
+
+// The canned data in this test was taken from np_adv/tests/examples_v0.rs
+TEST_F(NpCppTest, V0PrivateIdentitySimpleCase) {
+ auto slab_result = nearby_protocol::CredentialSlab::TryCreate();
+ ASSERT_TRUE(slab_result.ok());
+
+ std::span<uint8_t> metadata_span(encrypted_metadata);
+ nearby_protocol::MatchedCredentialData match_data(123, metadata_span);
+
+ std::array<uint8_t, 32> key_seed = {};
+ std::fill_n(key_seed.begin(), 32, 0x11);
+
+ nearby_protocol::V0MatchableCredential v0_cred(
+ key_seed, legacy_metadata_key_hmac, match_data);
+
+ auto add_result = slab_result->AddV0Credential(v0_cred);
+ ASSERT_EQ(add_result, absl::OkStatus());
+
+ auto book_result =
+ nearby_protocol::CredentialBook::TryCreateFromSlab(*slab_result);
+ ASSERT_TRUE(book_result.ok());
+
+ auto deserialize_result =
+ nearby_protocol::Deserializer::DeserializeAdvertisement(
+ V0AdvPrivateIdentity, *book_result);
+ ASSERT_EQ(deserialize_result.GetKind(),
+ nearby_protocol::DeserializeAdvertisementResultKind::V0);
+
+ auto v0_adv = deserialize_result.IntoV0();
+ auto kind = v0_adv.GetKind();
+ ASSERT_EQ(kind, nearby_protocol::DeserializedV0AdvertisementKind::Legible);
+
+ auto legible_adv = v0_adv.IntoLegible();
+ auto identity_kind = legible_adv.GetIdentityKind();
+ ASSERT_EQ(identity_kind,
+ nearby_protocol::DeserializedV0IdentityKind::Decrypted);
+ ASSERT_EQ(legible_adv.GetNumberOfDataElements(), 1);
+
+ auto payload = legible_adv.IntoPayload();
+ auto de = payload.TryGetDataElement(0);
+ ASSERT_TRUE(de.ok());
+
+ auto metadata = payload.DecryptMetadata();
+ ASSERT_TRUE(metadata.ok());
+ ASSERT_EQ(std::string("{\"name\":\"Alice\",\"email\":\"alice@gmail.com\"}"),
+ std::string(metadata->begin(), metadata->end()));
+
+ auto identity_details = payload.GetIdentityDetails();
+ ASSERT_TRUE(identity_details.ok());
+ ASSERT_EQ(identity_details->cred_id, 123);
+ ASSERT_EQ(identity_details->identity_type,
+ nearby_protocol::EncryptedIdentityType::Private);
+
+ auto de_type = de->GetKind();
+ ASSERT_EQ(de_type, nearby_protocol::V0DataElementKind::TxPower);
+
+ auto tx_power_de = de->AsTxPower();
+ ASSERT_EQ(tx_power_de.tx_power, 3);
+}
+
+static nearby_protocol::CredentialBook CreateEmptyCredBook() {
+ auto slab = nearby_protocol::CredentialSlab::TryCreate().value();
+ auto book = nearby_protocol::CredentialBook::TryCreateFromSlab(slab).value();
+ return book;
+}
+
+TEST_F(NpCppTest, V0PrivateIdentityEmptyBook) {
+ auto book = CreateEmptyCredBook();
+ auto deserialize_result =
+ nearby_protocol::Deserializer::DeserializeAdvertisement(
+ V0AdvPrivateIdentity, book);
+ ASSERT_EQ(deserialize_result.GetKind(),
+ nearby_protocol::DeserializeAdvertisementResultKind::V0);
+
+ auto v0_adv = deserialize_result.IntoV0();
+ ASSERT_EQ(
+ v0_adv.GetKind(),
+ nearby_protocol::DeserializedV0AdvertisementKind::NoMatchingCredentials);
+
+ // Should not be able to actually access contents
+ ASSERT_DEATH([[maybe_unused]] auto failure = v0_adv.IntoLegible(), "");
+}
+
+TEST_F(NpCppTest, V0PrivateIdentityNoMatchingCreds) {
+ auto slab_result = nearby_protocol::CredentialSlab::TryCreate();
+ ASSERT_TRUE(slab_result.ok());
+
+ uint8_t metadata[] = {0};
+ std::span<uint8_t> metadata_span(metadata);
+ nearby_protocol::MatchedCredentialData match_data(123, metadata_span);
+
+ // A randomly picked key seed, does NOT match what was used for the canned adv
+ std::array<uint8_t, 32> key_seed = {};
+ std::fill_n(key_seed.begin(), 31, 0x11);
+
+ nearby_protocol::V0MatchableCredential v0_cred(
+ key_seed, legacy_metadata_key_hmac, match_data);
+
+ auto add_result = slab_result->AddV0Credential(v0_cred);
+ ASSERT_EQ(add_result, absl::OkStatus());
+
+ auto book_result =
+ nearby_protocol::CredentialBook::TryCreateFromSlab(*slab_result);
+ ASSERT_TRUE(book_result.ok());
+
+ auto deserialize_result =
+ nearby_protocol::Deserializer::DeserializeAdvertisement(
+ V0AdvPrivateIdentity, *book_result);
+ ASSERT_EQ(deserialize_result.GetKind(),
+ nearby_protocol::DeserializeAdvertisementResultKind::V0);
+
+ auto v0_adv = deserialize_result.IntoV0();
+ ASSERT_EQ(
+ v0_adv.GetKind(),
+ nearby_protocol::DeserializedV0AdvertisementKind::NoMatchingCredentials);
+
+ // Should not be able to actually access contents
+ ASSERT_DEATH([[maybe_unused]] auto failure = v0_adv.IntoLegible(), "");
+}
+
+// Make sure the correct credential is matched out of multiple provided
+TEST_F(NpCppTest, V0PrivateIdentityMultipleCredentials) {
+ auto slab = nearby_protocol::CredentialSlab::TryCreate().value();
+ std::span<uint8_t> metadata_span(encrypted_metadata);
+ std::array<uint8_t, 32> key_seed = {};
+
+ // Non matching credential
+ nearby_protocol::MatchedCredentialData match_data(123, metadata_span);
+ std::fill_n(key_seed.begin(), 32, 0x12);
+ nearby_protocol::V0MatchableCredential v0_cred(
+ key_seed, legacy_metadata_key_hmac, match_data);
+ ASSERT_TRUE(slab.AddV0Credential(v0_cred).ok());
+
+ // Matching credential
+ nearby_protocol::MatchedCredentialData match_data2(456, metadata_span);
+ std::fill_n(key_seed.begin(), 32, 0x11);
+ nearby_protocol::V0MatchableCredential v0_cred2(
+ key_seed, legacy_metadata_key_hmac, match_data2);
+ ASSERT_TRUE(slab.AddV0Credential(v0_cred2).ok());
+
+ // Non matching credential
+ nearby_protocol::MatchedCredentialData match_data3(789, metadata_span);
+ std::fill_n(key_seed.begin(), 32, 0x13);
+ nearby_protocol::V0MatchableCredential v0_cred3(
+ key_seed, legacy_metadata_key_hmac, match_data3);
+ ASSERT_TRUE(slab.AddV0Credential(v0_cred3).ok());
+
+ auto book =
+ nearby_protocol::CredentialBook::TryCreateFromSlab(slab).value();
+ auto legible_adv =
+ nearby_protocol::Deserializer::DeserializeAdvertisement(
+ V0AdvPrivateIdentity, book).IntoV0().IntoLegible();
+ ASSERT_EQ(legible_adv.GetIdentityKind(),
+ nearby_protocol::DeserializedV0IdentityKind::Decrypted);
+ ASSERT_EQ(legible_adv.GetNumberOfDataElements(), 1);
+
+ auto payload = legible_adv.IntoPayload();
+ ASSERT_TRUE(payload.TryGetDataElement(0).ok());
+
+ // Make sure the correct credential matches
+ auto identity_details = payload.GetIdentityDetails();
+ ASSERT_TRUE(identity_details.ok());
+ ASSERT_EQ(identity_details->cred_id, 456);
+ ASSERT_EQ(identity_details->identity_type,
+ nearby_protocol::EncryptedIdentityType::Private);
+}
diff --git a/nearby/presence/np_cpp_ffi/tests/deserialize_v0_tests.cc b/nearby/presence/np_cpp_ffi/tests/v0_public_identity_tests.cc
index 9781720..3ed2d79 100644
--- a/nearby/presence/np_cpp_ffi/tests/deserialize_v0_tests.cc
+++ b/nearby/presence/np_cpp_ffi/tests/v0_public_identity_tests.cc
@@ -14,10 +14,24 @@
#include "nearby_protocol.h"
#include "shared_test_util.h"
-
+#include "np_cpp_test.h"
#include "gtest/gtest.h"
-TEST(NpFfiDeserializeV0Tests, V0SingleDataElementTxPower) {
+TEST_F(NpCppTest, InvalidCast) {
+ auto 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(),
+ np_ffi::internal::DeserializeAdvertisementResultKind::V0);
+
+ // Now try to cast the result into the wrong type and verify the process
+ // aborts
+ ASSERT_DEATH({ [[maybe_unused]] auto failure = deserialize_result.IntoV1(); },
+ "");
+}
+
+TEST_F(NpCppTest, V0SingleDataElementTxPower) {
nearby_protocol::RawAdvertisementPayload adv(
nearby_protocol::ByteBuffer<255>({
4,
@@ -26,7 +40,8 @@ TEST(NpFfiDeserializeV0Tests, V0SingleDataElementTxPower) {
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(
@@ -39,8 +54,8 @@ TEST(NpFfiDeserializeV0Tests, V0SingleDataElementTxPower) {
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();
@@ -56,7 +71,7 @@ TEST(NpFfiDeserializeV0Tests, V0SingleDataElementTxPower) {
ASSERT_EQ(tx_power.tx_power, 3);
}
-TEST(NpFfiDeserializeV0Tests, V0LengthOneActionsDataElement) {
+TEST_F(NpCppTest, V0LengthOneActionsDataElement) {
nearby_protocol::RawAdvertisementPayload adv(
nearby_protocol::ByteBuffer<255>({
4,
@@ -65,7 +80,9 @@ TEST(NpFfiDeserializeV0Tests, V0LengthOneActionsDataElement) {
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(
@@ -78,8 +95,8 @@ TEST(NpFfiDeserializeV0Tests, V0LengthOneActionsDataElement) {
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();
@@ -92,10 +109,10 @@ TEST(NpFfiDeserializeV0Tests, V0LengthOneActionsDataElement) {
ASSERT_EQ(de.GetKind(), nearby_protocol::V0DataElementKind::Actions);
auto actions = de.AsActions();
- ASSERT_EQ(actions.GetAsU32(), 0);
+ ASSERT_EQ(actions.GetAsU32(), (uint32_t)0);
}
-TEST(NpFfiDeserializeV0Tests, V0LengthTwoActionsDataElement) {
+TEST_F(NpCppTest, V0LengthTwoActionsDataElement) {
nearby_protocol::RawAdvertisementPayload adv(
nearby_protocol::ByteBuffer<255>({
5,
@@ -104,7 +121,9 @@ TEST(NpFfiDeserializeV0Tests, V0LengthTwoActionsDataElement) {
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(
@@ -117,8 +136,8 @@ TEST(NpFfiDeserializeV0Tests, V0LengthTwoActionsDataElement) {
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();
@@ -150,7 +169,7 @@ TEST(NpFfiDeserializeV0Tests, V0LengthTwoActionsDataElement) {
ASSERT_EQ(actions.GetContextSyncSequenceNumber(), 0xD);
}
-TEST(NpFfiDeserializeV0Tests, V0MultipleDataElements) {
+TEST_F(NpCppTest, V0MultipleDataElements) {
nearby_protocol::RawAdvertisementPayload adv(nearby_protocol::ByteBuffer<255>(
{7,
{
@@ -160,7 +179,9 @@ TEST(NpFfiDeserializeV0Tests, V0MultipleDataElements) {
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(
@@ -173,8 +194,8 @@ TEST(NpFfiDeserializeV0Tests, V0MultipleDataElements) {
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();
@@ -195,41 +216,28 @@ TEST(NpFfiDeserializeV0Tests, V0MultipleDataElements) {
ASSERT_EQ(second_de.GetKind(), nearby_protocol::V0DataElementKind::Actions);
auto actions = second_de.AsActions();
- ASSERT_EQ(actions.GetAsU32(), 0x00460000);
- ASSERT_EQ(actions.GetContextSyncSequenceNumber(), 0);
+ ASSERT_EQ(actions.GetAsU32(), (uint32_t)0x00460000);
+ ASSERT_EQ(actions.GetContextSyncSequenceNumber(), (uint8_t)0);
}
-TEST(NpFfiDeserializeV0Tests, V0EmptyPayload) {
- auto maybe_credential_book = nearby_protocol::CredentialBook::TryCreate();
+TEST_F(NpCppTest, V0EmptyPayload) {
+ 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(
V0AdvEmpty, maybe_credential_book.value());
ASSERT_EQ(deserialize_result.GetKind(),
- nearby_protocol::DeserializeAdvertisementResultKind::V0);
- auto v0_adv = deserialize_result.IntoV0();
-
- ASSERT_EQ(v0_adv.GetKind(),
- nearby_protocol::DeserializedV0AdvertisementKind::Legible);
- auto legible_adv = v0_adv.IntoLegible();
- auto identity = legible_adv.GetIdentity();
- ASSERT_EQ(identity.GetKind(),
- nearby_protocol::DeserializedV0IdentityKind::Plaintext);
-
- auto num_des = legible_adv.GetNumberOfDataElements();
- ASSERT_EQ(num_des, 0);
- auto payload = legible_adv.IntoPayload();
-
- auto result = payload.TryGetDataElement(0);
- ASSERT_FALSE(result.ok());
- ASSERT_TRUE(absl::IsOutOfRange(result.status()));
+ nearby_protocol::DeserializeAdvertisementResultKind::Error);
}
-TEST(NpFfiDeserializeV0Tests, TestV0AdvMoveConstructor) {
- auto book = nearby_protocol::CredentialBook::TryCreate().value();
- auto result =
- nearby_protocol::Deserializer::DeserializeAdvertisement(V0AdvEmpty, book);
+TEST_F(NpCppTest, TestV0AdvMoveConstructor) {
+ 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(),
np_ffi::internal::DeserializeAdvertisementResultKind::V0);
auto adv = result.IntoV0();
@@ -253,17 +261,18 @@ TEST(NpFfiDeserializeV0Tests, TestV0AdvMoveConstructor) {
ASSERT_DEATH([[maybe_unused]] auto failure = moved_again.GetKind(), "");
}
-TEST(NpFfiDeserializeResultTests, TestV0AdvMoveAssignment) {
- auto book = nearby_protocol::CredentialBook::TryCreate().value();
- auto result =
- nearby_protocol::Deserializer::DeserializeAdvertisement(V0AdvEmpty, book);
+TEST_F(NpCppTest, TestV0AdvMoveAssignment) {
+ 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(),
np_ffi::internal::DeserializeAdvertisementResultKind::V0);
auto adv = result.IntoV0();
// create a second result
- auto another_result =
- nearby_protocol::Deserializer::DeserializeAdvertisement(V0AdvEmpty, book);
+ auto another_result = nearby_protocol::Deserializer::DeserializeAdvertisement(
+ V0AdvSimple, book);
ASSERT_EQ(another_result.GetKind(),
np_ffi::internal::DeserializeAdvertisementResultKind::V0);
auto adv2 = another_result.IntoV0();
@@ -286,40 +295,53 @@ TEST(NpFfiDeserializeResultTests, TestV0AdvMoveAssignment) {
ASSERT_DEATH([[maybe_unused]] auto failure = moved_again.GetKind(), "");
}
-TEST(NpFfiDeserializeV0Tests, V0AdvDestructor) {
- nearby_protocol::GlobalConfig::SetMaxNumDeserializedV0Advertisements(1);
- auto book_result = nearby_protocol::CredentialBook::TryCreate();
+static nearby_protocol::DeserializeAdvertisementResult
+CreateAdv(nearby_protocol::CredentialBook &book) {
+ auto adv = nearby_protocol::Deserializer::DeserializeAdvertisement(
+ V0AdvSimple, book);
+ return adv;
+}
+
+TEST_F(NpCppTest, V0AdvDestructor) {
+ 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 =
- nearby_protocol::Deserializer::DeserializeAdvertisement(
- V0AdvEmpty, book_result.value());
+ auto deserialize_result = CreateAdv(book_result.value());
+ auto deserialize_result2 = CreateAdv(book_result.value());
+ // Deserialize 2 advertisements, which will take up 2 slots in the handle
+ // map
ASSERT_EQ(deserialize_result.GetKind(),
np_ffi::internal::DeserializeAdvertisementResultKind::V0);
+ ASSERT_EQ(deserialize_result2.GetKind(),
+ np_ffi::internal::DeserializeAdvertisementResultKind::V0);
// Going over max amount should result in error
- auto deserialize_result2 =
+ auto deserialize_result3 =
nearby_protocol::Deserializer::DeserializeAdvertisement(
- V0AdvEmpty, book_result.value());
- ASSERT_EQ(deserialize_result2.GetKind(),
+ V0AdvSimple, book_result.value());
+ ASSERT_EQ(deserialize_result3.GetKind(),
np_ffi::internal::DeserializeAdvertisementResultKind::Error);
- // Calling IntoV0() should move the underlying resources into the v0 object
- // when both go out of scope only one should be freed
+ // Calling IntoV0() should move the underlying resources into the v0
+ // object when both go out of scope only one should be freed
auto v0_adv = deserialize_result.IntoV0();
}
// Now that the first v0 adv is out of scope, it should be de-allocated which
// will create room for one more to be created.
- auto deserialize_result3 =
+ auto deserialize_result =
nearby_protocol::Deserializer::DeserializeAdvertisement(
- V0AdvEmpty, book_result.value());
- ASSERT_EQ(deserialize_result3.GetKind(),
+ V0AdvSimple, book_result.value());
+ ASSERT_EQ(deserialize_result.GetKind(),
np_ffi::internal::DeserializeAdvertisementResultKind::V0);
}
-TEST(NpFfiDeserializeV0Tests, V0AdvUseAfterMove) {
- auto maybe_credential_book = nearby_protocol::CredentialBook::TryCreate();
+TEST_F(NpCppTest, V0AdvUseAfterMove) {
+ 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(
@@ -338,24 +360,25 @@ TEST(NpFfiDeserializeV0Tests, V0AdvUseAfterMove) {
ASSERT_DEATH([[maybe_unused]] auto failure = v0_adv.IntoLegible(), "");
}
-TEST(NpFfiDeserializeV0Tests, TestLegibleAdvMoveConstructor) {
- auto book = nearby_protocol::CredentialBook::TryCreate().value();
- auto result =
- nearby_protocol::Deserializer::DeserializeAdvertisement(V0AdvEmpty, book);
+TEST_F(NpCppTest, TestLegibleAdvMoveConstructor) {
+ 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(),
np_ffi::internal::DeserializeAdvertisementResultKind::V0);
auto legible = result.IntoV0().IntoLegible();
// 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(), 0);
- ASSERT_EQ(moved.GetIdentity().GetKind(),
+ ASSERT_EQ(moved.GetNumberOfDataElements(), 1);
+ 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(), "");
@@ -365,37 +388,38 @@ TEST(NpFfiDeserializeV0Tests, TestLegibleAdvMoveConstructor) {
// 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(),
"");
ASSERT_DEATH([[maybe_unused]] auto failure = moved_again.IntoPayload(), "");
}
-TEST(NpFfiDeserializeResultTests, TestLegibleAdvMoveAssignment) {
- auto book = nearby_protocol::CredentialBook::TryCreate().value();
- auto result =
- nearby_protocol::Deserializer::DeserializeAdvertisement(V0AdvEmpty, book);
+TEST_F(NpCppTest, TestLegibleAdvMoveAssignment) {
+ 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(),
np_ffi::internal::DeserializeAdvertisementResultKind::V0);
auto legible = result.IntoV0().IntoLegible();
// create a second result
- auto another_result =
- nearby_protocol::Deserializer::DeserializeAdvertisement(V0AdvEmpty, book);
+ auto another_result = nearby_protocol::Deserializer::DeserializeAdvertisement(
+ V0AdvSimple, book);
ASSERT_EQ(another_result.GetKind(),
np_ffi::internal::DeserializeAdvertisementResultKind::V0);
auto legible2 = another_result.IntoV0().IntoLegible();
// 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(), "");
@@ -404,7 +428,7 @@ TEST(NpFfiDeserializeResultTests, TestLegibleAdvMoveAssignment) {
// 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(),
"");
@@ -418,36 +442,41 @@ CreateLegibleAdv(nearby_protocol::CredentialBook &book) {
return v0_adv.IntoLegible();
}
-TEST(NpFfiDeserializeV0Tests, V0LegibleAdvUseAfterMove) {
- auto book = nearby_protocol::CredentialBook::TryCreate().value();
+TEST_F(NpCppTest, V0LegibleAdvUseAfterMove) {
+ 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(),
"");
ASSERT_DEATH([[maybe_unused]] auto failure = legible_adv.IntoPayload(), "");
}
-TEST(NpFfiDeserializeV0Tests, LegibleAdvDestructor) {
- auto book = nearby_protocol::CredentialBook::TryCreate().value();
- nearby_protocol::GlobalConfig::SetMaxNumDeserializedV0Advertisements(1);
+TEST_F(NpCppTest, LegibleAdvDestructor) {
+ 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 adv is valid.
- ASSERT_EQ(legible_adv.GetIdentity().GetKind(),
+ // check that legible advs are valid.
+ ASSERT_EQ(legible_adv.GetIdentityKind(),
nearby_protocol::DeserializedV0IdentityKind::Plaintext);
ASSERT_EQ(legible_adv.GetNumberOfDataElements(), 1);
+ ASSERT_EQ(legible_adv2.GetIdentityKind(),
+ nearby_protocol::DeserializedV0IdentityKind::Plaintext);
+ ASSERT_EQ(legible_adv2.GetNumberOfDataElements(), 1);
// allocation slots should be full
ASSERT_EQ(nearby_protocol::Deserializer::DeserializeAdvertisement(
@@ -469,14 +498,16 @@ CreatePayload(nearby_protocol::CredentialBook &book) {
return legible_adv.IntoPayload();
}
-TEST(NpFfiDeserializeV0Tests, V0PayloadDestructor) {
- auto book = nearby_protocol::CredentialBook::TryCreate().value();
- nearby_protocol::GlobalConfig::SetMaxNumDeserializedV0Advertisements(1);
+TEST_F(NpCppTest, V0PayloadDestructor) {
+ auto slab = nearby_protocol::CredentialSlab::TryCreate().value();
+ auto book = nearby_protocol::CredentialBook::TryCreateFromSlab(slab).value();
{
auto payload = CreatePayload(book);
+ auto payload2 = CreatePayload(book);
// check that payload adv is valid even though its parent is out of scope
ASSERT_TRUE(payload.TryGetDataElement(0).ok());
+ ASSERT_TRUE(payload2.TryGetDataElement(0).ok());
// allocation slots should be full
ASSERT_EQ(nearby_protocol::Deserializer::DeserializeAdvertisement(
@@ -493,8 +524,9 @@ TEST(NpFfiDeserializeV0Tests, V0PayloadDestructor) {
nearby_protocol::DeserializeAdvertisementResultKind::V0);
}
-TEST(NpFfiDeserializeV0Tests, TestV0PayloadMoveConstructor) {
- auto book = nearby_protocol::CredentialBook::TryCreate().value();
+TEST_F(NpCppTest, TestV0PayloadMoveConstructor) {
+ 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(),
@@ -519,8 +551,9 @@ TEST(NpFfiDeserializeV0Tests, TestV0PayloadMoveConstructor) {
"");
}
-TEST(NpFfiDeserializeResultTests, TestV0PayloadMoveAssignment) {
- auto book = nearby_protocol::CredentialBook::TryCreate().value();
+TEST_F(NpCppTest, TestV0PayloadMoveAssignment) {
+ 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(),
@@ -550,11 +583,10 @@ TEST(NpFfiDeserializeResultTests, TestV0PayloadMoveAssignment) {
"");
}
-TEST(NpFfiDeserializeV0Tests, InvalidDataElementCast) {
- ASSERT_TRUE(
- nearby_protocol::GlobalConfig::SetPanicHandler(test_panic_handler));
-
- auto maybe_credential_book = nearby_protocol::CredentialBook::TryCreate();
+TEST_F(NpCppTest, InvalidDataElementCast) {
+ 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(
@@ -566,8 +598,8 @@ TEST(NpFfiDeserializeV0Tests, InvalidDataElementCast) {
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();
@@ -582,8 +614,10 @@ TEST(NpFfiDeserializeV0Tests, InvalidDataElementCast) {
ASSERT_DEATH([[maybe_unused]] auto failure = de.AsActions();, "");
}
-TEST(NpFfiDeserializeV0Tests, InvalidDataElementIndex) {
- auto maybe_credential_book = nearby_protocol::CredentialBook::TryCreate();
+TEST_F(NpCppTest, InvalidDataElementIndex) {
+ 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(
@@ -596,8 +630,8 @@ TEST(NpFfiDeserializeV0Tests, InvalidDataElementIndex) {
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/v1_private_identity_tests.cc b/nearby/presence/np_cpp_ffi/tests/v1_private_identity_tests.cc
new file mode 100644
index 0000000..8cd0bd0
--- /dev/null
+++ b/nearby/presence/np_cpp_ffi/tests/v1_private_identity_tests.cc
@@ -0,0 +1,130 @@
+// 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 "np_cpp_test.h"
+#include "gtest/gtest.h"
+#include <algorithm>
+
+// All canned data output from np_adv/tests/examples_v1.rs
+static nearby_protocol::RawAdvertisementPayload
+ V1AdvPrivateIdentity(nearby_protocol::ByteBuffer<255>(
+ {105,
+ {
+ 0x20, 0x67, 0x91, 0x10, 0x08, 0xAD, 0x69, 0x46, 0x04, 0x5D, 0xAE,
+ 0x6D, 0xB7, 0xF7, 0x5C, 0xD3, 0xB8, 0xAC, 0xF0, 0xBF, 0x75, 0x90,
+ 0x01, 0xBE, 0x73, 0x33, 0xA4, 0x76, 0x84, 0x4A, 0x09, 0x0F, 0x2B,
+ 0x99, 0x47, 0xDF, 0x8B, 0x46, 0xCA, 0x16, 0xCE, 0x13, 0xB5, 0x6E,
+ 0x53, 0xAE, 0x28, 0x56, 0x44, 0x0E, 0xA6, 0x8D, 0xEB, 0xA1, 0x11,
+ 0xAF, 0x4E, 0x1B, 0xE0, 0x8E, 0xF5, 0xBA, 0x90, 0x4F, 0x2E, 0x94,
+ 0xFC, 0xDE, 0xA6, 0x7F, 0x5D, 0xC8, 0x37, 0xB7, 0xEF, 0xCA, 0xAC,
+ 0x8B, 0x9F, 0x1B, 0xD4, 0xC6, 0x11, 0x85, 0xD3, 0x67, 0x39, 0x32,
+ 0xD1, 0x82, 0xCA, 0x4E, 0xB9, 0x46, 0x03, 0x83, 0x68, 0x56, 0x0B,
+ 0xCC, 0xFD, 0x7A, 0x2A, 0xBE, 0x07,
+ }}));
+
+static std::array<uint8_t, 32> key_seed = {
+ 0x31, 0x43, 0x63, 0x1E, 0xCA, 0xE8, 0x97, 0x4B, 0x96, 0x50, 0xCC,
+ 0x1C, 0x48, 0x25, 0x0E, 0x81, 0x58, 0x06, 0x81, 0x51, 0xF9, 0xEB,
+ 0x25, 0x23, 0x03, 0xD4, 0x97, 0x6D, 0x95, 0x19, 0x91, 0x39,
+};
+
+static std::array<uint8_t, 32> expected_unsigned_metadata_key_hmac = {0};
+
+static std::array<uint8_t, 32> expected_signed_metadata_key_hmac = {
+ 0x1C, 0xBC, 0xEB, 0xDC, 0x17, 0xB5, 0x91, 0xE5, 0x07, 0x9D, 0x70,
+ 0xC1, 0xE8, 0x4B, 0xCC, 0xDB, 0x4B, 0x0F, 0x76, 0x83, 0x59, 0x62,
+ 0x0A, 0x2D, 0x55, 0x0B, 0x3B, 0x36, 0xA4, 0x92, 0x8B, 0x13,
+};
+
+static std::array<uint8_t, 32> public_key = {
+ 0x6D, 0x0D, 0xB6, 0x09, 0x10, 0xB1, 0x4E, 0xC4, 0x7E, 0x10, 0x16,
+ 0x14, 0x9C, 0x9F, 0xF2, 0x14, 0x0F, 0xEC, 0x53, 0x76, 0xE3, 0x07,
+ 0xD9, 0xD3, 0x9E, 0xAE, 0xE7, 0x45, 0x2C, 0x03, 0xEC, 0x6D,
+};
+
+static uint8_t encrypted_metadata[] = {
+ 0x09, 0xB8, 0xC6, 0x6B, 0x71, 0x43, 0x55, 0x4C, 0xB9, 0x9D, 0xBF,
+ 0xE4, 0xAF, 0x3E, 0xA2, 0x56, 0x0E, 0x6C, 0xBC, 0xDC, 0x3F, 0x3F,
+ 0x0D, 0x28, 0xD4, 0x50, 0xA9, 0xEA, 0xC3, 0x60, 0xB0, 0x81, 0x31,
+ 0xE2, 0x67, 0xB5, 0xC8, 0x15, 0x0C, 0xCA, 0x0B, 0x9B, 0x2C, 0x80,
+ 0xC1, 0xB1, 0xF6, 0x5F, 0xE1, 0x51, 0xF9, 0xE2, 0x23, 0x56, 0xD4,
+ 0x0B, 0x89, 0xA7, 0xF3, 0x4D, 0xE8, 0x79, 0x26, 0x44, 0x7E, 0x62,
+ 0xDE, 0x53, 0x13, 0x15, 0x3D, 0xFC, 0x04, 0x2E, 0x2D, 0x08, 0x43,
+ 0x2E, 0xE1, 0x96, 0xE8, 0x0F, 0xD0, 0xFC, 0xDE, 0x03, 0x86, 0x23,
+ 0xB6, 0x98, 0x85, 0x27, 0x67, 0xD8, 0x1D, 0xC3, 0xE2, 0xE0, 0xA4,
+ 0x32, 0x1A, 0x5F, 0x51, 0x0B, 0xA8, 0xD8, 0xA7, 0x23, 0xA4, 0x57,
+};
+
+TEST_F(NpCppTest, V1PrivateIdentitySimpleCase) {
+ auto slab_result = nearby_protocol::CredentialSlab::TryCreate();
+ ASSERT_TRUE(slab_result.ok());
+
+ std::span<uint8_t> metadata_span(encrypted_metadata);
+ nearby_protocol::MatchedCredentialData match_data(123, metadata_span);
+
+ nearby_protocol::V1MatchableCredential v1_cred(
+ key_seed, expected_unsigned_metadata_key_hmac,
+ expected_signed_metadata_key_hmac, public_key, match_data);
+
+ auto add_result = slab_result->AddV1Credential(v1_cred);
+ ASSERT_EQ(add_result, absl::OkStatus());
+
+ auto book_result =
+ nearby_protocol::CredentialBook::TryCreateFromSlab(*slab_result);
+ ASSERT_TRUE(book_result.ok());
+
+ auto deserialize_result =
+ nearby_protocol::Deserializer::DeserializeAdvertisement(
+ V1AdvPrivateIdentity, *book_result);
+ ASSERT_EQ(deserialize_result.GetKind(),
+ nearby_protocol::DeserializeAdvertisementResultKind::V1);
+
+ auto v1_adv = deserialize_result.IntoV1();
+ ASSERT_EQ(v1_adv.GetNumUndecryptableSections(), 0);
+ ASSERT_EQ(v1_adv.GetNumLegibleSections(), 1);
+
+ auto section = v1_adv.TryGetSection(0);
+ ASSERT_TRUE(section.ok());
+ ASSERT_EQ(section->GetIdentityKind(),
+ nearby_protocol::DeserializedV1IdentityKind::Decrypted);
+ ASSERT_EQ(section->NumberOfDataElements(), 1);
+
+ auto metadata = section->DecryptMetadata();
+ ASSERT_TRUE(metadata.ok());
+ ASSERT_EQ(
+ std::string("{\"uuid\":\"378845e1-2616-420d-86f5-674177a7504d\","
+ "\"display_name\":\"Alice\",\"location\":\"Wonderland\"}"),
+ std::string(metadata->begin(), metadata->end()));
+
+ auto identity_details = section->GetIdentityDetails();
+ ASSERT_TRUE(identity_details.ok());
+ ASSERT_EQ(identity_details->cred_id, 123);
+ ASSERT_EQ(identity_details->verification_mode,
+ nearby_protocol::V1VerificationMode::Signature);
+ ASSERT_EQ(identity_details->identity_type,
+ nearby_protocol::EncryptedIdentityType::Private);
+
+ auto de = section->TryGetDataElement(0);
+ ASSERT_TRUE(de.ok());
+ ASSERT_EQ(de->GetDataElementTypeCode(), 5);
+ ASSERT_EQ(de->GetPayload().ToVector(), std::vector<uint8_t>{7});
+
+ auto offset = de->GetOffset();
+ auto derived_salt = section->DeriveSaltForOffset(offset);
+ ASSERT_TRUE(derived_salt.ok());
+ std::array<uint8_t, 16> expected =
+ {94, 154, 245, 152, 164, 22, 131, 157, 8, 79, 28, 77, 236, 57, 17, 97};
+ ASSERT_EQ(*derived_salt, expected);
+} \ No newline at end of file
diff --git a/nearby/presence/np_cpp_ffi/tests/deserialize_v1_tests.cc b/nearby/presence/np_cpp_ffi/tests/v1_public_identity_tests.cc
index 115ad14..aa32f7b 100644
--- a/nearby/presence/np_cpp_ffi/tests/deserialize_v1_tests.cc
+++ b/nearby/presence/np_cpp_ffi/tests/v1_public_identity_tests.cc
@@ -14,11 +14,14 @@
#include "nearby_protocol.h"
#include "shared_test_util.h"
+#include "np_cpp_test.h"
#include "gtest/gtest.h"
-TEST(NpFfiDeserializeV1Tests, V1SimpleTestCase) {
- auto maybe_credential_book = nearby_protocol::CredentialBook::TryCreate();
+TEST_F(NpCppTest, V1SimpleTestCase) {
+ 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 =
@@ -47,7 +50,7 @@ TEST(NpFfiDeserializeV1Tests, V1SimpleTestCase) {
auto de = section.value().TryGetDataElement(0);
ASSERT_TRUE(de.ok());
- ASSERT_EQ(de.value().GetDataElementTypeCode(), 5);
+ ASSERT_EQ(de.value().GetDataElementTypeCode(), (uint8_t)5);
auto payload = de.value().GetPayload();
auto vec = payload.ToVector();
@@ -55,8 +58,9 @@ TEST(NpFfiDeserializeV1Tests, V1SimpleTestCase) {
ASSERT_EQ(vec, expected);
}
-TEST(NpFfiDeserializeV1Tests, TestV1AdvMoveConstructor) {
- auto book = nearby_protocol::CredentialBook::TryCreate().value();
+TEST_F(NpCppTest, TestV1AdvMoveConstructor) {
+ 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(),
@@ -91,8 +95,9 @@ TEST(NpFfiDeserializeV1Tests, TestV1AdvMoveConstructor) {
"");
}
-TEST(NpFfiDeserializeV1Tests, TestV1AdvMoveAssignment) {
- auto book = nearby_protocol::CredentialBook::TryCreate().value();
+TEST_F(NpCppTest, TestV1AdvMoveAssignment) {
+ 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(),
@@ -151,10 +156,10 @@ bool TryDeserializeNewV1Adv(nearby_protocol::CredentialBook &book) {
np_ffi::internal::DeserializeAdvertisementResultKind::V1;
}
-TEST(NpFfiDeserializeV1Tests, TestSectionOwnership) {
- // Capping the max number so we can verify that de-allocations are happening
- nearby_protocol::GlobalConfig::SetMaxNumDeserializedV1Advertisements(1);
- auto maybe_credential_book = nearby_protocol::CredentialBook::TryCreate();
+TEST_F(NpCppTest, TestSectionOwnership) {
+ 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());
{
@@ -164,6 +169,12 @@ TEST(NpFfiDeserializeV1Tests, TestSectionOwnership) {
ASSERT_EQ(section.NumberOfDataElements(), 1);
ASSERT_TRUE(section.TryGetDataElement(0).ok());
+ auto section2 = GetSection(maybe_credential_book.value());
+ ASSERT_EQ(section2.GetIdentityKind(),
+ nearby_protocol::DeserializedV1IdentityKind::Plaintext);
+ ASSERT_EQ(section2.NumberOfDataElements(), 1);
+ ASSERT_TRUE(section2.TryGetDataElement(0).ok());
+
ASSERT_FALSE(TryDeserializeNewV1Adv(maybe_credential_book.value()));
}
@@ -175,7 +186,7 @@ TEST(NpFfiDeserializeV1Tests, TestSectionOwnership) {
/*
* Multiple sections are not supported in plaintext advertisements
* TODO Update the below test to use encrypted sections
-TEST(NpFfiDeserializeV1Tests, V1MultipleSections) {
+TEST(NpCppTest, V1MultipleSections) {
auto maybe_credential_book = nearby_protocol::CredentialBook::TryCreate();
ASSERT_TRUE(maybe_credential_book.ok());
@@ -218,4 +229,4 @@ TEST(NpFfiDeserializeV1Tests, V1MultipleSections) {
nearby_protocol::DeserializedV1IdentityKind::Plaintext);
ASSERT_EQ(section2.value().NumberOfDataElements(), 1);
}
-*/ \ No newline at end of file
+*/
diff --git a/nearby/presence/np_ed25519/Cargo.toml b/nearby/presence/np_ed25519/Cargo.toml
index b4bca66..bd42c65 100644
--- a/nearby/presence/np_ed25519/Cargo.toml
+++ b/nearby/presence/np_ed25519/Cargo.toml
@@ -4,9 +4,12 @@ version.workspace = true
edition.workspace = true
publish.workspace = true
+[lints]
+workspace = true
+
[dependencies]
array_view.workspace = true
-crypto_provider.workspace = true
+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 60c8302..fe2ab5a 100644
--- a/nearby/presence/np_ed25519/src/lib.rs
+++ b/nearby/presence/np_ed25519/src/lib.rs
@@ -19,15 +19,11 @@
//! 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::{
- Ed25519Provider, KeyPair as _, PublicKey as _, RawPrivateKey, RawPublicKey, RawSignature,
- Signature as _, SignatureError,
+ Ed25519Provider, KeyPair as _, PrivateKey, PublicKey as _, RawPrivateKey, RawPrivateKeyPermit,
+ RawPublicKey, RawSignature, Signature as _, SignatureError,
};
use crypto_provider::CryptoProvider;
use sink::{Sink, SinkWriter};
@@ -55,15 +51,30 @@ pub const MAX_SIGNATURE_BUFFER_LEN: usize = 512;
pub struct KeyPair<C: CryptoProvider>(CpKeyPair<C>);
impl<C: CryptoProvider> KeyPair<C> {
- /// Returns the `KeyPair`'s private key bytes. This method should only ever be called by code
- /// which securely stores private credentials.
- pub fn private_key(&self) -> RawPrivateKey {
+ /// Returns the `KeyPair`'s private key bytes.
+ /// This method is only usable in situations where
+ /// the caller has permission to handle the raw bytes
+ /// of a private key.
+ pub fn raw_private_key(&self, permit: &RawPrivateKeyPermit) -> RawPrivateKey {
+ self.0.raw_private_key(permit)
+ }
+
+ /// Builds this key-pair from an array of its private key bytes in the format
+ /// yielded by `private_key`.
+ /// This method is only usable in situations where
+ /// the caller has permission to handle the raw bytes
+ /// of a private key.
+ pub fn from_raw_private_key(private_key: &RawPrivateKey, permit: &RawPrivateKeyPermit) -> Self {
+ Self(CpKeyPair::<C>::from_raw_private_key(private_key, permit))
+ }
+
+ /// Returns the private key of this key-pair.
+ pub fn private_key(&self) -> PrivateKey {
self.0.private_key()
}
- /// Builds this key-pair from an array of its private key bytes in the yielded by `private_key`.
- /// This method should only ever be called by code which securely stores private credentials.
- pub fn from_private_key(private_key: &RawPrivateKey) -> Self {
+ /// Builds this key-pair from a private key.
+ pub fn from_private_key(private_key: &PrivateKey) -> Self {
Self(CpKeyPair::<C>::from_private_key(private_key))
}
@@ -127,6 +138,11 @@ pub struct PublicKey<C: CryptoProvider> {
}
impl<C: CryptoProvider> PublicKey<C> {
+ /// Constructs a public key for NP adv signature verification
+ /// from a public key under the given crypto-provider
+ pub fn new(public_key: CpPublicKey<C>) -> Self {
+ Self { public_key }
+ }
/// Succeeds if the signature was a valid signature created via the corresponding
/// keypair to this public key using the given [`SignatureContext`] on the given
/// message payload. The message payload is represented
@@ -171,7 +187,8 @@ impl<C: CryptoProvider> PublicKey<C> {
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")
}
}
@@ -230,6 +247,7 @@ impl SignatureContext {
/// [`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 5be03a6..6c9a6f4 100644
--- a/nearby/presence/np_ffi_core/Cargo.toml
+++ b/nearby/presence/np_ffi_core/Cargo.toml
@@ -4,14 +4,23 @@ version.workspace = true
edition.workspace = true
publish.workspace = true
+[lints]
+workspace = true
+
[dependencies]
array_view.workspace = true
-np_adv.workspace = true
+ldt_np_adv.workspace = true
+np_adv = { workspace = true, features = ["alloc"] }
+np_adv_dynamic.workspace = true
+np_hkdf.workspace = true
handle_map.workspace = true
crypto_provider.workspace = true
-#TODO: Allow this to be configurable!
-crypto_provider_default = {workspace = true, features = ["rustcrypto"]}
-spin.workspace = true
+crypto_provider_default = { workspace = true, default-features = false }
+lock_adapter.workspace = true
+lazy_static.workspace = true
[features]
-std = []
+default = ["rustcrypto"]
+rustcrypto = ["crypto_provider_default/rustcrypto", "crypto_provider_default/std"]
+opensslbssl = ["crypto_provider_default/opensslbssl"]
+boringssl = ["crypto_provider_default/boringssl"]
diff --git a/nearby/presence/np_ffi_core/src/common.rs b/nearby/presence/np_ffi_core/src/common.rs
index f00c358..5996e80 100644
--- a/nearby/presence/np_ffi_core/src/common.rs
+++ b/nearby/presence/np_ffi_core/src/common.rs
@@ -14,11 +14,15 @@
//! Common externally-accessible FFI constructs which are needed
//! in order to define the interfaces in this crate's various modules.
-use alloc::string::String;
use array_view::ArrayView;
+use crypto_provider::{CryptoProvider, CryptoRng};
+use crypto_provider_default::CryptoProviderImpl;
use handle_map::HandleNotPresentError;
+use lock_adapter::std::{RwLock, RwLockWriteGuard};
+use lock_adapter::RwLock as _;
+use std::string::String;
-const DEFAULT_MAX_HANDLES: u32 = u32::MAX - 1;
+pub(crate) const DEFAULT_MAX_HANDLES: u32 = u32::MAX - 1;
/// Configuration for top-level constants to be used
/// by the rest of the FFI which are independent of
@@ -38,6 +42,11 @@ pub struct CommonConfig {
/// - 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.
@@ -54,6 +63,12 @@ pub struct CommonConfig {
/// value will be set to `u32::MAX - 1`, which is the upper-bound
/// on this value.
max_num_deserialized_v1_advertisements: u32,
+
+ /// The maximum number of v1 advertisement builders
+ /// 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_v1_advertisement_builders: u32,
}
impl Default for CommonConfig {
@@ -66,9 +81,11 @@ impl CommonConfig {
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,
+ max_num_v1_advertisement_builders: DEFAULT_MAX_HANDLES,
}
}
#[cfg(feature = "std")]
@@ -90,6 +107,9 @@ impl CommonConfig {
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
}
@@ -99,6 +119,9 @@ impl CommonConfig {
pub(crate) fn max_num_deserialized_v1_advertisements(&self) -> u32 {
self.max_num_deserialized_v1_advertisements
}
+ pub(crate) fn max_num_v1_advertisement_builders(&self) -> u32 {
+ self.max_num_v1_advertisement_builders
+ }
pub(crate) fn set_num_shards(&mut self, num_shards: u8) {
self.num_shards = num_shards
}
@@ -110,6 +133,13 @@ impl CommonConfig {
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`.
@@ -121,7 +151,7 @@ impl CommonConfig {
DEFAULT_MAX_HANDLES.min(max_num_deserialized_v0_advertisements)
}
- /// Sets the maximum number of active handles to deserialized v0
+ /// Sets the maximum number of active handles to deserialized v1
/// advertisements which may be active at any one time.
/// Max value: `u32::MAX - 1`.
pub fn set_max_num_deserialized_v1_advertisements(
@@ -131,13 +161,26 @@ impl CommonConfig {
self.max_num_deserialized_v1_advertisements =
DEFAULT_MAX_HANDLES.min(max_num_deserialized_v1_advertisements)
}
+ /// Sets the maximum number of active handles to v1 advertisement
+ /// builders which may be active at any one time.
+ /// Max value: `u32::MAX - 1`.
+ pub fn set_max_num_v1_advertisement_builders(
+ &mut self,
+ max_num_v1_advertisement_builders: u32,
+ ) {
+ self.max_num_v1_advertisement_builders =
+ DEFAULT_MAX_HANDLES.min(max_num_v1_advertisement_builders)
+ }
}
-static COMMON_CONFIG: spin::RwLock<CommonConfig> = spin::RwLock::new(CommonConfig::new());
+static COMMON_CONFIG: RwLock<CommonConfig> = RwLock::new(CommonConfig::new());
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()
}
@@ -147,6 +190,9 @@ pub(crate) fn global_max_num_deserialized_v0_advertisements() -> u32 {
pub(crate) fn global_max_num_deserialized_v1_advertisements() -> u32 {
COMMON_CONFIG.read().max_num_deserialized_v1_advertisements()
}
+pub(crate) fn global_max_num_v1_advertisement_builders() -> u32 {
+ COMMON_CONFIG.read().max_num_v1_advertisement_builders()
+}
/// Sets an override to the number of shards to employ in the NP FFI's
/// internal handle-maps, which places an upper bound on the number
@@ -167,6 +213,17 @@ pub fn global_config_set_num_shards(num_shards: u8) {
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`.
///
@@ -239,8 +296,12 @@ pub struct RawAdvertisementPayload {
impl RawAdvertisementPayload {
/// Yields a slice of the bytes in this raw advertisement payload.
+ #[allow(clippy::unwrap_used)]
pub fn as_slice(&self) -> &[u8] {
- self.bytes.as_slice()
+ // The unwrapping here will never trigger a panic,
+ // because the byte-buffer is 255 bytes, the byte-length
+ // of which is the maximum value storable in a u8.
+ self.bytes.as_slice().unwrap()
}
}
@@ -257,6 +318,21 @@ pub struct ByteBuffer<const N: usize> {
bytes: [u8; N],
}
+/// A FFI safe wrapper of a fixed size array
+#[repr(C)]
+pub struct FixedSizeArray<const N: usize>([u8; N]);
+
+impl<const N: usize> FixedSizeArray<N> {
+ /// Constructs a byte-buffer from a Rust-side-derived owned array
+ pub(crate) fn from_array(bytes: [u8; N]) -> Self {
+ Self(bytes)
+ }
+ /// Yields a slice of the bytes
+ pub fn as_slice(&self) -> &[u8] {
+ self.0.as_slice()
+ }
+}
+
impl<const N: usize> ByteBuffer<N> {
/// Constructs a byte-buffer from a Rust-side-derived
/// ArrayView, which is assumed to be trusted to be
@@ -268,8 +344,73 @@ impl<const N: usize> ByteBuffer<N> {
Self { len, bytes }
}
/// Yields a slice of the first `self.len` bytes of `self.bytes`.
- pub fn as_slice(&self) -> &[u8] {
- &self.bytes[..(self.len as usize)]
+ pub fn as_slice(&self) -> Option<&[u8]> {
+ if self.len as usize <= N {
+ Some(&self.bytes[..(self.len as usize)])
+ } else {
+ None
+ }
+ }
+}
+
+pub(crate) type CryptoRngImpl = <CryptoProviderImpl as CryptoProvider>::CryptoRng;
+
+pub(crate) struct LazyInitCryptoRng {
+ maybe_rng: Option<CryptoRngImpl>,
+}
+
+impl LazyInitCryptoRng {
+ const fn new() -> Self {
+ Self { maybe_rng: None }
+ }
+ pub(crate) fn get_rng(&mut self) -> &mut CryptoRngImpl {
+ self.maybe_rng.get_or_insert_with(CryptoRngImpl::new)
+ }
+}
+
+/// Shared, lazily-initialized cryptographically-secure
+/// RNG for all operations in the FFI core.
+static CRYPTO_RNG: RwLock<LazyInitCryptoRng> = RwLock::new(LazyInitCryptoRng::new());
+
+/// Gets a write guard to the (lazily-init) library-global crypto rng.
+pub(crate) fn get_global_crypto_rng() -> RwLockWriteGuard<'static, LazyInitCryptoRng> {
+ CRYPTO_RNG.write()
+}
+
+/// 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<EncryptedIdentityType> for np_adv::de_type::EncryptedIdentityDataElementType {
+ fn from(val: EncryptedIdentityType) -> np_adv::de_type::EncryptedIdentityDataElementType {
+ use np_adv::de_type::EncryptedIdentityDataElementType;
+ match val {
+ EncryptedIdentityType::Private => EncryptedIdentityDataElementType::Private,
+ EncryptedIdentityType::Trusted => EncryptedIdentityDataElementType::Trusted,
+ EncryptedIdentityType::Provisioned => EncryptedIdentityDataElementType::Provisioned,
+ }
+ }
+}
+
+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,
+ }
}
}
diff --git a/nearby/presence/np_ffi_core/src/credentials.rs b/nearby/presence/np_ffi_core/src/credentials.rs
index 5e945dd..c1fc808 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 {
@@ -29,48 +298,71 @@ fn get_credential_book_handle_map_dimensions() -> HandleMapDimensions {
}
}
-declare_handle_map! {credential_book, CredentialBook, super::CredentialBookInternals, super::get_credential_book_handle_map_dimensions()}
-use credential_book::CredentialBook;
+declare_handle_map! {
+ mod credential_book {
+ #[dimensions = super::get_credential_book_handle_map_dimensions()]
+ type CredentialBook: HandleLike<Object = super::CredentialBookInternals>;
+ }
+}
+pub use credential_book::CredentialBook;
/// 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
+ }
}
}
}
@@ -84,5 +376,41 @@ pub fn deallocate_credential_book(credential_book: CredentialBook) -> Deallocate
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()
+}
+
+/// Cryptographic information about a particular V1 broadcast credential
+/// necessary to encrypt V1 MIC-verified and signature-verified sections.
+#[repr(C)]
+pub struct V1BroadcastCredential {
+ key_seed: [u8; 32],
+ metadata_key: [u8; 16],
+ private_key: [u8; 32],
+}
+
+impl V1BroadcastCredential {
+ /// Constructs a new `V1BroadcastCredential` from the given
+ /// key-seed, 16-byte metadata key, and the raw bytes
+ /// of the ed25519 private key.
+ ///
+ /// Safety: Since this representation requires transmission
+ /// of the raw bytes of an ed25519 private key (and other
+ /// sensitive cryptographic info) over FFI, foreign-lang
+ /// code around how this information is maintained
+ /// deserves close scrutiny.
+ pub fn new(key_seed: [u8; 32], metadata_key: [u8; 16], private_key: [u8; 32]) -> Self {
+ Self { key_seed, metadata_key, private_key }
+ }
+ pub(crate) fn into_internal(
+ self,
+ ) -> np_adv::credential::v1::SimpleSignedBroadcastCryptoMaterial {
+ let permit = crypto_provider::ed25519::RawPrivateKeyPermit::default();
+ np_adv::credential::v1::SimpleSignedBroadcastCryptoMaterial::new(
+ self.key_seed,
+ np_adv::MetadataKey(self.metadata_key),
+ crypto_provider::ed25519::PrivateKey::from_raw_private_key(self.private_key, &permit),
+ )
+ }
+}
diff --git a/nearby/presence/np_ffi_core/src/deserialize/mod.rs b/nearby/presence/np_ffi_core/src/deserialize/mod.rs
index 99ba846..5c1a891 100644
--- a/nearby/presence/np_ffi_core/src/deserialize/mod.rs
+++ b/nearby/presence/np_ffi_core/src/deserialize/mod.rs
@@ -19,7 +19,10 @@ use crate::deserialize::v0::*;
use crate::deserialize::v1::*;
use crate::utils::FfiEnum;
use crypto_provider_default::CryptoProviderImpl;
-use handle_map::{HandleLike, HandleMapFullError, HandleNotPresentError};
+use handle_map::{
+ declare_handle_map, HandleLike, HandleMapDimensions, HandleMapFullError, HandleNotPresentError,
+};
+use np_adv::deserialization_arena;
pub mod v0;
pub mod v1;
@@ -86,7 +89,7 @@ enum DeserializeAdvertisementSuccess {
V1(DeserializedV1Advertisement),
}
-struct DeserializeAdvertisementError;
+pub(crate) struct DeserializeAdvertisementError;
impl From<HandleMapFullError> for DeserializeAdvertisementError {
fn from(_: HandleMapFullError) -> Self {
@@ -106,32 +109,18 @@ impl From<np_adv::AdvDeserializationError> for DeserializeAdvertisementError {
}
}
-type DefaultV0Credential = np_adv::credential::simple::SimpleV0Credential<
- np_adv::credential::v0::MinimumFootprintV0CryptoMaterial,
- (),
->;
-type DefaultV1Credential = np_adv::credential::simple::SimpleV1Credential<
- np_adv::credential::v1::MinimumFootprintV1CryptoMaterial,
- (),
->;
-type DefaultBothCredentialSource =
- np_adv::credential::source::OwnedBothCredentialSource<DefaultV0Credential, DefaultV1Credential>;
-
fn deserialize_advertisement_from_slice_internal(
adv_payload: &[u8],
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_source: DefaultBothCredentialSource = DefaultBothCredentialSource::new_empty();
+ let cred_book = &credential_book_read_guard.book;
+ let arena = deserialization_arena!();
let deserialized_advertisement =
- np_adv::deserialize_advertisement::<_, _, _, _, CryptoProviderImpl>(
- adv_payload,
- &cred_source,
- )?;
+ 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)?;
@@ -166,3 +155,132 @@ pub fn deserialize_advertisement(
) -> DeserializeAdvertisementResult {
deserialize_advertisement_from_slice(adv_payload.as_slice(), credential_book)
}
+
+/// Errors returned from attempting to decrypt metadata
+pub(crate) 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,
+}
+
+/// The result of decrypting metadata from either a V0Payload or DeserializedV1Section
+#[repr(C)]
+#[allow(missing_docs)]
+pub enum DecryptMetadataResult {
+ Success(DecryptedMetadata),
+ Error,
+}
+
+/// Discriminant for `DecryptMetadataResult`.
+#[repr(u8)]
+pub enum DecryptMetadataResultKind {
+ /// The attempt to decrypt the metadata of the associated credential succeeded
+ /// The associated payload may be obtained via
+ /// `DecryptMetadataResult#into_success`.
+ Success,
+ /// The attempt to decrypt the metadata failed, either the payload had no matching identity
+ /// ie it was a public advertisement OR the decrypt attempt itself was unsuccessful
+ Error,
+}
+
+impl FfiEnum for DecryptMetadataResult {
+ type Kind = DecryptMetadataResultKind;
+
+ fn kind(&self) -> Self::Kind {
+ match self {
+ DecryptMetadataResult::Success(_) => DecryptMetadataResultKind::Success,
+ DecryptMetadataResult::Error => DecryptMetadataResultKind::Error,
+ }
+ }
+}
+
+impl DecryptMetadataResult {
+ declare_enum_cast! {into_success, Success, DecryptedMetadata}
+}
+
+/// Internals of decrypted metadata
+pub struct DecryptedMetadataInternals {
+ decrypted_bytes: Box<[u8]>,
+}
+
+declare_handle_map! {
+ mod decrypted_metadata {
+ #[dimensions = super::get_decrypted_metadata_handle_map_dimensions()]
+ type DecryptedMetadata: HandleLike<Object = super::DecryptedMetadataInternals>;
+ }
+}
+use decrypted_metadata::DecryptedMetadata;
+
+fn get_decrypted_metadata_handle_map_dimensions() -> HandleMapDimensions {
+ HandleMapDimensions { num_shards: global_num_shards(), max_active_handles: DEFAULT_MAX_HANDLES }
+}
+
+/// The pointer and length of the decrypted metadata byte buffer
+#[repr(C)]
+pub struct MetadataBufferParts {
+ ptr: *const u8,
+ len: usize,
+}
+
+#[repr(C)]
+#[allow(missing_docs)]
+pub enum GetMetadataBufferPartsResult {
+ Success(MetadataBufferParts),
+ Error,
+}
+
+impl GetMetadataBufferPartsResult {
+ declare_enum_cast! {into_success, Success, MetadataBufferParts}
+}
+
+#[repr(u8)]
+#[allow(missing_docs)]
+pub enum GetMetadataBufferPartsResultKind {
+ Success = 0,
+ Error = 1,
+}
+
+impl FfiEnum for GetMetadataBufferPartsResult {
+ type Kind = GetMetadataBufferPartsResultKind;
+
+ fn kind(&self) -> Self::Kind {
+ match self {
+ GetMetadataBufferPartsResult::Success(_) => GetMetadataBufferPartsResultKind::Success,
+ GetMetadataBufferPartsResult::Error => GetMetadataBufferPartsResultKind::Error,
+ }
+ }
+}
+
+fn allocate_decrypted_metadata_handle(metadata: Vec<u8>) -> DecryptMetadataResult {
+ let allocate_result = DecryptedMetadata::allocate(move || DecryptedMetadataInternals {
+ decrypted_bytes: metadata.into_boxed_slice(),
+ });
+ match allocate_result {
+ Ok(decrypted) => DecryptMetadataResult::Success(decrypted),
+ Err(_) => DecryptMetadataResult::Error,
+ }
+}
+
+impl DecryptedMetadata {
+ /// Gets the raw parts, pointer + length representation of the metadata byte buffer
+ pub fn get_metadata_buffer_parts(&self) -> GetMetadataBufferPartsResult {
+ match self.get() {
+ Ok(metadata_internals) => {
+ let result = MetadataBufferParts {
+ ptr: metadata_internals.decrypted_bytes.as_ptr(),
+ len: metadata_internals.decrypted_bytes.len(),
+ };
+ GetMetadataBufferPartsResult::Success(result)
+ }
+ Err(_) => GetMetadataBufferPartsResult::Error,
+ }
+ }
+
+ /// Frees the underlying decrypted metadata buffer
+ pub fn deallocate_metadata(&self) -> DeallocateResult {
+ self.deallocate().map(|_| ()).into()
+ }
+}
diff --git a/nearby/presence/np_ffi_core/src/deserialize/v0.rs b/nearby/presence/np_ffi_core/src/deserialize/v0.rs
index 0f4b24b..14214c4 100644
--- a/nearby/presence/np_ffi_core/src/deserialize/v0.rs
+++ b/nearby/presence/np_ffi_core/src/deserialize/v0.rs
@@ -15,11 +15,17 @@
use crate::common::*;
use crate::credentials::credential_book::CredentialBook;
+use crate::credentials::MatchedCredential;
+use crate::deserialize::{
+ allocate_decrypted_metadata_handle, DecryptMetadataError, DecryptMetadataResult,
+};
use crate::utils::{FfiEnum, LocksLongerThan};
-use alloc::vec::Vec;
+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
#[derive(Clone, Copy)]
@@ -66,20 +72,28 @@ impl DeserializedV0Advertisement {
}
}
- pub(crate) fn allocate_with_contents<'m, M: np_adv::credential::MatchedCredential<'m>>(
- contents: np_adv::V0AdvContents<'m, M>,
- ) -> Result<Self, HandleMapFullError> {
+ pub(crate) fn allocate_with_contents(
+ contents: np_adv::V0AdvertisementContents<
+ np_adv::credential::ReferencedMatchedCredential<MatchedCredential>,
+ >,
+ ) -> Result<Self, DeserializeAdvertisementError> {
match contents {
- np_adv::V0AdvContents::Plaintext(plaintext_contents) => {
+ np_adv::V0AdvertisementContents::Plaintext(plaintext_contents) => {
let adv = LegibleDeserializedV0Advertisement::allocate_with_plaintext_contents(
plaintext_contents,
)?;
Ok(Self::Legible(adv))
}
- np_adv::V0AdvContents::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)
}
- np_adv::V0AdvContents::NoMatchingCredentials => Ok(Self::NoMatchingCredentials),
}
}
@@ -91,17 +105,50 @@ impl DeserializedV0Advertisement {
pub struct LegibleDeserializedV0Advertisement {
num_des: u8,
payload: V0Payload,
- identity: DeserializedV0Identity,
+ identity_kind: DeserializedV0IdentityKind,
}
impl LegibleDeserializedV0Advertisement {
pub(crate) fn allocate_with_plaintext_contents(
contents: np_adv::legacy::deserialize::PlaintextAdvContents,
- ) -> Result<Self, HandleMapFullError> {
- let data_elements = contents.to_data_elements();
+ ) -> Result<Self, DeserializeAdvertisementError> {
+ let data_elements = contents
+ .data_elements()
+ .collect::<Result<Vec<_>, _>>()
+ .map_err(|_| DeserializeAdvertisementError)?;
let num_des = data_elements.len() as u8;
- let payload = V0Payload::allocate_with_data_elements(data_elements)?;
- Ok(Self { num_des, payload, identity: DeserializedV0Identity::Plaintext })
+ 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(...)`.
@@ -109,12 +156,13 @@ impl LegibleDeserializedV0Advertisement {
self.num_des
}
/// Destructures this legible advertisement into just the payload
- pub fn into_payload(self) -> V0Payload {
+ pub fn payload(&self) -> V0Payload {
self.payload
}
- /// Destructures this legible advertisement into just the identity information
- pub fn into_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 {
@@ -122,7 +170,8 @@ impl LegibleDeserializedV0Advertisement {
}
}
-/// 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 {
@@ -132,29 +181,136 @@ pub enum DeserializedV0IdentityKind {
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)]
+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 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 DeserializedV0Identity {
- Plaintext,
- // TODO: This gets a payload once we support creds
- Decrypted,
+pub enum GetV0IdentityDetailsResult {
+ Error,
+ Success(DeserializedV0IdentityDetails),
}
-impl FfiEnum for DeserializedV0Identity {
- type Kind = DeserializedV0IdentityKind;
+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>,
}
@@ -167,6 +323,24 @@ impl V0PayloadInternals {
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 {
@@ -176,18 +350,46 @@ fn get_v0_payload_handle_map_dimensions() -> HandleMapDimensions {
}
}
-declare_handle_map! {v0_payload, V0Payload, super::V0PayloadInternals, super::get_v0_payload_handle_map_dimensions() }
+declare_handle_map! {
+ mod v0_payload {
+ #[dimensions = super::get_v0_payload_handle_map_dimensions()]
+ type V0Payload: HandleLike<Object = super::V0PayloadInternals>;
+ }
+}
use v0_payload::V0Payload;
+use super::DeserializeAdvertisementError;
+
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
@@ -198,6 +400,28 @@ impl V0Payload {
}
}
+ /// Gets the identity details for this V0 payload,
+ /// if this payload was associated 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)
+ pub fn decrypt_metadata(&self) -> DecryptMetadataResult {
+ match self.get() {
+ Ok(read_guard) => match read_guard.decrypt_metadata() {
+ Ok(decrypted_metadata) => allocate_decrypted_metadata_handle(decrypted_metadata),
+ Err(_) => DecryptMetadataResult::Error,
+ },
+ Err(_) => DecryptMetadataResult::Error,
+ }
+ }
+
/// Deallocates any underlying data held by a V0Payload
pub fn deallocate_payload(&self) -> DeallocateResult {
self.deallocate().map(|_| ()).into()
@@ -327,10 +551,10 @@ impl<F: np_adv::legacy::PacketFlavor> From<np_adv::legacy::actions::ActionsDataE
fn from(value: ActionsDataElement<F>) -> Self {
match F::ENUM_VARIANT {
PacketFlavorEnum::Plaintext => {
- Self::Plaintext(V0ActionBits { bitfield: value.as_u32() })
+ Self::Plaintext(V0ActionBits { bitfield: value.action.as_u32() })
}
PacketFlavorEnum::Ciphertext => {
- Self::Encrypted(V0ActionBits { bitfield: value.as_u32() })
+ Self::Encrypted(V0ActionBits { bitfield: value.action.as_u32() })
}
}
}
@@ -402,6 +626,7 @@ impl V0Actions {
let actions_de = np_adv::legacy::actions::ActionsDataElement::from(bits);
Ok(actions_de
+ .action
.has_action(&action_type.into())
.expect("BooleanActionType only has one bit"))
}
@@ -413,6 +638,7 @@ impl V0Actions {
let actions_de = np_adv::legacy::actions::ActionsDataElement::from(bits);
Ok(actions_de
+ .action
.has_action(&action_type.into())
.expect("BooleanActionType only has one bit"))
}
@@ -430,7 +656,7 @@ impl V0Actions {
.map_err(|_| InvalidActionBits)?;
let actions_de = np_adv::legacy::actions::ActionsDataElement::from(bits);
- Ok(actions_de.context_sync_seq_num().as_u8())
+ Ok(actions_de.action.context_sync_seq_num().as_u8())
}
V0Actions::Encrypted(action_bits) => {
let bits = np_adv::legacy::actions::ActionBits::<Ciphertext>::try_from(
@@ -438,7 +664,7 @@ impl V0Actions {
)
.map_err(|_| InvalidActionBits)?;
let actions_de = np_adv::legacy::actions::ActionsDataElement::from(bits);
- Ok(actions_de.context_sync_seq_num().as_u8())
+ Ok(actions_de.action.context_sync_seq_num().as_u8())
}
}
}
diff --git a/nearby/presence/np_ffi_core/src/deserialize/v1.rs b/nearby/presence/np_ffi_core/src/deserialize/v1.rs
index 9b4a3f4..cb56b33 100644
--- a/nearby/presence/np_ffi_core/src/deserialize/v1.rs
+++ b/nearby/presence/np_ffi_core/src/deserialize/v1.rs
@@ -13,12 +13,20 @@
// limitations under the License.
//! Core NP Rust FFI structures and methods for v1 advertisement deserialization.
+use super::DeserializeAdvertisementError;
use crate::common::*;
use crate::credentials::credential_book::CredentialBook;
+use crate::credentials::MatchedCredential;
+use crate::deserialize::{allocate_decrypted_metadata_handle, DecryptMetadataResult};
use crate::utils::*;
-use alloc::vec::Vec;
+use crate::v1::V1VerificationMode;
use array_view::ArrayView;
-use handle_map::{declare_handle_map, HandleLike, HandleMapDimensions, HandleMapFullError};
+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
#[repr(C)]
@@ -56,14 +64,17 @@ impl DeserializedV1Advertisement {
self.legible_sections.deallocate().map(|_| ()).into()
}
- pub(crate) fn allocate_with_contents<'m, M: np_adv::credential::MatchedCredential<'m>>(
- contents: np_adv::V1AdvContents<'m, M>,
- ) -> Result<Self, HandleMapFullError> {
+ 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;
- let legible_sections = contents.into_valid_sections();
+ let legible_sections = contents.into_sections();
let num_legible_sections = legible_sections.len() as u8;
- let legible_sections = LegibleV1Sections::allocate_with_contents(legible_sections)?;
+ let legible_sections =
+ LegibleV1Sections::allocate_with_contents(legible_sections.into_vec())?;
Ok(Self { num_undecryptable_sections, num_legible_sections, legible_sections })
}
}
@@ -105,12 +116,31 @@ impl LegibleV1SectionsInternals {
}
}
-impl<'m, M: np_adv::credential::MatchedCredential<'m>>
- From<Vec<np_adv::V1DeserializedSection<'m, M>>> for LegibleV1SectionsInternals
+impl<'adv>
+ TryFrom<
+ Vec<
+ np_adv::V1DeserializedSection<
+ 'adv,
+ np_adv::credential::ReferencedMatchedCredential<'adv, MatchedCredential>,
+ >,
+ >,
+ > for LegibleV1SectionsInternals
{
- fn from(contents: Vec<np_adv::V1DeserializedSection<'m, M>>) -> Self {
- let sections = contents.into_iter().map(DeserializedV1SectionInternals::from).collect();
- Self { sections }
+ type Error = DataElementParseError;
+
+ fn try_from(
+ contents: Vec<
+ np_adv::V1DeserializedSection<
+ 'adv,
+ np_adv::credential::ReferencedMatchedCredential<'adv, MatchedCredential>,
+ >,
+ >,
+ ) -> Result<Self, Self::Error> {
+ let sections = contents
+ .into_iter()
+ .map(DeserializedV1SectionInternals::try_from)
+ .collect::<Result<Vec<_>, _>>()?;
+ Ok(Self { sections })
}
}
@@ -121,16 +151,26 @@ fn get_legible_v1_sections_handle_map_dimensions() -> HandleMapDimensions {
}
}
-declare_handle_map! {legible_v1_sections, LegibleV1Sections, super::LegibleV1SectionsInternals, super::get_legible_v1_sections_handle_map_dimensions() }
-use legible_v1_sections::LegibleV1Sections;
+declare_handle_map! {
+ mod legible_v1_sections {
+ #[dimensions = super::get_legible_v1_sections_handle_map_dimensions()]
+ type LegibleV1Sections: HandleLike<Object = super::LegibleV1SectionsInternals>;
+ }
+}
impl LocksLongerThan<LegibleV1Sections> for CredentialBook {}
impl LegibleV1Sections {
- pub(crate) fn allocate_with_contents<'m, M: np_adv::credential::MatchedCredential<'m>>(
- contents: Vec<np_adv::V1DeserializedSection<'m, M>>,
- ) -> Result<Self, HandleMapFullError> {
- Self::allocate(move || LegibleV1SectionsInternals::from(contents))
+ 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)?;
+ Self::allocate(move || section).map_err(|e| e.into())
}
}
@@ -173,10 +213,48 @@ impl GetV1SectionResult {
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.
+#[repr(C)]
+#[allow(missing_docs)]
+pub enum GetV1DE16ByteSaltResult {
+ Error,
+ Success(FixedSizeArray<16>),
+}
+
+impl GetV1DE16ByteSaltResult {
+ declare_enum_cast! {into_success, Success, FixedSizeArray<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 {
@@ -184,10 +262,16 @@ impl DeserializedV1SectionInternals {
fn num_des(&self) -> u8 {
self.des.len() as u8
}
+
/// 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 {
match self.des.get(index as usize) {
@@ -195,22 +279,89 @@ impl DeserializedV1SectionInternals {
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) -> DecryptMetadataResult {
+ match &self.identity {
+ None => DecryptMetadataResult::Error,
+ Some(identity) => match identity.decrypt_metadata() {
+ None => DecryptMetadataResult::Error,
+ Some(metadata) => allocate_decrypted_metadata_handle(metadata),
+ },
+ }
+ }
+
+ /// 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<'m, M: np_adv::credential::MatchedCredential<'m>> From<np_adv::V1DeserializedSection<'m, M>>
- for DeserializedV1SectionInternals
+impl<'adv>
+ TryFrom<
+ np_adv::V1DeserializedSection<
+ 'adv,
+ np_adv::credential::ReferencedMatchedCredential<'adv, MatchedCredential>,
+ >,
+ > for DeserializedV1SectionInternals
{
- fn from(section: np_adv::V1DeserializedSection<'m, M>) -> Self {
+ type Error = DataElementParseError;
+
+ 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 {
V1DeserializedSection::Plaintext(section) => {
- let des = section.data_elements().map(V1DataElement::from).collect();
- let identity = DeserializedV1Identity::Plaintext;
- Self { des, identity }
+ let des = section
+ .iter_data_elements()
+ .map(|r| r.map(|de| V1DataElement::from(&de)))
+ .collect::<Result<Vec<_>, _>>()?;
+ 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 })
}
}
}
@@ -226,27 +377,149 @@ pub enum DeserializedV1IdentityKind {
Decrypted = 1,
}
-/// Deserialized information about the identity
-/// employed in a V1 adveritsement section.
+/// 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 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<FixedSizeArray<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)).map(FixedSizeArray::from_array)
+ }
+}
+
+/// 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 DeserializedV1Identity {
- Plaintext,
- // TODO: This gets a payload once we support creds
- Decrypted,
+pub enum GetV1IdentityDetailsResult {
+ Error,
+ Success(DeserializedV1IdentityDetails),
}
-impl FfiEnum for DeserializedV1Identity {
- type Kind = DeserializedV1IdentityKind;
+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 {
@@ -270,16 +543,57 @@ impl DeserializedV1Section {
/// 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) -> DecryptMetadataResult {
+ self.apply_to_section_internals(
+ DeserializedV1SectionInternals::decrypt_metadata,
+ DecryptMetadataResult::Error,
+ )
+ }
+ /// 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,
+ )
+ }
+
+ 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,
}
}
}
@@ -341,15 +655,16 @@ impl V1DataElement {
}
}
-impl<'a> From<np_adv::extended::deserialize::DataElement<'a>> for V1DataElement {
- fn from(de: np_adv::extended::deserialize::DataElement<'a>) -> Self {
+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 })
}
}
@@ -359,6 +674,8 @@ impl<'a> From<np_adv::extended::deserialize::DataElement<'a>> for V1DataElement
#[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
@@ -367,6 +684,10 @@ pub struct GenericV1DataElement {
}
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 dd9e49e..f3bb7a3 100644
--- a/nearby/presence/np_ffi_core/src/lib.rs
+++ b/nearby/presence/np_ffi_core/src/lib.rs
@@ -11,22 +11,16 @@
// 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
-#![cfg_attr(not(feature = "std"), no_std)]
-#![forbid(unsafe_code)]
-#![deny(
- missing_docs,
- clippy::indexing_slicing,
- clippy::unwrap_used,
- clippy::panic,
- clippy::expect_used
-)]
-extern crate alloc;
-extern crate core;
+#[macro_use]
+extern crate lazy_static;
#[macro_use]
pub mod utils;
pub mod common;
pub mod credentials;
pub mod deserialize;
+pub mod serialize;
+pub mod v1;
diff --git a/nearby/presence/np_ffi_core/src/serialize/mod.rs b/nearby/presence/np_ffi_core/src/serialize/mod.rs
new file mode 100644
index 0000000..aeb68a0
--- /dev/null
+++ b/nearby/presence/np_ffi_core/src/serialize/mod.rs
@@ -0,0 +1,38 @@
+// 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.
+//! Core NP Rust FFI structures and methods for advertisement serialization.
+
+pub mod v1;
+
+/// Enum common to V0 and V1 serialization expressing
+/// what kind of advertisement builder (public/encrypted)
+/// is in use.
+#[derive(Clone, Copy)]
+#[repr(u8)]
+pub enum AdvertisementBuilderKind {
+ /// The builder is for a public advertisement.
+ Public = 0,
+ /// The builder is for an encrypted advertisement.
+ Encrypted = 1,
+}
+
+impl AdvertisementBuilderKind {
+ pub(crate) fn as_internal_v1(&self) -> np_adv::extended::serialize::AdvertisementType {
+ use np_adv::extended::serialize::AdvertisementType;
+ match self {
+ Self::Public => AdvertisementType::Plaintext,
+ Self::Encrypted => AdvertisementType::Encrypted,
+ }
+ }
+}
diff --git a/nearby/presence/np_ffi_core/src/serialize/v1.rs b/nearby/presence/np_ffi_core/src/serialize/v1.rs
new file mode 100644
index 0000000..ccc6ef7
--- /dev/null
+++ b/nearby/presence/np_ffi_core/src/serialize/v1.rs
@@ -0,0 +1,589 @@
+// 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.
+
+//! NP Rust FFI structures and methods for V1 advertisement serialization.
+
+use crate::common::*;
+use crate::credentials::V1BroadcastCredential;
+use crate::serialize::AdvertisementBuilderKind;
+use crate::utils::FfiEnum;
+use crate::v1::V1VerificationMode;
+use crypto_provider_default::CryptoProviderImpl;
+use handle_map::{declare_handle_map, HandleLike, HandleMapDimensions, HandleMapFullError};
+use np_adv;
+use np_adv_dynamic;
+
+/// A handle to a builder for V1 advertisements.
+#[derive(Clone, Copy)]
+#[repr(C)]
+pub struct V1AdvertisementBuilder {
+ kind: AdvertisementBuilderKind,
+ handle: V1AdvertisementBuilderHandle,
+}
+
+impl V1AdvertisementBuilder {
+ /// Attempts to create a builder for a new public section within
+ /// this advertisement, returning a handle to the newly-created
+ /// section builder if successful.
+ ///
+ /// This method may fail if there is another currently-active
+ /// section builder for the same advertisement builder, if the
+ /// kind of section being added does not match the advertisement
+ /// type (public/encrypted), or if the section would not manage
+ /// to fit within the enclosing advertisement.
+ pub fn public_section_builder(&self) -> CreateV1SectionBuilderResult {
+ self.section_builder_internals(|internals| internals.public_section_builder())
+ }
+ /// Attempts to create a builder for a new encrypted section within
+ /// this advertisement, returning a handle to the newly-created
+ /// section builder if successful.
+ ///
+ /// The identity details for the new section builder may be specified
+ /// via providing the broadcast credential data, the kind of encrypted
+ /// identity being broadcast (private/trusted/provisioned), and the
+ /// verification mode (MIC/Signature) to be used for the encrypted section.
+ ///
+ /// This method may fail if there is another currently-active
+ /// section builder for the same advertisement builder, if the
+ /// kind of section being added does not match the advertisement
+ /// type (public/encrypted), or if the section would not manage
+ /// to fit within the enclosing advertisement.
+ pub fn encrypted_section_builder(
+ &self,
+ broadcast_cred: V1BroadcastCredential,
+ identity_type: EncryptedIdentityType,
+ verification_mode: V1VerificationMode,
+ ) -> CreateV1SectionBuilderResult {
+ self.section_builder_internals(move |internals| {
+ internals.encrypted_section_builder(broadcast_cred, identity_type, verification_mode)
+ })
+ }
+
+ fn section_builder_internals(
+ &self,
+ builder_supplier: impl FnOnce(
+ &mut V1AdvertisementBuilderInternals,
+ ) -> Result<usize, SectionBuilderError>,
+ ) -> CreateV1SectionBuilderResult {
+ match self.handle.get_mut() {
+ Ok(mut adv_builder_write_guard) => {
+ match builder_supplier(&mut adv_builder_write_guard) {
+ Ok(section_index) => CreateV1SectionBuilderResult::Success(V1SectionBuilder {
+ adv_builder: *self,
+ section_index: section_index as u8,
+ }),
+ Err(e) => e.into(),
+ }
+ }
+ Err(_) => CreateV1SectionBuilderResult::InvalidAdvBuilderHandle,
+ }
+ }
+}
+
+/// Discriminant for `CreateV1AdvertisementBuilderResult`
+
+#[derive(Copy, Clone)]
+#[repr(u8)]
+pub enum CreateV1AdvertisementBuilderResultKind {
+ /// The attempt to create a new advertisement builder
+ /// failed since there are no more available
+ /// slots for V1 advertisement builders in their handle-map.
+ NoSpaceLeft = 0,
+ /// The attempt succeeded. The wrapped advertisement builder
+ /// may be obtained via
+ /// `CreateV1AdvertisementBuilderResult#into_success`.
+ Success = 1,
+}
+
+/// The result of attempting to create a new V1 advertisement builder.
+#[repr(C)]
+#[allow(missing_docs)]
+pub enum CreateV1AdvertisementBuilderResult {
+ NoSpaceLeft,
+ Success(V1AdvertisementBuilder),
+}
+
+impl From<Result<V1AdvertisementBuilder, HandleMapFullError>>
+ for CreateV1AdvertisementBuilderResult
+{
+ fn from(result: Result<V1AdvertisementBuilder, HandleMapFullError>) -> Self {
+ match result {
+ Ok(builder) => CreateV1AdvertisementBuilderResult::Success(builder),
+ Err(_) => CreateV1AdvertisementBuilderResult::NoSpaceLeft,
+ }
+ }
+}
+
+impl FfiEnum for CreateV1AdvertisementBuilderResult {
+ type Kind = CreateV1AdvertisementBuilderResultKind;
+ fn kind(&self) -> Self::Kind {
+ match self {
+ CreateV1AdvertisementBuilderResult::NoSpaceLeft => {
+ CreateV1AdvertisementBuilderResultKind::NoSpaceLeft
+ }
+ CreateV1AdvertisementBuilderResult::Success(_) => {
+ CreateV1AdvertisementBuilderResultKind::Success
+ }
+ }
+ }
+}
+
+impl CreateV1AdvertisementBuilderResult {
+ declare_enum_cast! {into_success, Success, V1AdvertisementBuilder }
+}
+
+/// Creates a new V1 advertisement builder for the given advertisement
+/// kind (public/encrypted).
+pub fn create_v1_advertisement_builder(
+ kind: AdvertisementBuilderKind,
+) -> CreateV1AdvertisementBuilderResult {
+ V1AdvertisementBuilderHandle::allocate(move || V1AdvertisementBuilderInternals::new(kind))
+ .map(|handle| V1AdvertisementBuilder { kind, handle })
+ .into()
+}
+
+impl V1AdvertisementBuilder {
+ /// Gets the kind of advertisement builder (public/encrypted)
+ pub fn kind(&self) -> AdvertisementBuilderKind {
+ self.kind
+ }
+}
+
+pub(crate) enum V1AdvertisementBuilderState {
+ /// Internal state for when we have an active advertisement
+ /// builder, but no currently-active section builder.
+ Advertisement(np_adv_dynamic::extended::BoxedAdvBuilder),
+ /// Internal state for when we have both an active advertisement
+ /// builder and an active section builder.
+ Section(
+ np_adv_dynamic::extended::BoxedSectionBuilder<
+ np_adv::extended::serialize::AdvBuilder,
+ CryptoProviderImpl,
+ >,
+ ),
+}
+
+/// Internal version of errors which may be raised when
+/// attempting to derive a new section builder from an
+/// advertisement builder.
+pub(crate) enum SectionBuilderError {
+ /// We're currently in the middle of building a section.
+ UnclosedActiveSection,
+ /// We're attempting to build a section with an identity
+ /// kind (public/encrypted) which doesn't match the kind
+ /// for the entire advertisement.
+ IdentityKindMismatch,
+ /// There isn't enough space for a new section, either
+ /// because the maximum section count has been exceeded
+ /// or because the advertisement is almost full, and
+ /// the minimum size of a section wouldn't fit.
+ NoSpaceLeft,
+}
+
+impl From<np_adv_dynamic::extended::BoxedAddSectionError> for SectionBuilderError {
+ fn from(err: np_adv_dynamic::extended::BoxedAddSectionError) -> Self {
+ use np_adv::extended::serialize::AddSectionError;
+ use np_adv_dynamic::extended::BoxedAddSectionError;
+ match err {
+ BoxedAddSectionError::IdentityRequiresSaltError
+ | BoxedAddSectionError::Underlying(AddSectionError::IncompatibleSectionType) => {
+ SectionBuilderError::IdentityKindMismatch
+ }
+ BoxedAddSectionError::Underlying(AddSectionError::InsufficientAdvSpace)
+ | BoxedAddSectionError::Underlying(AddSectionError::MaxSectionCountExceeded) => {
+ SectionBuilderError::NoSpaceLeft
+ }
+ }
+ }
+}
+
+/// Internal, Rust-side implementation of a V1 advertisement builder.
+pub struct V1AdvertisementBuilderInternals {
+ // Note: This is actually always populated from an external
+ // perspective in the absence of panics. We only need
+ // the `Option` in order to be able to take ownership
+ // and perform a state transition when needed.
+ state: Option<V1AdvertisementBuilderState>,
+}
+
+impl V1AdvertisementBuilderInternals {
+ pub(crate) fn new(kind: AdvertisementBuilderKind) -> Self {
+ let adv_type = kind.as_internal_v1();
+ let builder = np_adv::extended::serialize::AdvBuilder::new(adv_type);
+ let builder = builder.into();
+ let state = Some(V1AdvertisementBuilderState::Advertisement(builder));
+ Self { state }
+ }
+ /// Internals of section_builder-type routines. Upon success, yields the index
+ /// of the newly-added section builder.
+ pub(crate) fn section_builder_internal(
+ &mut self,
+ identity: np_adv_dynamic::extended::BoxedIdentity<CryptoProviderImpl>,
+ ) -> Result<usize, SectionBuilderError> {
+ let state = self.state.take();
+ match state {
+ Some(V1AdvertisementBuilderState::Advertisement(adv_builder)) => {
+ match adv_builder.into_section_builder::<CryptoProviderImpl>(identity) {
+ Ok(section_builder) => {
+ let section_index = section_builder.section_index();
+ self.state = Some(V1AdvertisementBuilderState::Section(section_builder));
+ Ok(section_index)
+ }
+ Err((adv_builder, err)) => {
+ self.state = Some(V1AdvertisementBuilderState::Advertisement(adv_builder));
+ Err(err.into())
+ }
+ }
+ }
+ x => {
+ // Note: Technically, this case also would leave the `None` state
+ // if we ever entered into it, but we never transition to that
+ // state during normal operation.
+ self.state = x;
+ Err(SectionBuilderError::UnclosedActiveSection)
+ }
+ }
+ }
+
+ pub(crate) fn public_section_builder(&mut self) -> Result<usize, SectionBuilderError> {
+ let identity = np_adv_dynamic::extended::BoxedIdentity::PublicIdentity;
+ self.section_builder_internal(identity)
+ }
+ pub(crate) fn encrypted_section_builder(
+ &mut self,
+ broadcast_cred: V1BroadcastCredential,
+ identity_type: EncryptedIdentityType,
+ verification_mode: V1VerificationMode,
+ ) -> Result<usize, SectionBuilderError> {
+ let mut rng = get_global_crypto_rng();
+ let rng = rng.get_rng();
+ let identity_type = identity_type.into();
+ let internal_broadcast_cred = broadcast_cred.into_internal();
+ let identity = match verification_mode {
+ V1VerificationMode::Mic => {
+ let encoder = np_adv::extended::serialize::MicEncryptedSectionEncoder::<
+ CryptoProviderImpl,
+ >::new_random_salt(
+ rng, identity_type, &internal_broadcast_cred
+ );
+ np_adv_dynamic::extended::BoxedIdentity::MicEncrypted(encoder)
+ }
+ V1VerificationMode::Signature => {
+ let encoder = np_adv::extended::serialize::SignedEncryptedSectionEncoder::<
+ CryptoProviderImpl,
+ >::new_random_salt(
+ rng, identity_type, &internal_broadcast_cred
+ );
+ np_adv_dynamic::extended::BoxedIdentity::SignedEncrypted(encoder)
+ }
+ };
+ self.section_builder_internal(identity)
+ }
+}
+
+fn get_v1_advertisement_builder_handle_map_dimensions() -> HandleMapDimensions {
+ HandleMapDimensions {
+ num_shards: global_num_shards(),
+ max_active_handles: global_max_num_v1_advertisement_builders(),
+ }
+}
+
+declare_handle_map! {
+ mod advertisement_builder {
+ #[dimensions = super::get_v1_advertisement_builder_handle_map_dimensions()]
+ type V1AdvertisementBuilderHandle: HandleLike<Object = super::V1AdvertisementBuilderInternals>;
+ }
+}
+use crate::serialize::v1::advertisement_builder::V1AdvertisementBuilderHandle;
+
+/// Discriminant for `CreateV1SectionBuilderResult`
+#[derive(Copy, Clone)]
+#[repr(u8)]
+pub enum CreateV1SectionBuilderResultKind {
+ /// The attempt to create a new section builder succeeded.
+ Success = 0,
+ /// We're currently in the middle of building a section.
+ UnclosedActiveSection = 1,
+ /// The advertisement builder handle was invalid.
+ InvalidAdvBuilderHandle = 2,
+ /// We're attempting to build a section with an identity
+ /// kind (public/encrypted) which doesn't match the kind
+ /// for the entire advertisement.
+ IdentityKindMismatch = 3,
+ /// There isn't enough space for a new section, either
+ /// because the maximum section count has been exceeded
+ /// or because the advertisement is almost full, and
+ /// the minimum size of a section wouldn't fit.
+ NoSpaceLeft = 4,
+}
+
+/// The result of attempting to create a new V1 section builder.
+#[repr(C)]
+#[allow(missing_docs)]
+pub enum CreateV1SectionBuilderResult {
+ Success(V1SectionBuilder),
+ UnclosedActiveSection,
+ InvalidAdvBuilderHandle,
+ IdentityKindMismatch,
+ NoSpaceLeft,
+}
+
+impl FfiEnum for CreateV1SectionBuilderResult {
+ type Kind = CreateV1SectionBuilderResultKind;
+ fn kind(&self) -> Self::Kind {
+ match self {
+ Self::Success(_) => CreateV1SectionBuilderResultKind::Success,
+ Self::UnclosedActiveSection => CreateV1SectionBuilderResultKind::UnclosedActiveSection,
+ Self::InvalidAdvBuilderHandle => {
+ CreateV1SectionBuilderResultKind::InvalidAdvBuilderHandle
+ }
+ Self::IdentityKindMismatch => CreateV1SectionBuilderResultKind::IdentityKindMismatch,
+ Self::NoSpaceLeft => CreateV1SectionBuilderResultKind::NoSpaceLeft,
+ }
+ }
+}
+
+impl CreateV1SectionBuilderResult {
+ declare_enum_cast! {into_success, Success, V1SectionBuilder}
+}
+
+impl From<SectionBuilderError> for CreateV1SectionBuilderResult {
+ fn from(err: SectionBuilderError) -> Self {
+ match err {
+ SectionBuilderError::UnclosedActiveSection => Self::UnclosedActiveSection,
+ SectionBuilderError::IdentityKindMismatch => Self::IdentityKindMismatch,
+ SectionBuilderError::NoSpaceLeft => Self::NoSpaceLeft,
+ }
+ }
+}
+
+/// Result code for [`V1SectionBuilder#add_to_advertisement`].
+#[derive(Clone, Copy)]
+#[repr(u8)]
+pub enum AddV1SectionToAdvertisementResult {
+ /// The section referenced by the given handle
+ /// couldn't be added to the containing advertisement,
+ /// possibly because the handle is invalid or the section
+ /// has already been added to the containing section.
+ Error = 0,
+ /// The section referenced by the given handle
+ /// was successfully added to the containing advertisement.
+ /// After obtaining this result code, the section
+ /// handle will no longer be valid.
+ Success = 1,
+}
+
+/// Result code for operations adding DEs to a section builder.
+#[derive(Clone, Copy)]
+#[repr(u8)]
+pub enum AddV1DEResult {
+ /// The DE was successfully added to the section builder
+ /// behind the given handle.
+ Success = 0,
+ /// The handle for the section builder was invalid.
+ InvalidSectionHandle = 1,
+ /// There was no more space left in the advertisement
+ /// to fit the DE in the containing section.
+ InsufficientSectionSpace = 2,
+ /// The data element itself had invalid characteristics,
+ /// most likely a length above 127.
+ InvalidDataElement = 3,
+}
+
+/// Discriminant for `NextV1DE16ByteSaltResult`.
+#[derive(Clone, Copy)]
+#[repr(u8)]
+pub enum NextV1DE16ByteSaltResultKind {
+ /// We couldn't return a 16-byte DE salt, possibly
+ /// because the handle to the section builder
+ /// was invalid, or possibly because the section
+ /// builder was for a public section.
+ Error = 0,
+ /// A 16-byte DE salt was returned successfully.
+ Success = 1,
+}
+
+/// The result of attempting to get the derived V1 DE
+/// 16-byte salt for the next-added DE to the section
+/// builder behind the given handle.
+#[derive(Clone, Copy)]
+#[repr(C)]
+#[allow(missing_docs)]
+pub enum NextV1DE16ByteSaltResult {
+ Error,
+ Success([u8; 16]),
+}
+
+impl FfiEnum for NextV1DE16ByteSaltResult {
+ type Kind = NextV1DE16ByteSaltResultKind;
+ fn kind(&self) -> Self::Kind {
+ match self {
+ Self::Error => NextV1DE16ByteSaltResultKind::Error,
+ Self::Success(_) => NextV1DE16ByteSaltResultKind::Success,
+ }
+ }
+}
+
+impl NextV1DE16ByteSaltResult {
+ declare_enum_cast! {into_success, Success, [u8; 16] }
+}
+
+impl From<Option<np_adv::extended::serialize::DeSalt<CryptoProviderImpl>>>
+ for NextV1DE16ByteSaltResult
+{
+ fn from(maybe_salt: Option<np_adv::extended::serialize::DeSalt<CryptoProviderImpl>>) -> Self {
+ match maybe_salt.and_then(|salt| salt.derive::<16>()) {
+ Some(salt) => NextV1DE16ByteSaltResult::Success(salt),
+ None => NextV1DE16ByteSaltResult::Error,
+ }
+ }
+}
+
+/// A handle to a builder for V1 sections.
+#[derive(Clone, Copy)]
+#[repr(C)]
+pub struct V1SectionBuilder {
+ adv_builder: V1AdvertisementBuilder,
+ section_index: u8,
+}
+
+impl V1SectionBuilder {
+ /// Attempts to add the section constructed behind this handle
+ /// to a section builder to the containing advertisement it
+ /// originated from.
+ pub fn add_to_advertisement(self) -> AddV1SectionToAdvertisementResult {
+ match self.adv_builder.handle.get_mut() {
+ Ok(mut adv_builder) => {
+ let state = adv_builder.state.take();
+ match state {
+ Some(V1AdvertisementBuilderState::Section(section_builder)) => {
+ // Make sure the index of the section we're trying to close
+ // matches the index of the section currently under construction.
+ let actual_section_index = section_builder.section_index() as u8;
+ if self.section_index == actual_section_index {
+ let updated_adv_builder = section_builder.add_to_advertisement();
+ adv_builder.state = Some(V1AdvertisementBuilderState::Advertisement(
+ updated_adv_builder,
+ ));
+ AddV1SectionToAdvertisementResult::Success
+ } else {
+ adv_builder.state =
+ Some(V1AdvertisementBuilderState::Section(section_builder));
+ AddV1SectionToAdvertisementResult::Error
+ }
+ }
+ x => {
+ adv_builder.state = x;
+ AddV1SectionToAdvertisementResult::Error
+ }
+ }
+ }
+ Err(_) => AddV1SectionToAdvertisementResult::Error,
+ }
+ }
+
+ /// Attempts to get the derived 16-byte V1 DE salt for the next
+ /// DE to be added to this section builder. May fail if this
+ /// section builder handle is invalid, or if the section
+ /// is a public section.
+ pub fn next_de_salt(&self) -> NextV1DE16ByteSaltResult {
+ self.try_apply_to_internals(
+ |section_builder| section_builder.next_de_salt().into(),
+ NextV1DE16ByteSaltResult::Error,
+ )
+ }
+
+ /// Attempts to add the given DE to the section builder behind
+ /// this handle. The passed DE may have a payload of up to 127
+ /// bytes, the maximum for a V1 DE.
+ pub fn add_127_byte_buffer_de(&self, de: V1DE127ByteBuffer) -> AddV1DEResult {
+ match de.into_internal() {
+ Some(generic_de) => self
+ .add_de_internals(np_adv_dynamic::extended::BoxedWriteDataElement::new(generic_de)),
+ None => AddV1DEResult::InvalidDataElement,
+ }
+ }
+
+ fn add_de_internals(
+ &self,
+ de: np_adv_dynamic::extended::BoxedWriteDataElement,
+ ) -> AddV1DEResult {
+ self.try_apply_to_internals(
+ move |section_builder| match section_builder.add_de(move |_| de) {
+ Ok(_) => AddV1DEResult::Success,
+ Err(_) => AddV1DEResult::InsufficientSectionSpace,
+ },
+ AddV1DEResult::InvalidSectionHandle,
+ )
+ }
+
+ fn try_apply_to_internals<R>(
+ &self,
+ func: impl FnOnce(
+ &mut np_adv_dynamic::extended::BoxedSectionBuilder<
+ np_adv::extended::serialize::AdvBuilder,
+ CryptoProviderImpl,
+ >,
+ ) -> R,
+ invalid_handle_error: R,
+ ) -> R {
+ match self.adv_builder.handle.get_mut() {
+ Ok(mut adv_builder) => {
+ match adv_builder.state.as_mut() {
+ Some(V1AdvertisementBuilderState::Section(ref mut section_builder)) => {
+ // Check to make sure that the section index matches, otherwise
+ // we have an invalid handle.
+ let current_section_index = section_builder.section_index() as u8;
+ if current_section_index == self.section_index {
+ func(section_builder)
+ } else {
+ invalid_handle_error
+ }
+ }
+ Some(V1AdvertisementBuilderState::Advertisement(_)) => invalid_handle_error,
+ None => invalid_handle_error,
+ }
+ }
+ Err(_) => invalid_handle_error,
+ }
+ }
+}
+
+/// Represents the contents of a V1 DE whose payload
+/// is stored in a buffer which may contain up to 127 bytes,
+/// which is the maximum for any V1 DE.
+///
+/// This representation is stable, and so you may directly
+/// reference this struct's fields if you wish.
+#[repr(C)]
+//TODO: Partial unification with `deserialize::v1::GenericV1DataElement`?
+pub struct V1DE127ByteBuffer {
+ /// The DE type code of this generic data-element.
+ pub de_type: u32,
+ /// The raw data-element byte payload, up to
+ /// 127 bytes in length.
+ pub payload: ByteBuffer<127>,
+}
+
+impl V1DE127ByteBuffer {
+ /// Attempts to convert this FFI-friendly DE with a byte-buffer size of 127
+ /// to the internal representation of a generic DE. May fail in the case
+ /// where the underlying payload byte-buffer has an invalid length above 127.
+ fn into_internal(self) -> Option<np_adv::extended::data_elements::GenericDataElement> {
+ let de_type = np_adv::extended::de_type::DeType::from(self.de_type);
+ self.payload.as_slice().and_then(move |payload_slice| {
+ np_adv::extended::data_elements::GenericDataElement::try_from(de_type, payload_slice)
+ .ok()
+ })
+ }
+}
diff --git a/nearby/presence/np_ffi_core/src/utils.rs b/nearby/presence/np_ffi_core/src/utils.rs
index e547a89..b1495b9 100644
--- a/nearby/presence/np_ffi_core/src/utils.rs
+++ b/nearby/presence/np_ffi_core/src/utils.rs
@@ -37,6 +37,7 @@ pub trait FfiEnum {
///
/// If the enclosing enum turns out to not be the requested
/// variant, the generated method will return `None`.
+#[macro_export]
macro_rules! declare_enum_cast {
($projection_method_name:ident, $variant_enum_name:ident, $variant_type_name:ty) => {
#[doc = concat!("Attempts to cast `self` to the `", stringify!($variant_enum_name),
diff --git a/nearby/presence/np_ffi_core/src/v1.rs b/nearby/presence/np_ffi_core/src/v1.rs
new file mode 100644
index 0000000..c7b564d
--- /dev/null
+++ b/nearby/presence/np_ffi_core/src/v1.rs
@@ -0,0 +1,38 @@
+// 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.
+
+//! Common externally-acessible V1 constructs for both of the
+//! serialization+deserialization flows.
+
+/// 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,
+ }
+ }
+}
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 @@ version.workspace = true
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;
+
#[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 f7f4f28..94e6855 100644
--- a/nearby/presence/np_hkdf/src/v1_salt.rs
+++ b/nearby/presence/np_hkdf/src/v1_salt.rs
@@ -45,7 +45,7 @@ impl<C: CryptoProvider> V1Salt<C> {
&[
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(),
],
@@ -96,7 +96,7 @@ impl<C: CryptoProvider> fmt::Debug for V1Salt<C> {
#[derive(PartialEq, Eq, Debug, Clone, Copy, PartialOrd, Ord)]
pub struct DataElementOffset {
/// 0-based offset of the DE in the advertisement
- offset: usize,
+ offset: u8,
}
impl DataElementOffset {
@@ -104,7 +104,7 @@ impl DataElementOffset {
pub const ZERO: DataElementOffset = Self { offset: 0 };
/// Returns the offset as a usize
- pub fn as_usize(&self) -> usize {
+ pub fn as_u8(&self) -> u8 {
self.offset
}
@@ -116,8 +116,8 @@ impl DataElementOffset {
}
}
-impl From<usize> for DataElementOffset {
- fn from(num: usize) -> Self {
+impl From<u8> for DataElementOffset {
+ fn from(num: u8) -> Self {
Self { offset: num }
}
}
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 @@ fn hkdf_test_vectors() -> Result<(), anyhow::Error> {
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 @@ version.workspace = true
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 @@ version.workspace = true
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 @@ version.workspace = true
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 @@ pub fn load_data_file_contents_as_string(file: &str) -> String {
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 @@ pub fn parse_json_data_file(file: &str) -> serde_json::Value {
/// 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 d0b88b6..86745c4 100644
--- a/nearby/presence/xts_aes/Cargo.toml
+++ b/nearby/presence/xts_aes/Cargo.toml
@@ -4,6 +4,9 @@ version.workspace = true
edition.workspace = true
publish.workspace = true
+[lints]
+workspace = true
+
[features]
default = []
std = []
@@ -14,9 +17,10 @@ crypto_provider.workspace = true
ldt_tbc.workspace = true
[dev-dependencies]
-crypto_provider_default.workspace = true
+crypto_provider_default = { workspace = true, features = ["rustcrypto"] }
rand_ext.workspace = true
test_helper.workspace = true
+wycheproof.workspace = true
anyhow.workspace = true
base64.workspace = true
diff --git a/nearby/presence/xts_aes/fuzz/Cargo.lock b/nearby/presence/xts_aes/fuzz/Cargo.lock
index f0b7f56..a08ab7c 100644
--- a/nearby/presence/xts_aes/fuzz/Cargo.lock
+++ b/nearby/presence/xts_aes/fuzz/Cargo.lock
@@ -25,6 +25,20 @@ dependencies = [
]
[[package]]
+name = "aes-gcm"
+version = "0.10.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "831010a0f742e1209b3bcea8fab6a8e149051ba6099432c8cb2cc117dec3ead1"
+dependencies = [
+ "aead",
+ "aes",
+ "cipher",
+ "ctr",
+ "ghash",
+ "subtle",
+]
+
+[[package]]
name = "aes-gcm-siv"
version = "0.11.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -157,6 +171,9 @@ dependencies = [
[[package]]
name = "crypto_provider"
version = "0.1.0"
+dependencies = [
+ "tinyvec",
+]
[[package]]
name = "crypto_provider_rustcrypto"
@@ -164,6 +181,7 @@ version = "0.1.0"
dependencies = [
"aead",
"aes",
+ "aes-gcm",
"aes-gcm-siv",
"cbc",
"cfg-if",
@@ -193,9 +211,9 @@ dependencies = [
[[package]]
name = "curve25519-dalek"
-version = "4.0.0-rc.3"
+version = "4.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "436ace70fc06e06f7f689d2624dc4e2f0ea666efb5aa704215f7249ae6e047a7"
+checksum = "f711ade317dd348950a9910f81c5947e3d8907ebd2b83f76203ff1807e6a2bc2"
dependencies = [
"cfg-if",
"cpufeatures",
@@ -261,14 +279,15 @@ dependencies = [
[[package]]
name = "ed25519-dalek"
-version = "2.0.0-rc.3"
+version = "2.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "faa8e9049d5d72bfc12acbc05914731b5322f79b5e2f195e9f2d705fca22ab4c"
+checksum = "1f628eaec48bfd21b865dc2950cfa014450c01d2fa2b69a86c2fd5844ec523c0"
dependencies = [
"curve25519-dalek",
"ed25519",
"rand_core",
"sha2",
+ "subtle",
]
[[package]]
@@ -329,6 +348,16 @@ dependencies = [
]
[[package]]
+name = "ghash"
+version = "0.5.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d930750de5717d2dd0b8c0d42c076c0e884c81a73e6cab859bbd2339c71e3e40"
+dependencies = [
+ "opaque-debug",
+ "polyval",
+]
+
+[[package]]
name = "group"
version = "0.13.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -531,9 +560,9 @@ checksum = "b0293b4b29daaf487284529cc2f5675b8e57c61f70167ba415a463651fd6a918"
[[package]]
name = "sha2"
-version = "0.10.7"
+version = "0.10.8"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "479fb9d862239e610720565ca91403019f2f00410f1864c5aa7479b950a76ed8"
+checksum = "793db75ad2bcafc3ffa7c68b215fee268f537982cd901d132f89c6343f3a3dc8"
dependencies = [
"cfg-if",
"cpufeatures",
@@ -564,6 +593,12 @@ dependencies = [
]
[[package]]
+name = "tinyvec"
+version = "1.6.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "87cc5ceb3875bb20c2890005a4e226a4651264a5c75edb2421b52861a0a0cb50"
+
+[[package]]
name = "typenum"
version = "1.16.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -599,9 +634,9 @@ checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423"
[[package]]
name = "x25519-dalek"
-version = "2.0.0-rc.3"
+version = "2.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "ec7fae07da688e17059d5886712c933bb0520f15eff2e09cfa18e30968f4e63a"
+checksum = "fb66477291e7e8d2b0ff1bcb900bf29489a9692816d79874bea351e7a8b6de96"
dependencies = [
"curve25519-dalek",
"rand_core",
diff --git a/nearby/presence/xts_aes/src/lib.rs b/nearby/presence/xts_aes/src/lib.rs
index 0d8d1fd..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;
@@ -34,7 +26,7 @@ use array_ref::{array_mut_ref, array_ref};
use core::fmt;
use core::marker::PhantomData;
-use crypto_provider::aes::{Aes, AesCipher, AesDecryptCipher, AesEncryptCipher};
+use crypto_provider::aes::{Aes, AesBlock, AesCipher, AesDecryptCipher, AesEncryptCipher};
use crypto_provider::{
aes::{AesKey, BLOCK_SIZE},
CryptoProvider,
@@ -58,7 +50,7 @@ mod tweak_tests;
/// little endian bytes (e.g. with `u128::to_le_bytes()`).
///
/// Inside each data unit, there are one or more 16-byte blocks. The length of a data unit SHOULD
-/// not to exceed 2^20 blocks (16GiB) in order to maintain security properties; see
+/// not to exceed 2^20 blocks (16MB) in order to maintain security properties; see
/// [appendix D](https://luca-giuzzi.unibs.it/corsi/Support/papers-cryptography/1619-2007-NIST-Submission.pdf).
/// The last block (if there is more than one block) may have fewer than 16 bytes, in which case
/// ciphertext stealing will be applied.
@@ -104,7 +96,6 @@ pub struct XtsDecrypter<A: Aes<Key = K::BlockCipherKey>, K: XtsKey> {
}
#[allow(clippy::expect_used)]
-#[allow(clippy::indexing_slicing)]
impl<A: Aes<Key = K::BlockCipherKey>, K: XtsKey> XtsEncrypter<A, K> {
/// Encrypt a data unit in place, using sequential block numbers for each block.
/// `data_unit` must be at least [BLOCK_SIZE] bytes, and fewer than
@@ -118,20 +109,24 @@ impl<A: Aes<Key = K::BlockCipherKey>, K: XtsKey> XtsEncrypter<A, K> {
standalone_blocks.chunks_mut(BLOCK_SIZE).for_each(|block| {
// Until array_chunks is stabilized, using a macro to get array refs out of slice chunks
// Won't panic because we are only processing complete blocks
+ #[allow(clippy::indexing_slicing)]
tweaked_xts.encrypt_block(array_mut_ref!(block, 0, BLOCK_SIZE));
tweaked_xts.advance_to_next_block_num();
});
if partial_last_block.is_empty() {
+ #[allow(clippy::indexing_slicing)]
tweaked_xts.encrypt_block(array_mut_ref!(last_complete_block, 0, BLOCK_SIZE));
// nothing to do for the last block since it's empty
} else {
+ // This implements ciphertext stealing for the last two blocks which allows processing
+ // of a message length not divisible by block size without any expansion or padding.
// b is in bytes not bits; we do not consider partial bytes
let b = partial_last_block.len();
// Per spec: CC = encrypt P_{m-1} (the last complete block)
let cc = {
- let mut last_complete_block_plaintext: crypto_provider::aes::AesBlock =
+ let mut last_complete_block_plaintext: AesBlock =
last_complete_block.try_into().expect("complete block");
tweaked_xts.encrypt_block(&mut last_complete_block_plaintext);
tweaked_xts.advance_to_next_block_num();
@@ -139,7 +134,7 @@ impl<A: Aes<Key = K::BlockCipherKey>, K: XtsKey> XtsEncrypter<A, K> {
};
// Copy b bytes of P_m before we overwrite them with C_m
- let mut partial_last_block_plaintext: crypto_provider::aes::AesBlock = [0; BLOCK_SIZE];
+ let mut partial_last_block_plaintext: AesBlock = [0; BLOCK_SIZE];
partial_last_block_plaintext
.get_mut(0..b)
.expect("this should never be hit, since a partial block is always smaller than a complete block")
@@ -148,7 +143,7 @@ impl<A: Aes<Key = K::BlockCipherKey>, K: XtsKey> XtsEncrypter<A, K> {
// C_m = first b bytes of CC
partial_last_block.copy_from_slice(
cc.get(0..b)
- .expect("partial block len should always be less than a complete block"),
+ .expect("b is the len of partial_last_block so it will always be in bounds"),
);
// PP = P_m || last (16 - b) bytes of CC
@@ -186,7 +181,6 @@ impl<A: Aes<Key = K::BlockCipherKey>, K: XtsKey> XtsEncrypter<A, K> {
}
#[allow(clippy::expect_used)]
-#[allow(clippy::indexing_slicing)]
impl<A: Aes<Key = K::BlockCipherKey>, K: XtsKey> XtsDecrypter<A, K> {
/// Decrypt a data unit in place, using sequential block numbers for each block.
/// `data_unit` must be at least [BLOCK_SIZE] bytes, and fewer than
@@ -198,11 +192,13 @@ impl<A: Aes<Key = K::BlockCipherKey>, K: XtsKey> XtsDecrypter<A, K> {
let mut tweaked_xts = self.tweaked(tweak);
standalone_blocks.chunks_mut(BLOCK_SIZE).for_each(|block| {
+ #[allow(clippy::indexing_slicing)]
tweaked_xts.decrypt_block(array_mut_ref!(block, 0, BLOCK_SIZE));
tweaked_xts.advance_to_next_block_num();
});
if partial_last_block.is_empty() {
+ #[allow(clippy::indexing_slicing)]
tweaked_xts.decrypt_block(array_mut_ref!(last_complete_block, 0, BLOCK_SIZE));
} else {
let b = partial_last_block.len();
@@ -215,7 +211,7 @@ impl<A: Aes<Key = K::BlockCipherKey>, K: XtsKey> XtsDecrypter<A, K> {
tweaked_xts.advance_to_next_block_num();
// Block num is now at m
// We need C_m later, so make a copy to avoid overwriting
- let mut last_complete_block_ciphertext: crypto_provider::aes::AesBlock =
+ let mut last_complete_block_ciphertext: AesBlock =
last_complete_block.try_into().expect("complete block");
tweaked_xts.decrypt_block(&mut last_complete_block_ciphertext);
tweaked_xts.set_tweak(tweak_state_m_1);
@@ -223,21 +219,40 @@ impl<A: Aes<Key = K::BlockCipherKey>, K: XtsKey> XtsDecrypter<A, K> {
};
// Copy b bytes of C_m before we overwrite them with P_m
- let mut partial_last_block_ciphertext: crypto_provider::aes::AesBlock = [0; BLOCK_SIZE];
- partial_last_block_ciphertext[0..b].copy_from_slice(partial_last_block);
+ let mut partial_last_block_ciphertext: AesBlock = [0; BLOCK_SIZE];
+ partial_last_block_ciphertext
+ .get_mut(0..b)
+ .expect("this should never be hit, since a partial block is always smaller than a complete block")
+ .copy_from_slice(partial_last_block);
// P_m = first b bytes of PP
- partial_last_block.copy_from_slice(&pp[0..b]);
+ partial_last_block.copy_from_slice(
+ pp.get(0..b)
+ .expect("b is the len of partial_last_block so it will always be in bounds"),
+ );
// CC = C_m | CP (last 16-b bytes of PP)
let cc = {
- let cp = &pp[b..];
- last_complete_block[0..b].copy_from_slice(&partial_last_block_ciphertext[0..b]);
- last_complete_block[b..].copy_from_slice(cp);
+ let cp = pp.get(b..).expect(
+ "b is in bounds since a partial block is always smaller than a complete block",
+ );
+ last_complete_block
+ .get_mut(0..b)
+ .expect("partial block length within bounds of complete block")
+ .copy_from_slice(
+ partial_last_block_ciphertext
+ .get(0..b)
+ .expect("partial block length within bounds of complete block"),
+ );
+ last_complete_block
+ .get_mut(b..)
+ .expect("partial block length within bounds of complete block")
+ .copy_from_slice(cp);
last_complete_block
};
// decrypting at block num = m -1
+ #[allow(clippy::indexing_slicing)]
tweaked_xts.decrypt_block(array_mut_ref!(cc, 0, BLOCK_SIZE));
}
@@ -289,10 +304,10 @@ impl<A: Aes<Key = K::BlockCipherKey>, K: XtsKey + TweakableBlockCipherKey>
}
#[allow(clippy::expect_used)]
- fn encrypt(&self, tweak: Self::Tweak, block: &mut [u8; 16]) {
- // we're encrypting precisely one block, so the block number won't advance, and ciphertext
- // stealing will not be applied.
- self.encrypt_data_unit(tweak, block).expect("One block is a valid size");
+ fn encrypt(&self, tweak: Self::Tweak, block: &mut [u8; BLOCK_SIZE]) {
+ // We're encrypting precisely one block, so the block number won't advance, and there is no
+ // need to apply ciphertext stealing
+ self.tweaked(tweak).encrypt_block(block)
}
}
@@ -311,8 +326,9 @@ impl<A: Aes<Key = K::BlockCipherKey>, K: XtsKey + TweakableBlockCipherKey>
}
#[allow(clippy::expect_used)]
- fn decrypt(&self, tweak: Self::Tweak, block: &mut [u8; 16]) {
- self.decrypt_data_unit(tweak, block).expect("One block is a valid size");
+ fn decrypt(&self, tweak: Self::Tweak, block: &mut [u8; BLOCK_SIZE]) {
+ // We don't need the ciphertext stealing here since the input size is always exactly one block
+ self.tweaked(tweak).decrypt_block(block)
}
}
@@ -341,6 +357,8 @@ pub trait XtsKey: for<'a> TryFrom<&'a [u8], Error = Self::TryFromError> {
fn key_2(&self) -> &Self::BlockCipherKey;
}
+const AES_128_KEY_SIZE: usize = 16;
+
/// An XTS-AES-128 key.
pub struct XtsAes128Key {
key_1: <Self as XtsKey>::BlockCipherKey,
@@ -364,7 +382,7 @@ impl TryFrom<&[u8]> for XtsAes128Key {
type Error = XtsKeyTryFromSliceError;
fn try_from(slice: &[u8]) -> Result<Self, Self::Error> {
- try_split_concat_key::<16>(slice)
+ try_split_concat_key::<AES_128_KEY_SIZE>(slice)
.map(|(key_1, key_2)| Self { key_1: key_1.into(), key_2: key_2.into() })
.ok_or_else(XtsKeyTryFromSliceError::new)
}
@@ -373,8 +391,10 @@ impl TryFrom<&[u8]> for XtsAes128Key {
#[allow(clippy::expect_used)]
impl From<&[u8; 32]> for XtsAes128Key {
fn from(array: &[u8; 32]) -> Self {
- let arr1: [u8; 16] = array[..16].try_into().expect("array is correctly sized");
- let arr2: [u8; 16] = array[16..].try_into().expect("array is correctly sized");
+ let arr1: [u8; AES_128_KEY_SIZE] =
+ array[..AES_128_KEY_SIZE].try_into().expect("array is correctly sized");
+ let arr2: [u8; AES_128_KEY_SIZE] =
+ array[AES_128_KEY_SIZE..].try_into().expect("array is correctly sized");
Self {
key_1: crypto_provider::aes::Aes128Key::from(arr1),
key_2: crypto_provider::aes::Aes128Key::from(arr2),
@@ -402,6 +422,8 @@ impl TweakableBlockCipherKey for XtsAes128Key {
}
}
+const AES_256_KEY_SIZE: usize = 32;
+
/// An XTS-AES-256 key.
pub struct XtsAes256Key {
key_1: <Self as XtsKey>::BlockCipherKey,
@@ -425,7 +447,7 @@ impl TryFrom<&[u8]> for XtsAes256Key {
type Error = XtsKeyTryFromSliceError;
fn try_from(slice: &[u8]) -> Result<Self, Self::Error> {
- try_split_concat_key::<32>(slice)
+ try_split_concat_key::<AES_256_KEY_SIZE>(slice)
.map(|(key_1, key_2)| Self { key_1: key_1.into(), key_2: key_2.into() })
.ok_or_else(XtsKeyTryFromSliceError::new)
}
@@ -434,8 +456,10 @@ impl TryFrom<&[u8]> for XtsAes256Key {
#[allow(clippy::expect_used)]
impl From<&[u8; 64]> for XtsAes256Key {
fn from(array: &[u8; 64]) -> Self {
- let arr1: [u8; 32] = array[..32].try_into().expect("array is correctly sized");
- let arr2: [u8; 32] = array[32..].try_into().expect("array is correctly sized");
+ let arr1: [u8; AES_256_KEY_SIZE] =
+ array[..AES_256_KEY_SIZE].try_into().expect("array is correctly sized");
+ let arr2: [u8; AES_256_KEY_SIZE] =
+ array[AES_256_KEY_SIZE..].try_into().expect("array is correctly sized");
Self {
key_1: crypto_provider::aes::Aes256Key::from(arr1),
key_2: crypto_provider::aes::Aes256Key::from(arr2),
@@ -478,18 +502,18 @@ impl XtsKeyTryFromSliceError {
/// The tweak for an XTS-AES cipher.
#[derive(Clone)]
pub struct Tweak {
- bytes: crypto_provider::aes::AesBlock,
+ bytes: AesBlock,
}
impl Tweak {
/// Little-endian content of the tweak.
- pub fn le_bytes(&self) -> crypto_provider::aes::AesBlock {
+ pub fn le_bytes(&self) -> AesBlock {
self.bytes
}
}
-impl From<crypto_provider::aes::AesBlock> for Tweak {
- fn from(bytes: crypto_provider::aes::AesBlock) -> Self {
+impl From<AesBlock> for Tweak {
+ fn from(bytes: AesBlock) -> Self {
Self { bytes }
}
}
@@ -506,15 +530,31 @@ pub(crate) struct TweakState {
/// The block number inside the data unit. Should not exceed 2^20.
block_num: u32,
/// Original tweak multiplied by the primitive polynomial `block_num` times as per section 5.2
- tweak: crypto_provider::aes::AesBlock,
+ tweak: AesBlock,
}
impl TweakState {
/// Create a TweakState from the provided state with block_num = 0.
- fn new(tweak: [u8; 16]) -> TweakState {
+ fn new(tweak: [u8; BLOCK_SIZE]) -> TweakState {
TweakState { block_num: 0, tweak }
}
+ /// Advance the tweak state in the data unit to the next block without encrypting
+ /// or decrypting the intermediate blocks.
+ #[allow(clippy::indexing_slicing)]
+ fn advance_to_next_block(&mut self) {
+ // Conceptual left shift across the bytes.
+ // Most significant byte: if shift would carry, XOR in the coefficients of primitive
+ // polynomial in F_2^128 (x^128 = x^7 + x^2 + x + 1) => 0b1000_0111 in binary.
+ let mut target = [0_u8; BLOCK_SIZE];
+ target[0] = (self.tweak[0] << 1) ^ ((self.tweak[BLOCK_SIZE - 1] >> 7) * 0b1000_0111);
+ for (j, byte) in target.iter_mut().enumerate().skip(1) {
+ *byte = (self.tweak[j] << 1) ^ (self.tweak[j - 1] >> 7);
+ }
+ self.tweak = target;
+ self.block_num += 1;
+ }
+
/// Advance the tweak state in the data unit to the `block_num`'th block without encrypting
/// or decrypting the intermediate blocks.
///
@@ -522,39 +562,17 @@ impl TweakState {
///
/// # Panics
/// - If `block_num` is less than the current block num
+ #[cfg(test)]
fn advance_to_block(&mut self, block_num: u32) {
// It's a programmer error; nothing to recover from
assert!(self.block_num <= block_num);
- let mut target = [0_u8; BLOCK_SIZE];
-
// Multiply by the primitive polynomial as many times as needed, as per section 5.2
// of IEEE spec
- #[allow(clippy::expect_used)]
+ #[allow(clippy::indexing_slicing)]
for _ in 0..(block_num - self.block_num) {
- // Conceptual left shift across the bytes.
- // Most significant byte: if shift would carry, XOR in the coefficients of primitive
- // polynomial in F_2^128 (x^128 = x^7 + x^2 + x + 1 = 0) = 135 decimal.
- // % 128 is compiled as & !128 (i.e. fast).
- target[0] = (2
- * (self.tweak.first().expect("aes block must have non zero length") % 128))
- ^ (135
- * select_hi_bit(
- *self.tweak.get(15).expect("15 is a valid index in an aes block"),
- ));
- // Remaining bytes
- for (j, byte) in target.iter_mut().enumerate().skip(1) {
- *byte = (2
- * (self.tweak.get(j).expect("j is always in range of block size") % 128))
- ^ select_hi_bit(
- *self.tweak.get(j - 1).expect("j > 0 always because of the .skip(1)"),
- );
- }
- self.tweak = target;
- // no need to zero target as it will be overwritten completely next iteration
+ self.advance_to_next_block()
}
-
- self.block_num = block_num;
}
}
@@ -570,11 +588,11 @@ struct XtsEncrypterTweaked<'a, A: Aes> {
impl<'a, A: Aes> XtsEncrypterTweaked<'a, A> {
fn advance_to_next_block_num(&mut self) {
- self.tweak_state.advance_to_block(self.tweak_state.block_num + 1)
+ self.tweak_state.advance_to_next_block()
}
/// Encrypt a block in place using the configured tweak and current block number.
- fn encrypt_block(&self, block: &mut crypto_provider::aes::AesBlock) {
+ fn encrypt_block(&self, block: &mut AesBlock) {
array_xor(block, &self.tweak_state.tweak);
self.enc_cipher.encrypt(block);
array_xor(block, &self.tweak_state.tweak);
@@ -593,7 +611,7 @@ struct XtsDecrypterTweaked<'a, A: Aes> {
impl<'a, A: Aes> XtsDecrypterTweaked<'a, A> {
fn advance_to_next_block_num(&mut self) {
- self.tweak_state.advance_to_block(self.tweak_state.block_num + 1)
+ self.tweak_state.advance_to_next_block()
}
/// Get the current tweak state -- useful if needed to reset to an earlier block num.
@@ -605,7 +623,7 @@ impl<'a, A: Aes> XtsDecrypterTweaked<'a, A> {
fn set_tweak(&mut self, tweak_state: TweakState) {
self.tweak_state = tweak_state;
}
- fn decrypt_block(&self, block: &mut crypto_provider::aes::AesBlock) {
+ fn decrypt_block(&self, block: &mut AesBlock) {
// CC = C ^ T
array_xor(block, &self.tweak_state.tweak);
// PP = decrypt CC
@@ -617,7 +635,7 @@ impl<'a, A: Aes> XtsDecrypterTweaked<'a, A> {
/// Calculate `base = base ^ rhs` for each byte.
#[allow(clippy::expect_used)]
-fn array_xor(base: &mut crypto_provider::aes::AesBlock, rhs: &crypto_provider::aes::AesBlock) {
+fn array_xor(base: &mut AesBlock, rhs: &AesBlock) {
// hopefully this gets done smartly by the compiler (intel pxor, arm veorq, or equivalent).
// This seems to happen in practice at opt level 3: https://gcc.godbolt.org/z/qvjE8joMv
for i in 0..BLOCK_SIZE {
@@ -626,12 +644,6 @@ fn array_xor(base: &mut crypto_provider::aes::AesBlock, rhs: &crypto_provider::a
}
}
-/// 1 if hi bit set, 0 if not.
-fn select_hi_bit(byte: u8) -> u8 {
- // compiled as shr 7: https://gcc.godbolt.org/z/1rzvfshnx
- byte / 128
-}
-
fn try_split_concat_key<const N: usize>(slice: &[u8]) -> Option<([u8; N], [u8; N])> {
slice.get(0..N).and_then(|slice| slice.try_into().ok()).and_then(|k1: [u8; N]| {
slice.get(N..).and_then(|slice| slice.try_into().ok()).map(|k2: [u8; N]| (k1, k2))
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/tests.rs b/nearby/presence/xts_aes/tests/tests.rs
new file mode 100644
index 0000000..0452f06
--- /dev/null
+++ b/nearby/presence/xts_aes/tests/tests.rs
@@ -0,0 +1,117 @@
+// Copyright 2022 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::aes::{Aes, BLOCK_SIZE};
+use crypto_provider::CryptoProvider;
+use crypto_provider_default::CryptoProviderImpl;
+use ldt_tbc::{TweakableBlockCipherDecrypter, TweakableBlockCipherEncrypter};
+use xts_aes::{XtsAes128Key, XtsAes256Key, XtsDecrypter, XtsEncrypter, XtsError, XtsKey};
+
+const MAX_XTS_SIZE: usize = (1 << 20) * BLOCK_SIZE;
+
+#[test]
+fn too_small_payload() {
+ let key = [0_u8; 32];
+ let (enc, dec) = build_ciphers::<<CryptoProviderImpl as CryptoProvider>::Aes128, _>(
+ &XtsAes128Key::from(&key),
+ );
+ let tweak = [0u8; 16];
+ let mut payload = [0u8; BLOCK_SIZE - 1];
+ assert_eq!(enc.encrypt_data_unit(tweak.into(), &mut payload), Err(XtsError::DataTooShort));
+ assert_eq!(dec.decrypt_data_unit(tweak.into(), &mut payload), Err(XtsError::DataTooShort));
+
+ let key = [0u8; 64];
+ let (enc, dec) = build_ciphers::<<CryptoProviderImpl as CryptoProvider>::Aes256, _>(
+ &XtsAes256Key::from(&key),
+ );
+ assert_eq!(enc.encrypt_data_unit(tweak.into(), &mut payload), Err(XtsError::DataTooShort));
+ assert_eq!(dec.decrypt_data_unit(tweak.into(), &mut payload), Err(XtsError::DataTooShort));
+}
+
+#[test]
+fn block_size_payload() {
+ let key = [0_u8; 32];
+ let (enc, dec) = build_ciphers::<<CryptoProviderImpl as CryptoProvider>::Aes128, _>(
+ &XtsAes128Key::from(&key),
+ );
+ let tweak = [0u8; 16];
+ let mut payload = [0u8; BLOCK_SIZE];
+ assert!(enc.encrypt_data_unit(tweak.into(), &mut payload).is_ok());
+ assert!(dec.decrypt_data_unit(tweak.into(), &mut payload).is_ok());
+
+ let key = [0u8; 64];
+ let (enc, dec) = build_ciphers::<<CryptoProviderImpl as CryptoProvider>::Aes256, _>(
+ &XtsAes256Key::from(&key),
+ );
+ assert!(enc.encrypt_data_unit(tweak.into(), &mut payload).is_ok());
+ assert!(dec.decrypt_data_unit(tweak.into(), &mut payload).is_ok());
+}
+
+#[test]
+fn max_xts_sized_payload() {
+ let key = [0_u8; 32];
+ let (enc, dec) = build_ciphers::<<CryptoProviderImpl as CryptoProvider>::Aes128, _>(
+ &XtsAes128Key::from(&key),
+ );
+ let tweak = [0u8; 16];
+ let mut payload = vec![0u8; MAX_XTS_SIZE];
+ assert!(enc.encrypt_data_unit(tweak.into(), payload.as_mut_slice()).is_ok());
+ assert!(dec.decrypt_data_unit(tweak.into(), payload.as_mut_slice()).is_ok());
+
+ let key = [0u8; 64];
+ let (enc, dec) = build_ciphers::<<CryptoProviderImpl as CryptoProvider>::Aes256, _>(
+ &XtsAes256Key::from(&key),
+ );
+ assert!(enc.encrypt_data_unit(tweak.into(), payload.as_mut_slice()).is_ok());
+ assert!(dec.decrypt_data_unit(tweak.into(), payload.as_mut_slice()).is_ok());
+}
+
+#[test]
+fn too_large_payload() {
+ let key = [0_u8; 32];
+ let (enc, dec) = build_ciphers::<<CryptoProviderImpl as CryptoProvider>::Aes128, _>(
+ &XtsAes128Key::from(&key),
+ );
+ let tweak = [0u8; 16];
+ let mut payload = vec![0u8; MAX_XTS_SIZE + 1];
+ assert_eq!(
+ enc.encrypt_data_unit(tweak.into(), payload.as_mut_slice()),
+ Err(XtsError::DataTooLong)
+ );
+ assert_eq!(
+ dec.decrypt_data_unit(tweak.into(), payload.as_mut_slice()),
+ Err(XtsError::DataTooLong)
+ );
+
+ let key = [0u8; 64];
+ let (enc, dec) = build_ciphers::<<CryptoProviderImpl as CryptoProvider>::Aes256, _>(
+ &XtsAes256Key::from(&key),
+ );
+ assert_eq!(
+ enc.encrypt_data_unit(tweak.into(), payload.as_mut_slice()),
+ Err(XtsError::DataTooLong)
+ );
+ assert_eq!(
+ dec.decrypt_data_unit(tweak.into(), payload.as_mut_slice()),
+ Err(XtsError::DataTooLong)
+ );
+}
+
+fn build_ciphers<A: Aes<Key = K::BlockCipherKey>, K: XtsKey + ldt_tbc::TweakableBlockCipherKey>(
+ key: &K,
+) -> (XtsEncrypter<A, K>, XtsDecrypter<A, K>) {
+ let enc = XtsEncrypter::<A, _>::new(key);
+ let dec = XtsDecrypter::<A, _>::new(key);
+ (enc, dec)
+}
diff --git a/nearby/presence/xts_aes/tests/wycheproof_test_vectors.rs b/nearby/presence/xts_aes/tests/wycheproof_test_vectors.rs
new file mode 100644
index 0000000..1f92f89
--- /dev/null
+++ b/nearby/presence/xts_aes/tests/wycheproof_test_vectors.rs
@@ -0,0 +1,77 @@
+// Copyright 2022 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.
+
+#![allow(clippy::unwrap_used, clippy::expect_used, clippy::indexing_slicing)]
+
+use crypto_provider::CryptoProvider;
+use crypto_provider_default::CryptoProviderImpl;
+use ldt_tbc::TweakableBlockCipherDecrypter;
+use ldt_tbc::TweakableBlockCipherEncrypter;
+use ldt_tbc::TweakableBlockCipherKey;
+use wycheproof::cipher::TestGroup;
+use xts_aes::{self, XtsAes128Key, XtsAes256Key, XtsDecrypter, XtsEncrypter, XtsKey};
+
+#[test]
+fn run_wycheproof_vectors() {
+ run_test_vectors();
+}
+
+fn run_test_vectors() {
+ let test_set = wycheproof::cipher::TestSet::load(wycheproof::cipher::TestName::AesXts)
+ .expect("Should be able to load test set");
+
+ for test_group in test_set.test_groups {
+ match test_group.key_size {
+ 256 => run_tests::<XtsAes128Key, <CryptoProviderImpl as CryptoProvider>::Aes128>(
+ test_group,
+ ),
+ 512 => run_tests::<XtsAes256Key, <CryptoProviderImpl as CryptoProvider>::Aes256>(
+ test_group,
+ ),
+ _ => {}
+ }
+ }
+}
+
+fn run_tests<K, A>(test_group: TestGroup)
+where
+ K: XtsKey + TweakableBlockCipherKey,
+ A: crypto_provider::aes::Aes<Key = K::BlockCipherKey>,
+{
+ let mut buf = Vec::new();
+ for test in test_group.tests {
+ buf.clear();
+ let key = test.key.as_slice();
+ let iv = test.nonce.as_slice();
+ let msg = test.pt.as_slice();
+ let ct = test.ct.as_slice();
+
+ let mut block = [0u8; 16];
+ block[..iv.len()].copy_from_slice(iv);
+ let tweak = xts_aes::Tweak::from(block);
+
+ // test encryption
+ let xts_enc = XtsEncrypter::<A, _>::new(&K::try_from(key).unwrap());
+ buf.extend_from_slice(msg);
+ xts_enc.encrypt_data_unit(tweak.clone(), &mut buf).unwrap();
+ assert_eq!(ct, buf, "Test case id: {}", test.tc_id);
+
+ // test decryption
+ buf.clear();
+ let xts_dec = XtsDecrypter::<A, _>::new(&K::try_from(key).unwrap());
+ buf.extend_from_slice(ct);
+ xts_dec.decrypt_data_unit(tweak, &mut buf).unwrap();
+ assert_eq!(msg, buf, "Test case id: {}", test.tc_id);
+ }
+}
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 @@ impl<I: Iterator<Item = String>> Iterator for TestVectorFileIterator<I> {
// `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/rustfmt.toml b/nearby/rustfmt.toml
index 10c1698..d45a5bf 100644
--- a/nearby/rustfmt.toml
+++ b/nearby/rustfmt.toml
@@ -1,3 +1,3 @@
edition = "2021"
newline_style = "Unix"
-use_small_heuristics = "Max"
+use_small_heuristics = "Max" \ No newline at end of file
diff --git a/nearby/scripts/openssl-patches/0001-Apply-Android-changes.patch b/nearby/scripts/openssl-patches/0001-Apply-Android-changes.patch
deleted file mode 100644
index be6ca09..0000000
--- a/nearby/scripts/openssl-patches/0001-Apply-Android-changes.patch
+++ /dev/null
@@ -1,2712 +0,0 @@
-From 66e1daa9b4a345617bf9a090d56fa09ccd9b1d35 Mon Sep 17 00:00:00 2001
-From: Maurice Lam <yukl@google.com>
-Date: Wed, 26 Apr 2023 02:21:36 +0000
-Subject: [PATCH] Apply Android patches
-
----
- openssl/src/bio.rs | 6 +-
- openssl/src/bn.rs | 2 +-
- openssl/src/cipher.rs | 4 +
- openssl/src/dh.rs | 2 +-
- openssl/src/ec.rs | 20 +
- openssl/src/encrypt.rs | 4 +-
- openssl/src/hkdf.rs | 89 ++
- openssl/src/hmac.rs | 217 ++++
- openssl/src/lib.rs | 12 +
- openssl/src/pkey.rs | 20 +-
- openssl/src/sign.rs | 10 +-
- openssl/src/symm.rs | 7 +-
- openssl/src/x509/mod.rs | 52 +-
- openssl/src/x509/mod.rs.orig | 1879 ++++++++++++++++++++++++++++++++++
- 14 files changed, 2292 insertions(+), 32 deletions(-)
- create mode 100644 openssl/src/hkdf.rs
- create mode 100644 openssl/src/hmac.rs
- create mode 100644 openssl/src/x509/mod.rs.orig
-
-diff --git a/openssl/src/bio.rs b/openssl/src/bio.rs
-index 6a72552a..03242188 100644
---- a/openssl/src/bio.rs
-+++ b/openssl/src/bio.rs
-@@ -4,7 +4,7 @@ use std::marker::PhantomData;
- use std::ptr;
- use std::slice;
-
--use crate::cvt_p;
-+use crate::{cvt_p, SignedLenType};
- use crate::error::ErrorStack;
-
- pub struct MemBioSlice<'a>(*mut ffi::BIO, PhantomData<&'a [u8]>);
-@@ -25,7 +25,7 @@ impl<'a> MemBioSlice<'a> {
- let bio = unsafe {
- cvt_p(BIO_new_mem_buf(
- buf.as_ptr() as *const _,
-- buf.len() as c_int,
-+ buf.len() as SignedLenType,
- ))?
- };
-
-@@ -78,7 +78,7 @@ cfg_if! {
- use ffi::BIO_new_mem_buf;
- } else {
- #[allow(bad_style)]
-- unsafe fn BIO_new_mem_buf(buf: *const ::libc::c_void, len: ::libc::c_int) -> *mut ffi::BIO {
-+ unsafe fn BIO_new_mem_buf(buf: *const ::libc::c_void, len: SignedLenType) -> *mut ffi::BIO {
- ffi::BIO_new_mem_buf(buf as *mut _, len)
- }
- }
-diff --git a/openssl/src/bn.rs b/openssl/src/bn.rs
-index 0328730a..fe820a2c 100644
---- a/openssl/src/bn.rs
-+++ b/openssl/src/bn.rs
-@@ -814,7 +814,7 @@ impl BigNumRef {
- /// assert_eq!(&bn_vec, &[0, 0, 0x45, 0x43]);
- /// ```
- #[corresponds(BN_bn2binpad)]
-- #[cfg(ossl110)]
-+ #[cfg(any(boringssl, ossl110))]
- pub fn to_vec_padded(&self, pad_to: i32) -> Result<Vec<u8>, ErrorStack> {
- let mut v = Vec::with_capacity(pad_to as usize);
- unsafe {
-diff --git a/openssl/src/cipher.rs b/openssl/src/cipher.rs
-index aeedf459..570794ca 100644
---- a/openssl/src/cipher.rs
-+++ b/openssl/src/cipher.rs
-@@ -208,6 +208,7 @@ impl Cipher {
- unsafe { CipherRef::from_ptr(ffi::EVP_aes_192_cfb1() as *mut _) }
- }
-
-+ #[cfg(not(boringssl))]
- pub fn aes_192_cfb128() -> &'static CipherRef {
- unsafe { CipherRef::from_ptr(ffi::EVP_aes_192_cfb128() as *mut _) }
- }
-@@ -253,6 +254,7 @@ impl Cipher {
- unsafe { CipherRef::from_ptr(ffi::EVP_aes_256_cfb1() as *mut _) }
- }
-
-+ #[cfg(not(boringssl))]
- pub fn aes_256_cfb128() -> &'static CipherRef {
- unsafe { CipherRef::from_ptr(ffi::EVP_aes_256_cfb128() as *mut _) }
- }
-@@ -282,11 +284,13 @@ impl Cipher {
- }
-
- #[cfg(not(osslconf = "OPENSSL_NO_BF"))]
-+ #[cfg(not(boringssl))]
- pub fn bf_cbc() -> &'static CipherRef {
- unsafe { CipherRef::from_ptr(ffi::EVP_bf_cbc() as *mut _) }
- }
-
- #[cfg(not(osslconf = "OPENSSL_NO_BF"))]
-+ #[cfg(not(boringssl))]
- pub fn bf_ecb() -> &'static CipherRef {
- unsafe { CipherRef::from_ptr(ffi::EVP_bf_ecb() as *mut _) }
- }
-diff --git a/openssl/src/dh.rs b/openssl/src/dh.rs
-index 12170b99..e781543e 100644
---- a/openssl/src/dh.rs
-+++ b/openssl/src/dh.rs
-@@ -239,7 +239,7 @@ where
- }
-
- cfg_if! {
-- if #[cfg(any(ossl110, libressl270))] {
-+ if #[cfg(any(ossl110, libressl270, boringssl))] {
- use ffi::{DH_set0_pqg, DH_get0_pqg, DH_get0_key, DH_set0_key};
- } else {
- #[allow(bad_style)]
-diff --git a/openssl/src/ec.rs b/openssl/src/ec.rs
-index 248ced3e..c56f5da7 100644
---- a/openssl/src/ec.rs
-+++ b/openssl/src/ec.rs
-@@ -954,6 +954,26 @@ impl EcKey<Private> {
- EcKey<Private>,
- ffi::d2i_ECPrivateKey
- }
-+
-+ /// Decodes a DER-encoded elliptic curve private key structure for the specified curve.
-+ #[corresponds(EC_KEY_parse_private_key)]
-+ #[cfg(boringssl)]
-+ pub fn private_key_from_der_for_group(
-+ der: &[u8],
-+ group: &EcGroupRef,
-+ ) -> Result<EcKey<Private>, ErrorStack> {
-+ unsafe {
-+ let mut cbs = ffi::CBS {
-+ data: der.as_ptr(),
-+ len: der.len(),
-+ };
-+ cvt_p(ffi::EC_KEY_parse_private_key(
-+ &mut cbs as *mut ffi::CBS,
-+ group.as_ptr(),
-+ ))
-+ .map(|p| EcKey::from_ptr(p))
-+ }
-+ }
- }
-
- impl<T> Clone for EcKey<T> {
-diff --git a/openssl/src/encrypt.rs b/openssl/src/encrypt.rs
-index d3db0fd4..19a9a459 100644
---- a/openssl/src/encrypt.rs
-+++ b/openssl/src/encrypt.rs
-@@ -148,7 +148,7 @@ impl<'a> Encrypter<'a> {
- /// This corresponds to [`EVP_PKEY_CTX_set_rsa_oaep_md`].
- ///
- /// [`EVP_PKEY_CTX_set_rsa_oaep_md`]: https://www.openssl.org/docs/manmaster/man3/EVP_PKEY_CTX_set_rsa_oaep_md.html
-- #[cfg(any(ossl102, libressl310))]
-+ #[cfg(any(ossl102, libressl310, boringssl))]
- pub fn set_rsa_oaep_md(&mut self, md: MessageDigest) -> Result<(), ErrorStack> {
- unsafe {
- cvt(ffi::EVP_PKEY_CTX_set_rsa_oaep_md(
-@@ -352,7 +352,7 @@ impl<'a> Decrypter<'a> {
- /// This corresponds to [`EVP_PKEY_CTX_set_rsa_oaep_md`].
- ///
- /// [`EVP_PKEY_CTX_set_rsa_oaep_md`]: https://www.openssl.org/docs/manmaster/man3/EVP_PKEY_CTX_set_rsa_oaep_md.html
-- #[cfg(any(ossl102, libressl310))]
-+ #[cfg(any(ossl102, libressl310, boringssl))]
- pub fn set_rsa_oaep_md(&mut self, md: MessageDigest) -> Result<(), ErrorStack> {
- unsafe {
- cvt(ffi::EVP_PKEY_CTX_set_rsa_oaep_md(
-diff --git a/openssl/src/hkdf.rs b/openssl/src/hkdf.rs
-new file mode 100644
-index 00000000..cc7e5b3a
---- /dev/null
-+++ b/openssl/src/hkdf.rs
-@@ -0,0 +1,89 @@
-+use crate::cvt;
-+use crate::error::ErrorStack;
-+use crate::md::MdRef;
-+use foreign_types::ForeignTypeRef;
-+use openssl_macros::corresponds;
-+
-+/// Computes HKDF (as specified by RFC 5869).
-+///
-+/// HKDF is an Extract-and-Expand algorithm. It does not do any key stretching,
-+/// and as such, is not suited to be used alone to generate a key from a
-+/// password.
-+#[corresponds(HKDF)]
-+#[inline]
-+pub fn hkdf(
-+ out_key: &mut [u8],
-+ md: &MdRef,
-+ secret: &[u8],
-+ salt: &[u8],
-+ info: &[u8],
-+) -> Result<(), ErrorStack> {
-+ unsafe {
-+ cvt(ffi::HKDF(
-+ out_key.as_mut_ptr(),
-+ out_key.len(),
-+ md.as_ptr(),
-+ secret.as_ptr(),
-+ secret.len(),
-+ salt.as_ptr(),
-+ salt.len(),
-+ info.as_ptr(),
-+ info.len(),
-+ ))?;
-+ }
-+
-+ Ok(())
-+}
-+
-+/// Computes a HKDF PRK (as specified by RFC 5869).
-+///
-+/// WARNING: This function orders the inputs differently from RFC 5869
-+/// specification. Double-check which parameter is the secret/IKM and which is
-+/// the salt when using.
-+#[corresponds(HKDF_extract)]
-+#[inline]
-+pub fn hkdf_extract<'a>(
-+ out_key: &'a mut [u8],
-+ md: &MdRef,
-+ secret: &[u8],
-+ salt: &[u8],
-+) -> Result<&'a [u8], ErrorStack> {
-+ let mut out_len = out_key.len();
-+ unsafe {
-+ cvt(ffi::HKDF_extract(
-+ out_key.as_mut_ptr(),
-+ &mut out_len,
-+ md.as_ptr(),
-+ secret.as_ptr(),
-+ secret.len(),
-+ salt.as_ptr(),
-+ salt.len(),
-+ ))?;
-+ }
-+
-+ Ok(&out_key[..out_len])
-+}
-+
-+/// Computes a HKDF OKM (as specified by RFC 5869).
-+#[corresponds(HKDF_expand)]
-+#[inline]
-+pub fn hkdf_expand(
-+ out_key: &mut [u8],
-+ md: &MdRef,
-+ prk: &[u8],
-+ info: &[u8],
-+) -> Result<(), ErrorStack> {
-+ unsafe {
-+ cvt(ffi::HKDF_expand(
-+ out_key.as_mut_ptr(),
-+ out_key.len(),
-+ md.as_ptr(),
-+ prk.as_ptr(),
-+ prk.len(),
-+ info.as_ptr(),
-+ info.len(),
-+ ))?;
-+ }
-+
-+ Ok(())
-+}
-diff --git a/openssl/src/hmac.rs b/openssl/src/hmac.rs
-new file mode 100644
-index 00000000..465781e2
---- /dev/null
-+++ b/openssl/src/hmac.rs
-@@ -0,0 +1,217 @@
-+use crate::error::ErrorStack;
-+use crate::md::MdRef;
-+use crate::{cvt, cvt_p};
-+use ffi::HMAC_CTX;
-+use foreign_types::ForeignTypeRef;
-+use libc::{c_uint, c_void};
-+use openssl_macros::corresponds;
-+use std::convert::TryFrom;
-+use std::ptr;
-+
-+/// Computes the HMAC as a one-shot operation.
-+///
-+/// Calculates the HMAC of data, using the given |key|
-+/// and hash function |md|, and returns the result re-using the space from
-+/// buffer |out|. On entry, |out| must contain at least |EVP_MD_size| bytes
-+/// of space. The actual length of the result is used to resize the returned
-+/// slice. An output size of |EVP_MAX_MD_SIZE| will always be large enough.
-+/// It returns a resized |out| or ErrorStack on error.
-+#[corresponds(HMAC)]
-+#[inline]
-+pub fn hmac<'a>(
-+ md: &MdRef,
-+ key: &[u8],
-+ data: &[u8],
-+ out: &'a mut [u8],
-+) -> Result<&'a [u8], ErrorStack> {
-+ assert!(out.len() >= md.size());
-+ let mut out_len = c_uint::try_from(out.len()).unwrap();
-+ unsafe {
-+ cvt_p(ffi::HMAC(
-+ md.as_ptr(),
-+ key.as_ptr() as *const c_void,
-+ key.len(),
-+ data.as_ptr(),
-+ data.len(),
-+ out.as_mut_ptr(),
-+ &mut out_len,
-+ ))?;
-+ }
-+ Ok(&out[..out_len as usize])
-+}
-+
-+/// A context object used to perform HMAC operations.
-+///
-+/// HMAC is a MAC (message authentication code), i.e. a keyed hash function used for message
-+/// authentication, which is based on a hash function.
-+///
-+/// Note: Only available in boringssl. For openssl, use `PKey::hmac` instead.
-+#[cfg(boringssl)]
-+pub struct HmacCtx {
-+ ctx: *mut HMAC_CTX,
-+ output_size: usize,
-+}
-+
-+#[cfg(boringssl)]
-+impl HmacCtx {
-+ /// Creates a new [HmacCtx] to use the hash function `md` and key `key`.
-+ #[corresponds(HMAC_Init_ex)]
-+ pub fn new(key: &[u8], md: &MdRef) -> Result<Self, ErrorStack> {
-+ unsafe {
-+ // Safety: If an error occurred, the resulting null from HMAC_CTX_new is converted into
-+ // ErrorStack in the returned result by `cvt_p`.
-+ let ctx = cvt_p(ffi::HMAC_CTX_new())?;
-+ // Safety:
-+ // - HMAC_Init_ex must be called with a context previously created with HMAC_CTX_new,
-+ // which is the line above.
-+ // - HMAC_Init_ex may return an error if key is null but the md is different from
-+ // before. This is avoided here since key is guaranteed to be non-null.
-+ cvt(ffi::HMAC_Init_ex(
-+ ctx,
-+ key.as_ptr() as *const c_void,
-+ key.len(),
-+ md.as_ptr(),
-+ ptr::null_mut(),
-+ ))?;
-+ Ok(Self {
-+ ctx,
-+ output_size: md.size(),
-+ })
-+ }
-+ }
-+
-+ /// `update` can be called repeatedly with chunks of the message `data` to be authenticated.
-+ #[corresponds(HMAC_Update)]
-+ pub fn update(&mut self, data: &[u8]) -> Result<(), ErrorStack> {
-+ unsafe {
-+ // Safety: HMAC_Update returns 0 on error, and that is converted into ErrorStack in the
-+ // returned result by `cvt`.
-+ cvt(ffi::HMAC_Update(self.ctx, data.as_ptr(), data.len())).map(|_| ())
-+ }
-+ }
-+
-+ /// Finishes the HMAC process, and places the message authentication code in `output`.
-+ /// The number of bytes written to `output` is returned.
-+ ///
-+ /// # Panics
-+ ///
-+ /// Panics if the `output` is smaller than the required size. The output size is indicated by
-+ /// `md.size()` for the `Md` instance passed in [new]. An output size of |EVP_MAX_MD_SIZE| will
-+ /// always be large enough.
-+ #[corresponds(HMAC_Final)]
-+ pub fn finalize(&mut self, output: &mut [u8]) -> Result<usize, ErrorStack> {
-+ assert!(output.len() >= self.output_size);
-+ unsafe {
-+ // Safety: The length assertion above makes sure that `HMAC_Final` will not write longer
-+ // than the length of `output`.
-+ let mut size: c_uint = 0;
-+ cvt(ffi::HMAC_Final(
-+ self.ctx,
-+ output.as_mut_ptr(),
-+ &mut size as *mut c_uint,
-+ ))
-+ .map(|_| size as usize)
-+ }
-+ }
-+}
-+
-+impl Drop for HmacCtx {
-+ #[corresponds(HMAC_CTX_free)]
-+ fn drop(&mut self) {
-+ unsafe {
-+ ffi::HMAC_CTX_free(self.ctx);
-+ }
-+ }
-+}
-+
-+#[cfg(test)]
-+mod tests {
-+ use super::*;
-+ use crate::md::Md;
-+
-+ const SHA_256_DIGEST_SIZE: usize = 32;
-+
-+ #[test]
-+ fn hmac_sha256_test() {
-+ let expected_hmac = [
-+ 0xb0, 0x34, 0x4c, 0x61, 0xd8, 0xdb, 0x38, 0x53, 0x5c, 0xa8, 0xaf, 0xce, 0xaf, 0xb,
-+ 0xf1, 0x2b, 0x88, 0x1d, 0xc2, 0x0, 0xc9, 0x83, 0x3d, 0xa7, 0x26, 0xe9, 0x37, 0x6c,
-+ 0x2e, 0x32, 0xcf, 0xf7,
-+ ];
-+ let mut out: [u8; SHA_256_DIGEST_SIZE] = [0; SHA_256_DIGEST_SIZE];
-+ let key: [u8; 20] = [0x0b; 20];
-+ let data = b"Hi There";
-+ let hmac_result =
-+ hmac(Md::sha256(), &key, data, &mut out).expect("Couldn't calculate sha256 hmac");
-+ assert_eq!(&hmac_result, &expected_hmac);
-+ }
-+
-+ #[test]
-+ #[should_panic]
-+ fn hmac_sha256_output_too_short() {
-+ let mut out = vec![0_u8; 1];
-+ let key: [u8; 20] = [0x0b; 20];
-+ let data = b"Hi There";
-+ hmac(Md::sha256(), &key, data, &mut out).expect("Couldn't calculate sha256 hmac");
-+ }
-+
-+ #[test]
-+ fn hmac_sha256_test_big_buffer() {
-+ let expected_hmac = [
-+ 0xb0, 0x34, 0x4c, 0x61, 0xd8, 0xdb, 0x38, 0x53, 0x5c, 0xa8, 0xaf, 0xce, 0xaf, 0xb,
-+ 0xf1, 0x2b, 0x88, 0x1d, 0xc2, 0x0, 0xc9, 0x83, 0x3d, 0xa7, 0x26, 0xe9, 0x37, 0x6c,
-+ 0x2e, 0x32, 0xcf, 0xf7,
-+ ];
-+ let mut out: [u8; 100] = [0; 100];
-+ let key: [u8; 20] = [0x0b; 20];
-+ let data = b"Hi There";
-+ let hmac_result =
-+ hmac(Md::sha256(), &key, data, &mut out).expect("Couldn't calculate sha256 hmac");
-+ assert_eq!(hmac_result.len(), SHA_256_DIGEST_SIZE);
-+ assert_eq!(&hmac_result, &expected_hmac);
-+ }
-+
-+ #[test]
-+ fn hmac_sha256_update_test() {
-+ let expected_hmac = [
-+ 0xb0, 0x34, 0x4c, 0x61, 0xd8, 0xdb, 0x38, 0x53, 0x5c, 0xa8, 0xaf, 0xce, 0xaf, 0xb,
-+ 0xf1, 0x2b, 0x88, 0x1d, 0xc2, 0x0, 0xc9, 0x83, 0x3d, 0xa7, 0x26, 0xe9, 0x37, 0x6c,
-+ 0x2e, 0x32, 0xcf, 0xf7,
-+ ];
-+ let mut out: [u8; SHA_256_DIGEST_SIZE] = [0; SHA_256_DIGEST_SIZE];
-+ let key: [u8; 20] = [0x0b; 20];
-+ let data = b"Hi There";
-+ let mut hmac_ctx = HmacCtx::new(&key, Md::sha256()).unwrap();
-+ hmac_ctx.update(data).unwrap();
-+ let size = hmac_ctx.finalize(&mut out).unwrap();
-+ assert_eq!(&out, &expected_hmac);
-+ assert_eq!(size, SHA_256_DIGEST_SIZE);
-+ }
-+
-+ #[test]
-+ fn hmac_sha256_update_chunks_test() {
-+ let expected_hmac = [
-+ 0xb0, 0x34, 0x4c, 0x61, 0xd8, 0xdb, 0x38, 0x53, 0x5c, 0xa8, 0xaf, 0xce, 0xaf, 0xb,
-+ 0xf1, 0x2b, 0x88, 0x1d, 0xc2, 0x0, 0xc9, 0x83, 0x3d, 0xa7, 0x26, 0xe9, 0x37, 0x6c,
-+ 0x2e, 0x32, 0xcf, 0xf7,
-+ ];
-+ let mut out: [u8; SHA_256_DIGEST_SIZE] = [0; SHA_256_DIGEST_SIZE];
-+ let key: [u8; 20] = [0x0b; 20];
-+ let mut hmac_ctx = HmacCtx::new(&key, Md::sha256()).unwrap();
-+ hmac_ctx.update(b"Hi").unwrap();
-+ hmac_ctx.update(b" There").unwrap();
-+ let size = hmac_ctx.finalize(&mut out).unwrap();
-+ assert_eq!(&out, &expected_hmac);
-+ assert_eq!(size, SHA_256_DIGEST_SIZE);
-+ }
-+
-+ #[test]
-+ #[should_panic]
-+ fn hmac_sha256_update_output_too_short() {
-+ let mut out = vec![0_u8; 1];
-+ let key: [u8; 20] = [0x0b; 20];
-+ let mut hmac_ctx = HmacCtx::new(&key, Md::sha256()).unwrap();
-+ hmac_ctx.update(b"Hi There").unwrap();
-+ hmac_ctx.finalize(&mut out).unwrap();
-+ }
-+}
-diff --git a/openssl/src/lib.rs b/openssl/src/lib.rs
-index 035c90c6..02a51dbb 100644
---- a/openssl/src/lib.rs
-+++ b/openssl/src/lib.rs
-@@ -120,6 +120,9 @@
- #![doc(html_root_url = "https://docs.rs/openssl/0.10")]
- #![warn(rust_2018_idioms)]
-
-+#[cfg(all(soong, boringssl))]
-+extern crate bssl_ffi as ffi;
-+
- #[doc(inline)]
- pub use ffi::init;
-
-@@ -155,6 +158,10 @@ pub mod ex_data;
- #[cfg(not(any(libressl, ossl300)))]
- pub mod fips;
- pub mod hash;
-+#[cfg(boringssl)]
-+pub mod hkdf;
-+#[cfg(boringssl)]
-+pub mod hmac;
- #[cfg(ossl300)]
- pub mod lib_ctx;
- pub mod md;
-@@ -189,6 +196,11 @@ type LenType = libc::size_t;
- #[cfg(not(boringssl))]
- type LenType = libc::c_int;
-
-+#[cfg(boringssl)]
-+type SignedLenType = libc::ssize_t;
-+#[cfg(not(boringssl))]
-+type SignedLenType = libc::c_int;
-+
- #[inline]
- fn cvt_p<T>(r: *mut T) -> Result<*mut T, ErrorStack> {
- if r.is_null() {
-diff --git a/openssl/src/pkey.rs b/openssl/src/pkey.rs
-index 780bd637..92daa882 100644
---- a/openssl/src/pkey.rs
-+++ b/openssl/src/pkey.rs
-@@ -47,7 +47,7 @@ use crate::dh::Dh;
- use crate::dsa::Dsa;
- use crate::ec::EcKey;
- use crate::error::ErrorStack;
--#[cfg(ossl110)]
-+#[cfg(any(boringssl, ossl110))]
- use crate::pkey_ctx::PkeyCtx;
- use crate::rsa::Rsa;
- use crate::symm::Cipher;
-@@ -89,11 +89,11 @@ impl Id {
- #[cfg(ossl110)]
- pub const HKDF: Id = Id(ffi::EVP_PKEY_HKDF);
-
-- #[cfg(ossl111)]
-+ #[cfg(any(boringssl, ossl111))]
- pub const ED25519: Id = Id(ffi::EVP_PKEY_ED25519);
- #[cfg(ossl111)]
- pub const ED448: Id = Id(ffi::EVP_PKEY_ED448);
-- #[cfg(ossl111)]
-+ #[cfg(any(boringssl, ossl111))]
- pub const X25519: Id = Id(ffi::EVP_PKEY_X25519);
- #[cfg(ossl111)]
- pub const X448: Id = Id(ffi::EVP_PKEY_X448);
-@@ -252,7 +252,7 @@ where
- /// This function only works for algorithms that support raw public keys.
- /// Currently this is: [`Id::X25519`], [`Id::ED25519`], [`Id::X448`] or [`Id::ED448`].
- #[corresponds(EVP_PKEY_get_raw_public_key)]
-- #[cfg(ossl111)]
-+ #[cfg(any(boringssl, ossl111))]
- pub fn raw_public_key(&self) -> Result<Vec<u8>, ErrorStack> {
- unsafe {
- let mut len = 0;
-@@ -303,7 +303,7 @@ where
- /// This function only works for algorithms that support raw private keys.
- /// Currently this is: [`Id::HMAC`], [`Id::X25519`], [`Id::ED25519`], [`Id::X448`] or [`Id::ED448`].
- #[corresponds(EVP_PKEY_get_raw_private_key)]
-- #[cfg(ossl111)]
-+ #[cfg(any(boringssl, ossl111))]
- pub fn raw_private_key(&self) -> Result<Vec<u8>, ErrorStack> {
- unsafe {
- let mut len = 0;
-@@ -503,7 +503,7 @@ impl PKey<Private> {
- ctx.keygen()
- }
-
-- #[cfg(ossl111)]
-+ #[cfg(any(boringssl, ossl111))]
- fn generate_eddsa(id: Id) -> Result<PKey<Private>, ErrorStack> {
- let mut ctx = PkeyCtx::new_id(id)?;
- ctx.keygen_init()?;
-@@ -533,7 +533,7 @@ impl PKey<Private> {
- /// assert_eq!(secret.len(), 32);
- /// # Ok(()) }
- /// ```
-- #[cfg(ossl111)]
-+ #[cfg(any(boringssl, ossl111))]
- pub fn generate_x25519() -> Result<PKey<Private>, ErrorStack> {
- PKey::generate_eddsa(Id::X25519)
- }
-@@ -587,7 +587,7 @@ impl PKey<Private> {
- /// assert_eq!(signature.len(), 64);
- /// # Ok(()) }
- /// ```
-- #[cfg(ossl111)]
-+ #[cfg(any(boringssl, ossl111))]
- pub fn generate_ed25519() -> Result<PKey<Private>, ErrorStack> {
- PKey::generate_eddsa(Id::ED25519)
- }
-@@ -737,7 +737,7 @@ impl PKey<Private> {
- ///
- /// Algorithm types that support raw private keys are HMAC, X25519, ED25519, X448 or ED448
- #[corresponds(EVP_PKEY_new_raw_private_key)]
-- #[cfg(ossl111)]
-+ #[cfg(any(boringssl, ossl111))]
- pub fn private_key_from_raw_bytes(
- bytes: &[u8],
- key_type: Id,
-@@ -778,7 +778,7 @@ impl PKey<Public> {
- ///
- /// Algorithm types that support raw public keys are X25519, ED25519, X448 or ED448
- #[corresponds(EVP_PKEY_new_raw_public_key)]
-- #[cfg(ossl111)]
-+ #[cfg(any(boringssl, ossl111))]
- pub fn public_key_from_raw_bytes(
- bytes: &[u8],
- key_type: Id,
-diff --git a/openssl/src/sign.rs b/openssl/src/sign.rs
-index 9cfda481..e5b17a87 100644
---- a/openssl/src/sign.rs
-+++ b/openssl/src/sign.rs
-@@ -290,7 +290,7 @@ impl<'a> Signer<'a> {
- self.len_intern()
- }
-
-- #[cfg(not(ossl111))]
-+ #[cfg(not(any(boringssl, ossl111)))]
- fn len_intern(&self) -> Result<usize, ErrorStack> {
- unsafe {
- let mut len = 0;
-@@ -303,7 +303,7 @@ impl<'a> Signer<'a> {
- }
- }
-
-- #[cfg(ossl111)]
-+ #[cfg(any(boringssl, ossl111))]
- fn len_intern(&self) -> Result<usize, ErrorStack> {
- unsafe {
- let mut len = 0;
-@@ -360,7 +360,7 @@ impl<'a> Signer<'a> {
- /// OpenSSL documentation at [`EVP_DigestSign`].
- ///
- /// [`EVP_DigestSign`]: https://www.openssl.org/docs/man1.1.1/man3/EVP_DigestSign.html
-- #[cfg(ossl111)]
-+ #[cfg(any(boringssl, ossl111))]
- pub fn sign_oneshot(
- &mut self,
- sig_buf: &mut [u8],
-@@ -382,7 +382,7 @@ impl<'a> Signer<'a> {
- /// Returns the signature.
- ///
- /// This is a simple convenience wrapper over `len` and `sign_oneshot`.
-- #[cfg(ossl111)]
-+ #[cfg(any(boringssl, ossl111))]
- pub fn sign_oneshot_to_vec(&mut self, data_buf: &[u8]) -> Result<Vec<u8>, ErrorStack> {
- let mut sig_buf = vec![0; self.len()?];
- let len = self.sign_oneshot(&mut sig_buf, data_buf)?;
-@@ -596,7 +596,7 @@ impl<'a> Verifier<'a> {
- /// OpenSSL documentation at [`EVP_DigestVerify`].
- ///
- /// [`EVP_DigestVerify`]: https://www.openssl.org/docs/man1.1.1/man3/EVP_DigestVerify.html
-- #[cfg(ossl111)]
-+ #[cfg(any(boringssl, ossl111))]
- pub fn verify_oneshot(&mut self, signature: &[u8], buf: &[u8]) -> Result<bool, ErrorStack> {
- unsafe {
- let r = ffi::EVP_DigestVerify(
-diff --git a/openssl/src/symm.rs b/openssl/src/symm.rs
-index 911a7ab2..75b0365c 100644
---- a/openssl/src/symm.rs
-+++ b/openssl/src/symm.rs
-@@ -119,6 +119,7 @@ impl Cipher {
- unsafe { Cipher(ffi::EVP_aes_128_cfb1()) }
- }
-
-+ #[cfg(not(boringssl))]
- pub fn aes_128_cfb128() -> Cipher {
- unsafe { Cipher(ffi::EVP_aes_128_cfb128()) }
- }
-@@ -164,6 +165,7 @@ impl Cipher {
- unsafe { Cipher(ffi::EVP_aes_192_cfb1()) }
- }
-
-+ #[cfg(not(boringssl))]
- pub fn aes_192_cfb128() -> Cipher {
- unsafe { Cipher(ffi::EVP_aes_192_cfb128()) }
- }
-@@ -214,6 +216,7 @@ impl Cipher {
- unsafe { Cipher(ffi::EVP_aes_256_cfb1()) }
- }
-
-+ #[cfg(not(boringssl))]
- pub fn aes_256_cfb128() -> Cipher {
- unsafe { Cipher(ffi::EVP_aes_256_cfb128()) }
- }
-@@ -242,12 +245,12 @@ impl Cipher {
- unsafe { Cipher(ffi::EVP_aes_256_ocb()) }
- }
-
-- #[cfg(not(osslconf = "OPENSSL_NO_BF"))]
-+ #[cfg(not(any(boringssl, osslconf = "OPENSSL_NO_BF")))]
- pub fn bf_cbc() -> Cipher {
- unsafe { Cipher(ffi::EVP_bf_cbc()) }
- }
-
-- #[cfg(not(osslconf = "OPENSSL_NO_BF"))]
-+ #[cfg(not(any(boringssl, osslconf = "OPENSSL_NO_BF")))]
- pub fn bf_ecb() -> Cipher {
- unsafe { Cipher(ffi::EVP_bf_ecb()) }
- }
-diff --git a/openssl/src/x509/mod.rs b/openssl/src/x509/mod.rs
-index 940c8c9c..f9477e4a 100644
---- a/openssl/src/x509/mod.rs
-+++ b/openssl/src/x509/mod.rs
-@@ -356,6 +356,19 @@ impl X509Builder {
- unsafe { cvt(ffi::X509_sign(self.0.as_ptr(), key.as_ptr(), hash.as_ptr())).map(|_| ()) }
- }
-
-+ /// Signs the certificate with a private key but without a digest.
-+ ///
-+ /// This is the only way to sign with Ed25519 keys as BoringSSL doesn't support the null
-+ /// message digest.
-+ #[cfg(boringssl)]
-+ #[corresponds(X509_sign)]
-+ pub fn sign_without_digest<T>(&mut self, key: &PKeyRef<T>) -> Result<(), ErrorStack>
-+ where
-+ T: HasPrivate,
-+ {
-+ unsafe { cvt(ffi::X509_sign(self.0.as_ptr(), key.as_ptr(), ptr::null())).map(|_| ()) }
-+ }
-+
- /// Consumes the builder, returning the certificate.
- pub fn build(self) -> X509 {
- self.0
-@@ -898,13 +911,13 @@ impl X509NameBuilder {
- pub fn append_entry_by_text(&mut self, field: &str, value: &str) -> Result<(), ErrorStack> {
- unsafe {
- let field = CString::new(field).unwrap();
-- assert!(value.len() <= c_int::max_value() as usize);
-+ assert!(value.len() <= isize::max_value() as usize);
- cvt(ffi::X509_NAME_add_entry_by_txt(
- self.0.as_ptr(),
- field.as_ptr() as *mut _,
- ffi::MBSTRING_UTF8,
- value.as_ptr(),
-- value.len() as c_int,
-+ value.len() as isize,
- -1,
- 0,
- ))
-@@ -925,13 +938,13 @@ impl X509NameBuilder {
- ) -> Result<(), ErrorStack> {
- unsafe {
- let field = CString::new(field).unwrap();
-- assert!(value.len() <= c_int::max_value() as usize);
-+ assert!(value.len() <= isize::max_value() as usize);
- cvt(ffi::X509_NAME_add_entry_by_txt(
- self.0.as_ptr(),
- field.as_ptr() as *mut _,
- ty.as_raw(),
- value.as_ptr(),
-- value.len() as c_int,
-+ value.len() as isize,
- -1,
- 0,
- ))
-@@ -946,13 +959,13 @@ impl X509NameBuilder {
- /// [`X509_NAME_add_entry_by_NID`]: https://www.openssl.org/docs/manmaster/crypto/X509_NAME_add_entry_by_NID.html
- pub fn append_entry_by_nid(&mut self, field: Nid, value: &str) -> Result<(), ErrorStack> {
- unsafe {
-- assert!(value.len() <= c_int::max_value() as usize);
-+ assert!(value.len() <= isize::max_value() as usize);
- cvt(ffi::X509_NAME_add_entry_by_NID(
- self.0.as_ptr(),
- field.as_raw(),
- ffi::MBSTRING_UTF8,
- value.as_ptr() as *mut _,
-- value.len() as c_int,
-+ value.len() as isize,
- -1,
- 0,
- ))
-@@ -972,13 +985,13 @@ impl X509NameBuilder {
- ty: Asn1Type,
- ) -> Result<(), ErrorStack> {
- unsafe {
-- assert!(value.len() <= c_int::max_value() as usize);
-+ assert!(value.len() <= isize::max_value() as usize);
- cvt(ffi::X509_NAME_add_entry_by_NID(
- self.0.as_ptr(),
- field.as_raw(),
- ty.as_raw(),
- value.as_ptr() as *mut _,
-- value.len() as c_int,
-+ value.len() as isize,
- -1,
- 0,
- ))
-@@ -1286,6 +1299,29 @@ impl X509ReqBuilder {
- }
- }
-
-+ /// Sign the request using a private key without a digest.
-+ ///
-+ /// This is the only way to sign with Ed25519 keys as BoringSSL doesn't support the null
-+ /// message digest.
-+ ///
-+ /// This corresponds to [`X509_REQ_sign`].
-+ ///
-+ /// [`X509_REQ_sign`]: https://www.openssl.org/docs/man1.1.0/crypto/X509_REQ_sign.html
-+ #[cfg(boringssl)]
-+ pub fn sign_without_digest<T>(&mut self, key: &PKeyRef<T>) -> Result<(), ErrorStack>
-+ where
-+ T: HasPrivate,
-+ {
-+ unsafe {
-+ cvt(ffi::X509_REQ_sign(
-+ self.0.as_ptr(),
-+ key.as_ptr(),
-+ ptr::null(),
-+ ))
-+ .map(|_| ())
-+ }
-+ }
-+
- /// Returns the `X509Req`.
- pub fn build(self) -> X509Req {
- self.0
-diff --git a/openssl/src/x509/mod.rs.orig b/openssl/src/x509/mod.rs.orig
-new file mode 100644
-index 00000000..34b86c10
---- /dev/null
-+++ b/openssl/src/x509/mod.rs.orig
-@@ -0,0 +1,1879 @@
-+//! The standard defining the format of public key certificates.
-+//!
-+//! An `X509` certificate binds an identity to a public key, and is either
-+//! signed by a certificate authority (CA) or self-signed. An entity that gets
-+//! a hold of a certificate can both verify your identity (via a CA) and encrypt
-+//! data with the included public key. `X509` certificates are used in many
-+//! Internet protocols, including SSL/TLS, which is the basis for HTTPS,
-+//! the secure protocol for browsing the web.
-+
-+use cfg_if::cfg_if;
-+use foreign_types::{ForeignType, ForeignTypeRef, Opaque};
-+use libc::{c_int, c_long, c_uint};
-+use std::cmp::{self, Ordering};
-+use std::convert::TryFrom;
-+use std::error::Error;
-+use std::ffi::{CStr, CString};
-+use std::fmt;
-+use std::marker::PhantomData;
-+use std::mem;
-+use std::net::IpAddr;
-+use std::path::Path;
-+use std::ptr;
-+use std::slice;
-+use std::str;
-+
-+use crate::asn1::{
-+ Asn1BitStringRef, Asn1IntegerRef, Asn1ObjectRef, Asn1StringRef, Asn1TimeRef, Asn1Type,
-+};
-+use crate::bio::MemBioSlice;
-+use crate::conf::ConfRef;
-+use crate::error::ErrorStack;
-+use crate::ex_data::Index;
-+use crate::hash::{DigestBytes, MessageDigest};
-+use crate::nid::Nid;
-+use crate::pkey::{HasPrivate, HasPublic, PKey, PKeyRef, Public};
-+use crate::ssl::SslRef;
-+use crate::stack::{Stack, StackRef, Stackable};
-+use crate::string::OpensslString;
-+use crate::util::{ForeignTypeExt, ForeignTypeRefExt};
-+use crate::{cvt, cvt_n, cvt_p};
-+use openssl_macros::corresponds;
-+
-+#[cfg(any(ossl102, libressl261))]
-+pub mod verify;
-+
-+pub mod extension;
-+pub mod store;
-+
-+#[cfg(test)]
-+mod tests;
-+
-+foreign_type_and_impl_send_sync! {
-+ type CType = ffi::X509_STORE_CTX;
-+ fn drop = ffi::X509_STORE_CTX_free;
-+
-+ /// An `X509` certificate store context.
-+ pub struct X509StoreContext;
-+
-+ /// A reference to an [`X509StoreContext`].
-+ pub struct X509StoreContextRef;
-+}
-+
-+impl X509StoreContext {
-+ /// Returns the index which can be used to obtain a reference to the `Ssl` associated with a
-+ /// context.
-+ #[corresponds(SSL_get_ex_data_X509_STORE_CTX_idx)]
-+ pub fn ssl_idx() -> Result<Index<X509StoreContext, SslRef>, ErrorStack> {
-+ unsafe { cvt_n(ffi::SSL_get_ex_data_X509_STORE_CTX_idx()).map(|idx| Index::from_raw(idx)) }
-+ }
-+
-+ /// Creates a new `X509StoreContext` instance.
-+ #[corresponds(X509_STORE_CTX_new)]
-+ pub fn new() -> Result<X509StoreContext, ErrorStack> {
-+ unsafe {
-+ ffi::init();
-+ cvt_p(ffi::X509_STORE_CTX_new()).map(X509StoreContext)
-+ }
-+ }
-+}
-+
-+impl X509StoreContextRef {
-+ /// Returns application data pertaining to an `X509` store context.
-+ #[corresponds(X509_STORE_CTX_get_ex_data)]
-+ pub fn ex_data<T>(&self, index: Index<X509StoreContext, T>) -> Option<&T> {
-+ unsafe {
-+ let data = ffi::X509_STORE_CTX_get_ex_data(self.as_ptr(), index.as_raw());
-+ if data.is_null() {
-+ None
-+ } else {
-+ Some(&*(data as *const T))
-+ }
-+ }
-+ }
-+
-+ /// Returns the error code of the context.
-+ #[corresponds(X509_STORE_CTX_get_error)]
-+ pub fn error(&self) -> X509VerifyResult {
-+ unsafe { X509VerifyResult::from_raw(ffi::X509_STORE_CTX_get_error(self.as_ptr())) }
-+ }
-+
-+ /// Initializes this context with the given certificate, certificates chain and certificate
-+ /// store. After initializing the context, the `with_context` closure is called with the prepared
-+ /// context. As long as the closure is running, the context stays initialized and can be used
-+ /// to e.g. verify a certificate. The context will be cleaned up, after the closure finished.
-+ ///
-+ /// * `trust` - The certificate store with the trusted certificates.
-+ /// * `cert` - The certificate that should be verified.
-+ /// * `cert_chain` - The certificates chain.
-+ /// * `with_context` - The closure that is called with the initialized context.
-+ ///
-+ /// This corresponds to [`X509_STORE_CTX_init`] before calling `with_context` and to
-+ /// [`X509_STORE_CTX_cleanup`] after calling `with_context`.
-+ ///
-+ /// [`X509_STORE_CTX_init`]: https://www.openssl.org/docs/manmaster/crypto/X509_STORE_CTX_init.html
-+ /// [`X509_STORE_CTX_cleanup`]: https://www.openssl.org/docs/manmaster/crypto/X509_STORE_CTX_cleanup.html
-+ pub fn init<F, T>(
-+ &mut self,
-+ trust: &store::X509StoreRef,
-+ cert: &X509Ref,
-+ cert_chain: &StackRef<X509>,
-+ with_context: F,
-+ ) -> Result<T, ErrorStack>
-+ where
-+ F: FnOnce(&mut X509StoreContextRef) -> Result<T, ErrorStack>,
-+ {
-+ struct Cleanup<'a>(&'a mut X509StoreContextRef);
-+
-+ impl<'a> Drop for Cleanup<'a> {
-+ fn drop(&mut self) {
-+ unsafe {
-+ ffi::X509_STORE_CTX_cleanup(self.0.as_ptr());
-+ }
-+ }
-+ }
-+
-+ unsafe {
-+ cvt(ffi::X509_STORE_CTX_init(
-+ self.as_ptr(),
-+ trust.as_ptr(),
-+ cert.as_ptr(),
-+ cert_chain.as_ptr(),
-+ ))?;
-+
-+ let cleanup = Cleanup(self);
-+ with_context(cleanup.0)
-+ }
-+ }
-+
-+ /// Verifies the stored certificate.
-+ ///
-+ /// Returns `true` if verification succeeds. The `error` method will return the specific
-+ /// validation error if the certificate was not valid.
-+ ///
-+ /// This will only work inside of a call to `init`.
-+ #[corresponds(X509_verify_cert)]
-+ pub fn verify_cert(&mut self) -> Result<bool, ErrorStack> {
-+ unsafe { cvt_n(ffi::X509_verify_cert(self.as_ptr())).map(|n| n != 0) }
-+ }
-+
-+ /// Set the error code of the context.
-+ #[corresponds(X509_STORE_CTX_set_error)]
-+ pub fn set_error(&mut self, result: X509VerifyResult) {
-+ unsafe {
-+ ffi::X509_STORE_CTX_set_error(self.as_ptr(), result.as_raw());
-+ }
-+ }
-+
-+ /// Returns a reference to the certificate which caused the error or None if
-+ /// no certificate is relevant to the error.
-+ #[corresponds(X509_STORE_CTX_get_current_cert)]
-+ pub fn current_cert(&self) -> Option<&X509Ref> {
-+ unsafe {
-+ let ptr = ffi::X509_STORE_CTX_get_current_cert(self.as_ptr());
-+ X509Ref::from_const_ptr_opt(ptr)
-+ }
-+ }
-+
-+ /// Returns a non-negative integer representing the depth in the certificate
-+ /// chain where the error occurred. If it is zero it occurred in the end
-+ /// entity certificate, one if it is the certificate which signed the end
-+ /// entity certificate and so on.
-+ #[corresponds(X509_STORE_CTX_get_error_depth)]
-+ pub fn error_depth(&self) -> u32 {
-+ unsafe { ffi::X509_STORE_CTX_get_error_depth(self.as_ptr()) as u32 }
-+ }
-+
-+ /// Returns a reference to a complete valid `X509` certificate chain.
-+ #[corresponds(X509_STORE_CTX_get0_chain)]
-+ pub fn chain(&self) -> Option<&StackRef<X509>> {
-+ unsafe {
-+ let chain = X509_STORE_CTX_get0_chain(self.as_ptr());
-+
-+ if chain.is_null() {
-+ None
-+ } else {
-+ Some(StackRef::from_ptr(chain))
-+ }
-+ }
-+ }
-+}
-+
-+/// A builder used to construct an `X509`.
-+pub struct X509Builder(X509);
-+
-+impl X509Builder {
-+ /// Creates a new builder.
-+ #[corresponds(X509_new)]
-+ pub fn new() -> Result<X509Builder, ErrorStack> {
-+ unsafe {
-+ ffi::init();
-+ cvt_p(ffi::X509_new()).map(|p| X509Builder(X509(p)))
-+ }
-+ }
-+
-+ /// Sets the notAfter constraint on the certificate.
-+ #[corresponds(X509_set1_notAfter)]
-+ pub fn set_not_after(&mut self, not_after: &Asn1TimeRef) -> Result<(), ErrorStack> {
-+ unsafe { cvt(X509_set1_notAfter(self.0.as_ptr(), not_after.as_ptr())).map(|_| ()) }
-+ }
-+
-+ /// Sets the notBefore constraint on the certificate.
-+ #[corresponds(X509_set1_notBefore)]
-+ pub fn set_not_before(&mut self, not_before: &Asn1TimeRef) -> Result<(), ErrorStack> {
-+ unsafe { cvt(X509_set1_notBefore(self.0.as_ptr(), not_before.as_ptr())).map(|_| ()) }
-+ }
-+
-+ /// Sets the version of the certificate.
-+ ///
-+ /// Note that the version is zero-indexed; that is, a certificate corresponding to version 3 of
-+ /// the X.509 standard should pass `2` to this method.
-+ #[corresponds(X509_set_version)]
-+ #[allow(clippy::useless_conversion)]
-+ pub fn set_version(&mut self, version: i32) -> Result<(), ErrorStack> {
-+ unsafe { cvt(ffi::X509_set_version(self.0.as_ptr(), version as c_long)).map(|_| ()) }
-+ }
-+
-+ /// Sets the serial number of the certificate.
-+ #[corresponds(X509_set_serialNumber)]
-+ pub fn set_serial_number(&mut self, serial_number: &Asn1IntegerRef) -> Result<(), ErrorStack> {
-+ unsafe {
-+ cvt(ffi::X509_set_serialNumber(
-+ self.0.as_ptr(),
-+ serial_number.as_ptr(),
-+ ))
-+ .map(|_| ())
-+ }
-+ }
-+
-+ /// Sets the issuer name of the certificate.
-+ #[corresponds(X509_set_issuer_name)]
-+ pub fn set_issuer_name(&mut self, issuer_name: &X509NameRef) -> Result<(), ErrorStack> {
-+ unsafe {
-+ cvt(ffi::X509_set_issuer_name(
-+ self.0.as_ptr(),
-+ issuer_name.as_ptr(),
-+ ))
-+ .map(|_| ())
-+ }
-+ }
-+
-+ /// Sets the subject name of the certificate.
-+ ///
-+ /// When building certificates, the `C`, `ST`, and `O` options are common when using the openssl command line tools.
-+ /// The `CN` field is used for the common name, such as a DNS name.
-+ ///
-+ /// ```
-+ /// use openssl::x509::{X509, X509NameBuilder};
-+ ///
-+ /// let mut x509_name = openssl::x509::X509NameBuilder::new().unwrap();
-+ /// x509_name.append_entry_by_text("C", "US").unwrap();
-+ /// x509_name.append_entry_by_text("ST", "CA").unwrap();
-+ /// x509_name.append_entry_by_text("O", "Some organization").unwrap();
-+ /// x509_name.append_entry_by_text("CN", "www.example.com").unwrap();
-+ /// let x509_name = x509_name.build();
-+ ///
-+ /// let mut x509 = openssl::x509::X509::builder().unwrap();
-+ /// x509.set_subject_name(&x509_name).unwrap();
-+ /// ```
-+ #[corresponds(X509_set_subject_name)]
-+ pub fn set_subject_name(&mut self, subject_name: &X509NameRef) -> Result<(), ErrorStack> {
-+ unsafe {
-+ cvt(ffi::X509_set_subject_name(
-+ self.0.as_ptr(),
-+ subject_name.as_ptr(),
-+ ))
-+ .map(|_| ())
-+ }
-+ }
-+
-+ /// Sets the public key associated with the certificate.
-+ #[corresponds(X509_set_pubkey)]
-+ pub fn set_pubkey<T>(&mut self, key: &PKeyRef<T>) -> Result<(), ErrorStack>
-+ where
-+ T: HasPublic,
-+ {
-+ unsafe { cvt(ffi::X509_set_pubkey(self.0.as_ptr(), key.as_ptr())).map(|_| ()) }
-+ }
-+
-+ /// Returns a context object which is needed to create certain X509 extension values.
-+ ///
-+ /// Set `issuer` to `None` if the certificate will be self-signed.
-+ #[corresponds(X509V3_set_ctx)]
-+ pub fn x509v3_context<'a>(
-+ &'a self,
-+ issuer: Option<&'a X509Ref>,
-+ conf: Option<&'a ConfRef>,
-+ ) -> X509v3Context<'a> {
-+ unsafe {
-+ let mut ctx = mem::zeroed();
-+
-+ let issuer = match issuer {
-+ Some(issuer) => issuer.as_ptr(),
-+ None => self.0.as_ptr(),
-+ };
-+ let subject = self.0.as_ptr();
-+ ffi::X509V3_set_ctx(
-+ &mut ctx,
-+ issuer,
-+ subject,
-+ ptr::null_mut(),
-+ ptr::null_mut(),
-+ 0,
-+ );
-+
-+ // nodb case taken care of since we zeroed ctx above
-+ if let Some(conf) = conf {
-+ ffi::X509V3_set_nconf(&mut ctx, conf.as_ptr());
-+ }
-+
-+ X509v3Context(ctx, PhantomData)
-+ }
-+ }
-+
-+ /// Adds an X509 extension value to the certificate.
-+ ///
-+ /// This works just as `append_extension` except it takes ownership of the `X509Extension`.
-+ pub fn append_extension(&mut self, extension: X509Extension) -> Result<(), ErrorStack> {
-+ self.append_extension2(&extension)
-+ }
-+
-+ /// Adds an X509 extension value to the certificate.
-+ #[corresponds(X509_add_ext)]
-+ pub fn append_extension2(&mut self, extension: &X509ExtensionRef) -> Result<(), ErrorStack> {
-+ unsafe {
-+ cvt(ffi::X509_add_ext(self.0.as_ptr(), extension.as_ptr(), -1))?;
-+ Ok(())
-+ }
-+ }
-+
-+ /// Signs the certificate with a private key.
-+ #[corresponds(X509_sign)]
-+ pub fn sign<T>(&mut self, key: &PKeyRef<T>, hash: MessageDigest) -> Result<(), ErrorStack>
-+ where
-+ T: HasPrivate,
-+ {
-+ unsafe { cvt(ffi::X509_sign(self.0.as_ptr(), key.as_ptr(), hash.as_ptr())).map(|_| ()) }
-+ }
-+
-+ /// Signs the certificate with a private key but without a digest.
-+ ///
-+ /// This is the only way to sign with Ed25519 keys as BoringSSL doesn't support the null
-+ /// message digest.
-+ #[cfg(boringssl)]
-+ #[corresponds(X509_sign)]
-+ pub fn sign_without_digest<T>(&mut self, key: &PKeyRef<T>) -> Result<(), ErrorStack>
-+ where
-+ T: HasPrivate,
-+ {
-+ unsafe { cvt(ffi::X509_sign(self.0.as_ptr(), key.as_ptr(), ptr::null())).map(|_| ()) }
-+ }
-+
-+ /// Consumes the builder, returning the certificate.
-+ pub fn build(self) -> X509 {
-+ self.0
-+ }
-+}
-+
-+foreign_type_and_impl_send_sync! {
-+ type CType = ffi::X509;
-+ fn drop = ffi::X509_free;
-+
-+ /// An `X509` public key certificate.
-+ pub struct X509;
-+ /// Reference to `X509`.
-+ pub struct X509Ref;
-+}
-+
-+#[cfg(boringssl)]
-+type X509LenTy = c_uint;
-+#[cfg(not(boringssl))]
-+type X509LenTy = c_int;
-+
-+impl X509Ref {
-+ /// Returns this certificate's subject name.
-+ #[corresponds(X509_get_subject_name)]
-+ pub fn subject_name(&self) -> &X509NameRef {
-+ unsafe {
-+ let name = ffi::X509_get_subject_name(self.as_ptr());
-+ X509NameRef::from_const_ptr_opt(name).expect("subject name must not be null")
-+ }
-+ }
-+
-+ /// Returns the hash of the certificates subject
-+ #[corresponds(X509_subject_name_hash)]
-+ pub fn subject_name_hash(&self) -> u32 {
-+ unsafe { ffi::X509_subject_name_hash(self.as_ptr()) as u32 }
-+ }
-+
-+ /// Returns this certificate's issuer name.
-+ #[corresponds(X509_get_issuer_name)]
-+ pub fn issuer_name(&self) -> &X509NameRef {
-+ unsafe {
-+ let name = ffi::X509_get_issuer_name(self.as_ptr());
-+ X509NameRef::from_const_ptr_opt(name).expect("issuer name must not be null")
-+ }
-+ }
-+
-+ /// Returns the hash of the certificates issuer
-+ #[corresponds(X509_issuer_name_hash)]
-+ pub fn issuer_name_hash(&self) -> u32 {
-+ unsafe { ffi::X509_issuer_name_hash(self.as_ptr()) as u32 }
-+ }
-+
-+ /// Returns this certificate's subject alternative name entries, if they exist.
-+ #[corresponds(X509_get_ext_d2i)]
-+ pub fn subject_alt_names(&self) -> Option<Stack<GeneralName>> {
-+ unsafe {
-+ let stack = ffi::X509_get_ext_d2i(
-+ self.as_ptr(),
-+ ffi::NID_subject_alt_name,
-+ ptr::null_mut(),
-+ ptr::null_mut(),
-+ );
-+ Stack::from_ptr_opt(stack as *mut _)
-+ }
-+ }
-+
-+ /// Returns this certificate's issuer alternative name entries, if they exist.
-+ #[corresponds(X509_get_ext_d2i)]
-+ pub fn issuer_alt_names(&self) -> Option<Stack<GeneralName>> {
-+ unsafe {
-+ let stack = ffi::X509_get_ext_d2i(
-+ self.as_ptr(),
-+ ffi::NID_issuer_alt_name,
-+ ptr::null_mut(),
-+ ptr::null_mut(),
-+ );
-+ Stack::from_ptr_opt(stack as *mut _)
-+ }
-+ }
-+
-+ /// Returns this certificate's [`authority information access`] entries, if they exist.
-+ ///
-+ /// [`authority information access`]: https://tools.ietf.org/html/rfc5280#section-4.2.2.1
-+ #[corresponds(X509_get_ext_d2i)]
-+ pub fn authority_info(&self) -> Option<Stack<AccessDescription>> {
-+ unsafe {
-+ let stack = ffi::X509_get_ext_d2i(
-+ self.as_ptr(),
-+ ffi::NID_info_access,
-+ ptr::null_mut(),
-+ ptr::null_mut(),
-+ );
-+ Stack::from_ptr_opt(stack as *mut _)
-+ }
-+ }
-+
-+ #[corresponds(X509_get_pubkey)]
-+ pub fn public_key(&self) -> Result<PKey<Public>, ErrorStack> {
-+ unsafe {
-+ let pkey = cvt_p(ffi::X509_get_pubkey(self.as_ptr()))?;
-+ Ok(PKey::from_ptr(pkey))
-+ }
-+ }
-+
-+ /// Returns a digest of the DER representation of the certificate.
-+ #[corresponds(X509_digest)]
-+ pub fn digest(&self, hash_type: MessageDigest) -> Result<DigestBytes, ErrorStack> {
-+ unsafe {
-+ let mut digest = DigestBytes {
-+ buf: [0; ffi::EVP_MAX_MD_SIZE as usize],
-+ len: ffi::EVP_MAX_MD_SIZE as usize,
-+ };
-+ let mut len = ffi::EVP_MAX_MD_SIZE as c_uint;
-+ cvt(ffi::X509_digest(
-+ self.as_ptr(),
-+ hash_type.as_ptr(),
-+ digest.buf.as_mut_ptr() as *mut _,
-+ &mut len,
-+ ))?;
-+ digest.len = len as usize;
-+
-+ Ok(digest)
-+ }
-+ }
-+
-+ #[deprecated(since = "0.10.9", note = "renamed to digest")]
-+ pub fn fingerprint(&self, hash_type: MessageDigest) -> Result<Vec<u8>, ErrorStack> {
-+ self.digest(hash_type).map(|b| b.to_vec())
-+ }
-+
-+ /// Returns the certificate's Not After validity period.
-+ #[corresponds(X509_getm_notAfter)]
-+ pub fn not_after(&self) -> &Asn1TimeRef {
-+ unsafe {
-+ let date = X509_getm_notAfter(self.as_ptr());
-+ Asn1TimeRef::from_const_ptr_opt(date).expect("not_after must not be null")
-+ }
-+ }
-+
-+ /// Returns the certificate's Not Before validity period.
-+ #[corresponds(X509_getm_notBefore)]
-+ pub fn not_before(&self) -> &Asn1TimeRef {
-+ unsafe {
-+ let date = X509_getm_notBefore(self.as_ptr());
-+ Asn1TimeRef::from_const_ptr_opt(date).expect("not_before must not be null")
-+ }
-+ }
-+
-+ /// Returns the certificate's signature
-+ #[corresponds(X509_get0_signature)]
-+ pub fn signature(&self) -> &Asn1BitStringRef {
-+ unsafe {
-+ let mut signature = ptr::null();
-+ X509_get0_signature(&mut signature, ptr::null_mut(), self.as_ptr());
-+ Asn1BitStringRef::from_const_ptr_opt(signature).expect("signature must not be null")
-+ }
-+ }
-+
-+ /// Returns the certificate's signature algorithm.
-+ #[corresponds(X509_get0_signature)]
-+ pub fn signature_algorithm(&self) -> &X509AlgorithmRef {
-+ unsafe {
-+ let mut algor = ptr::null();
-+ X509_get0_signature(ptr::null_mut(), &mut algor, self.as_ptr());
-+ X509AlgorithmRef::from_const_ptr_opt(algor)
-+ .expect("signature algorithm must not be null")
-+ }
-+ }
-+
-+ /// Returns the list of OCSP responder URLs specified in the certificate's Authority Information
-+ /// Access field.
-+ #[corresponds(X509_get1_ocsp)]
-+ pub fn ocsp_responders(&self) -> Result<Stack<OpensslString>, ErrorStack> {
-+ unsafe { cvt_p(ffi::X509_get1_ocsp(self.as_ptr())).map(|p| Stack::from_ptr(p)) }
-+ }
-+
-+ /// Checks that this certificate issued `subject`.
-+ #[corresponds(X509_check_issued)]
-+ pub fn issued(&self, subject: &X509Ref) -> X509VerifyResult {
-+ unsafe {
-+ let r = ffi::X509_check_issued(self.as_ptr(), subject.as_ptr());
-+ X509VerifyResult::from_raw(r)
-+ }
-+ }
-+
-+ /// Returns certificate version. If this certificate has no explicit version set, it defaults to
-+ /// version 1.
-+ ///
-+ /// Note that `0` return value stands for version 1, `1` for version 2 and so on.
-+ #[corresponds(X509_get_version)]
-+ #[cfg(ossl110)]
-+ pub fn version(&self) -> i32 {
-+ unsafe { ffi::X509_get_version(self.as_ptr()) as i32 }
-+ }
-+
-+ /// Check if the certificate is signed using the given public key.
-+ ///
-+ /// Only the signature is checked: no other checks (such as certificate chain validity)
-+ /// are performed.
-+ ///
-+ /// Returns `true` if verification succeeds.
-+ #[corresponds(X509_verify)]
-+ pub fn verify<T>(&self, key: &PKeyRef<T>) -> Result<bool, ErrorStack>
-+ where
-+ T: HasPublic,
-+ {
-+ unsafe { cvt_n(ffi::X509_verify(self.as_ptr(), key.as_ptr())).map(|n| n != 0) }
-+ }
-+
-+ /// Returns this certificate's serial number.
-+ #[corresponds(X509_get_serialNumber)]
-+ pub fn serial_number(&self) -> &Asn1IntegerRef {
-+ unsafe {
-+ let r = ffi::X509_get_serialNumber(self.as_ptr());
-+ Asn1IntegerRef::from_const_ptr_opt(r).expect("serial number must not be null")
-+ }
-+ }
-+
-+ to_pem! {
-+ /// Serializes the certificate into a PEM-encoded X509 structure.
-+ ///
-+ /// The output will have a header of `-----BEGIN CERTIFICATE-----`.
-+ #[corresponds(PEM_write_bio_X509)]
-+ to_pem,
-+ ffi::PEM_write_bio_X509
-+ }
-+
-+ to_der! {
-+ /// Serializes the certificate into a DER-encoded X509 structure.
-+ #[corresponds(i2d_X509)]
-+ to_der,
-+ ffi::i2d_X509
-+ }
-+
-+ to_pem! {
-+ /// Converts the certificate to human readable text.
-+ #[corresponds(X509_print)]
-+ to_text,
-+ ffi::X509_print
-+ }
-+}
-+
-+impl ToOwned for X509Ref {
-+ type Owned = X509;
-+
-+ fn to_owned(&self) -> X509 {
-+ unsafe {
-+ X509_up_ref(self.as_ptr());
-+ X509::from_ptr(self.as_ptr())
-+ }
-+ }
-+}
-+
-+impl Ord for X509Ref {
-+ fn cmp(&self, other: &Self) -> cmp::Ordering {
-+ // X509_cmp returns a number <0 for less than, 0 for equal and >0 for greater than.
-+ // It can't fail if both pointers are valid, which we know is true.
-+ let cmp = unsafe { ffi::X509_cmp(self.as_ptr(), other.as_ptr()) };
-+ cmp.cmp(&0)
-+ }
-+}
-+
-+impl PartialOrd for X509Ref {
-+ fn partial_cmp(&self, other: &Self) -> Option<cmp::Ordering> {
-+ Some(self.cmp(other))
-+ }
-+}
-+
-+impl PartialOrd<X509> for X509Ref {
-+ fn partial_cmp(&self, other: &X509) -> Option<cmp::Ordering> {
-+ <X509Ref as PartialOrd<X509Ref>>::partial_cmp(self, other)
-+ }
-+}
-+
-+impl PartialEq for X509Ref {
-+ fn eq(&self, other: &Self) -> bool {
-+ self.cmp(other) == cmp::Ordering::Equal
-+ }
-+}
-+
-+impl PartialEq<X509> for X509Ref {
-+ fn eq(&self, other: &X509) -> bool {
-+ <X509Ref as PartialEq<X509Ref>>::eq(self, other)
-+ }
-+}
-+
-+impl Eq for X509Ref {}
-+
-+impl X509 {
-+ /// Returns a new builder.
-+ pub fn builder() -> Result<X509Builder, ErrorStack> {
-+ X509Builder::new()
-+ }
-+
-+ from_pem! {
-+ /// Deserializes a PEM-encoded X509 structure.
-+ ///
-+ /// The input should have a header of `-----BEGIN CERTIFICATE-----`.
-+ #[corresponds(PEM_read_bio_X509)]
-+ from_pem,
-+ X509,
-+ ffi::PEM_read_bio_X509
-+ }
-+
-+ from_der! {
-+ /// Deserializes a DER-encoded X509 structure.
-+ #[corresponds(d2i_X509)]
-+ from_der,
-+ X509,
-+ ffi::d2i_X509
-+ }
-+
-+ /// Deserializes a list of PEM-formatted certificates.
-+ #[corresponds(PEM_read_bio_X509)]
-+ pub fn stack_from_pem(pem: &[u8]) -> Result<Vec<X509>, ErrorStack> {
-+ unsafe {
-+ ffi::init();
-+ let bio = MemBioSlice::new(pem)?;
-+
-+ let mut certs = vec![];
-+ loop {
-+ let r =
-+ ffi::PEM_read_bio_X509(bio.as_ptr(), ptr::null_mut(), None, ptr::null_mut());
-+ if r.is_null() {
-+ let err = ffi::ERR_peek_last_error();
-+ if ffi::ERR_GET_LIB(err) as X509LenTy == ffi::ERR_LIB_PEM
-+ && ffi::ERR_GET_REASON(err) == ffi::PEM_R_NO_START_LINE
-+ {
-+ ffi::ERR_clear_error();
-+ break;
-+ }
-+
-+ return Err(ErrorStack::get());
-+ } else {
-+ certs.push(X509(r));
-+ }
-+ }
-+
-+ Ok(certs)
-+ }
-+ }
-+}
-+
-+impl Clone for X509 {
-+ fn clone(&self) -> X509 {
-+ X509Ref::to_owned(self)
-+ }
-+}
-+
-+impl fmt::Debug for X509 {
-+ fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
-+ let serial = match &self.serial_number().to_bn() {
-+ Ok(bn) => match bn.to_hex_str() {
-+ Ok(hex) => hex.to_string(),
-+ Err(_) => "".to_string(),
-+ },
-+ Err(_) => "".to_string(),
-+ };
-+ let mut debug_struct = formatter.debug_struct("X509");
-+ debug_struct.field("serial_number", &serial);
-+ debug_struct.field("signature_algorithm", &self.signature_algorithm().object());
-+ debug_struct.field("issuer", &self.issuer_name());
-+ debug_struct.field("subject", &self.subject_name());
-+ if let Some(subject_alt_names) = &self.subject_alt_names() {
-+ debug_struct.field("subject_alt_names", subject_alt_names);
-+ }
-+ debug_struct.field("not_before", &self.not_before());
-+ debug_struct.field("not_after", &self.not_after());
-+
-+ if let Ok(public_key) = &self.public_key() {
-+ debug_struct.field("public_key", public_key);
-+ };
-+ // TODO: Print extensions once they are supported on the X509 struct.
-+
-+ debug_struct.finish()
-+ }
-+}
-+
-+impl AsRef<X509Ref> for X509Ref {
-+ fn as_ref(&self) -> &X509Ref {
-+ self
-+ }
-+}
-+
-+impl Stackable for X509 {
-+ type StackType = ffi::stack_st_X509;
-+}
-+
-+impl Ord for X509 {
-+ fn cmp(&self, other: &Self) -> cmp::Ordering {
-+ X509Ref::cmp(self, other)
-+ }
-+}
-+
-+impl PartialOrd for X509 {
-+ fn partial_cmp(&self, other: &Self) -> Option<cmp::Ordering> {
-+ X509Ref::partial_cmp(self, other)
-+ }
-+}
-+
-+impl PartialOrd<X509Ref> for X509 {
-+ fn partial_cmp(&self, other: &X509Ref) -> Option<cmp::Ordering> {
-+ X509Ref::partial_cmp(self, other)
-+ }
-+}
-+
-+impl PartialEq for X509 {
-+ fn eq(&self, other: &Self) -> bool {
-+ X509Ref::eq(self, other)
-+ }
-+}
-+
-+impl PartialEq<X509Ref> for X509 {
-+ fn eq(&self, other: &X509Ref) -> bool {
-+ X509Ref::eq(self, other)
-+ }
-+}
-+
-+impl Eq for X509 {}
-+
-+/// A context object required to construct certain `X509` extension values.
-+pub struct X509v3Context<'a>(ffi::X509V3_CTX, PhantomData<(&'a X509Ref, &'a ConfRef)>);
-+
-+impl<'a> X509v3Context<'a> {
-+ pub fn as_ptr(&self) -> *mut ffi::X509V3_CTX {
-+ &self.0 as *const _ as *mut _
-+ }
-+}
-+
-+foreign_type_and_impl_send_sync! {
-+ type CType = ffi::X509_EXTENSION;
-+ fn drop = ffi::X509_EXTENSION_free;
-+
-+ /// Permit additional fields to be added to an `X509` v3 certificate.
-+ pub struct X509Extension;
-+ /// Reference to `X509Extension`.
-+ pub struct X509ExtensionRef;
-+}
-+
-+impl Stackable for X509Extension {
-+ type StackType = ffi::stack_st_X509_EXTENSION;
-+}
-+
-+impl X509Extension {
-+ /// Constructs an X509 extension value. See `man x509v3_config` for information on supported
-+ /// names and their value formats.
-+ ///
-+ /// Some extension types, such as `subjectAlternativeName`, require an `X509v3Context` to be
-+ /// provided.
-+ ///
-+ /// See the extension module for builder types which will construct certain common extensions.
-+ pub fn new(
-+ conf: Option<&ConfRef>,
-+ context: Option<&X509v3Context<'_>>,
-+ name: &str,
-+ value: &str,
-+ ) -> Result<X509Extension, ErrorStack> {
-+ let name = CString::new(name).unwrap();
-+ let value = CString::new(value).unwrap();
-+ unsafe {
-+ ffi::init();
-+ let conf = conf.map_or(ptr::null_mut(), ConfRef::as_ptr);
-+ let context = context.map_or(ptr::null_mut(), X509v3Context::as_ptr);
-+ let name = name.as_ptr() as *mut _;
-+ let value = value.as_ptr() as *mut _;
-+
-+ cvt_p(ffi::X509V3_EXT_nconf(conf, context, name, value)).map(X509Extension)
-+ }
-+ }
-+
-+ /// Constructs an X509 extension value. See `man x509v3_config` for information on supported
-+ /// extensions and their value formats.
-+ ///
-+ /// Some extension types, such as `nid::SUBJECT_ALTERNATIVE_NAME`, require an `X509v3Context` to
-+ /// be provided.
-+ ///
-+ /// See the extension module for builder types which will construct certain common extensions.
-+ pub fn new_nid(
-+ conf: Option<&ConfRef>,
-+ context: Option<&X509v3Context<'_>>,
-+ name: Nid,
-+ value: &str,
-+ ) -> Result<X509Extension, ErrorStack> {
-+ let value = CString::new(value).unwrap();
-+ unsafe {
-+ ffi::init();
-+ let conf = conf.map_or(ptr::null_mut(), ConfRef::as_ptr);
-+ let context = context.map_or(ptr::null_mut(), X509v3Context::as_ptr);
-+ let name = name.as_raw();
-+ let value = value.as_ptr() as *mut _;
-+
-+ cvt_p(ffi::X509V3_EXT_nconf_nid(conf, context, name, value)).map(X509Extension)
-+ }
-+ }
-+
-+ /// Adds an alias for an extension
-+ ///
-+ /// # Safety
-+ ///
-+ /// This method modifies global state without locking and therefore is not thread safe
-+ #[corresponds(X509V3_EXT_add_alias)]
-+ pub unsafe fn add_alias(to: Nid, from: Nid) -> Result<(), ErrorStack> {
-+ ffi::init();
-+ cvt(ffi::X509V3_EXT_add_alias(to.as_raw(), from.as_raw())).map(|_| ())
-+ }
-+}
-+
-+/// A builder used to construct an `X509Name`.
-+pub struct X509NameBuilder(X509Name);
-+
-+impl X509NameBuilder {
-+ /// Creates a new builder.
-+ pub fn new() -> Result<X509NameBuilder, ErrorStack> {
-+ unsafe {
-+ ffi::init();
-+ cvt_p(ffi::X509_NAME_new()).map(|p| X509NameBuilder(X509Name(p)))
-+ }
-+ }
-+
-+ /// Add a name entry
-+ #[corresponds(X509_NAME_add_entry)]
-+ #[cfg(any(ossl101, libressl350))]
-+ pub fn append_entry(&mut self, ne: &X509NameEntryRef) -> std::result::Result<(), ErrorStack> {
-+ unsafe {
-+ cvt(ffi::X509_NAME_add_entry(
-+ self.0.as_ptr(),
-+ ne.as_ptr(),
-+ -1,
-+ 0,
-+ ))
-+ .map(|_| ())
-+ }
-+ }
-+
-+ /// Add a field entry by str.
-+ ///
-+ /// This corresponds to [`X509_NAME_add_entry_by_txt`].
-+ ///
-+ /// [`X509_NAME_add_entry_by_txt`]: https://www.openssl.org/docs/manmaster/crypto/X509_NAME_add_entry_by_txt.html
-+ pub fn append_entry_by_text(&mut self, field: &str, value: &str) -> Result<(), ErrorStack> {
-+ unsafe {
-+ let field = CString::new(field).unwrap();
-+ assert!(value.len() <= c_int::max_value() as usize);
-+ cvt(ffi::X509_NAME_add_entry_by_txt(
-+ self.0.as_ptr(),
-+ field.as_ptr() as *mut _,
-+ ffi::MBSTRING_UTF8,
-+ value.as_ptr(),
-+ value.len() as c_int,
-+ -1,
-+ 0,
-+ ))
-+ .map(|_| ())
-+ }
-+ }
-+
-+ /// Add a field entry by str with a specific type.
-+ ///
-+ /// This corresponds to [`X509_NAME_add_entry_by_txt`].
-+ ///
-+ /// [`X509_NAME_add_entry_by_txt`]: https://www.openssl.org/docs/manmaster/crypto/X509_NAME_add_entry_by_txt.html
-+ pub fn append_entry_by_text_with_type(
-+ &mut self,
-+ field: &str,
-+ value: &str,
-+ ty: Asn1Type,
-+ ) -> Result<(), ErrorStack> {
-+ unsafe {
-+ let field = CString::new(field).unwrap();
-+ assert!(value.len() <= c_int::max_value() as usize);
-+ cvt(ffi::X509_NAME_add_entry_by_txt(
-+ self.0.as_ptr(),
-+ field.as_ptr() as *mut _,
-+ ty.as_raw(),
-+ value.as_ptr(),
-+ value.len() as c_int,
-+ -1,
-+ 0,
-+ ))
-+ .map(|_| ())
-+ }
-+ }
-+
-+ /// Add a field entry by NID.
-+ ///
-+ /// This corresponds to [`X509_NAME_add_entry_by_NID`].
-+ ///
-+ /// [`X509_NAME_add_entry_by_NID`]: https://www.openssl.org/docs/manmaster/crypto/X509_NAME_add_entry_by_NID.html
-+ pub fn append_entry_by_nid(&mut self, field: Nid, value: &str) -> Result<(), ErrorStack> {
-+ unsafe {
-+ assert!(value.len() <= c_int::max_value() as usize);
-+ cvt(ffi::X509_NAME_add_entry_by_NID(
-+ self.0.as_ptr(),
-+ field.as_raw(),
-+ ffi::MBSTRING_UTF8,
-+ value.as_ptr() as *mut _,
-+ value.len() as c_int,
-+ -1,
-+ 0,
-+ ))
-+ .map(|_| ())
-+ }
-+ }
-+
-+ /// Add a field entry by NID with a specific type.
-+ ///
-+ /// This corresponds to [`X509_NAME_add_entry_by_NID`].
-+ ///
-+ /// [`X509_NAME_add_entry_by_NID`]: https://www.openssl.org/docs/manmaster/crypto/X509_NAME_add_entry_by_NID.html
-+ pub fn append_entry_by_nid_with_type(
-+ &mut self,
-+ field: Nid,
-+ value: &str,
-+ ty: Asn1Type,
-+ ) -> Result<(), ErrorStack> {
-+ unsafe {
-+ assert!(value.len() <= c_int::max_value() as usize);
-+ cvt(ffi::X509_NAME_add_entry_by_NID(
-+ self.0.as_ptr(),
-+ field.as_raw(),
-+ ty.as_raw(),
-+ value.as_ptr() as *mut _,
-+ value.len() as c_int,
-+ -1,
-+ 0,
-+ ))
-+ .map(|_| ())
-+ }
-+ }
-+
-+ /// Return an `X509Name`.
-+ pub fn build(self) -> X509Name {
-+ self.0
-+ }
-+}
-+
-+foreign_type_and_impl_send_sync! {
-+ type CType = ffi::X509_NAME;
-+ fn drop = ffi::X509_NAME_free;
-+
-+ /// The names of an `X509` certificate.
-+ pub struct X509Name;
-+ /// Reference to `X509Name`.
-+ pub struct X509NameRef;
-+}
-+
-+impl X509Name {
-+ /// Returns a new builder.
-+ pub fn builder() -> Result<X509NameBuilder, ErrorStack> {
-+ X509NameBuilder::new()
-+ }
-+
-+ /// Loads subject names from a file containing PEM-formatted certificates.
-+ ///
-+ /// This is commonly used in conjunction with `SslContextBuilder::set_client_ca_list`.
-+ pub fn load_client_ca_file<P: AsRef<Path>>(file: P) -> Result<Stack<X509Name>, ErrorStack> {
-+ let file = CString::new(file.as_ref().as_os_str().to_str().unwrap()).unwrap();
-+ unsafe { cvt_p(ffi::SSL_load_client_CA_file(file.as_ptr())).map(|p| Stack::from_ptr(p)) }
-+ }
-+
-+ from_der! {
-+ /// Deserializes a DER-encoded X509 name structure.
-+ ///
-+ /// This corresponds to [`d2i_X509_NAME`].
-+ ///
-+ /// [`d2i_X509_NAME`]: https://www.openssl.org/docs/manmaster/man3/d2i_X509_NAME.html
-+ from_der,
-+ X509Name,
-+ ffi::d2i_X509_NAME
-+ }
-+}
-+
-+impl Stackable for X509Name {
-+ type StackType = ffi::stack_st_X509_NAME;
-+}
-+
-+impl X509NameRef {
-+ /// Returns the name entries by the nid.
-+ pub fn entries_by_nid(&self, nid: Nid) -> X509NameEntries<'_> {
-+ X509NameEntries {
-+ name: self,
-+ nid: Some(nid),
-+ loc: -1,
-+ }
-+ }
-+
-+ /// Returns an iterator over all `X509NameEntry` values
-+ pub fn entries(&self) -> X509NameEntries<'_> {
-+ X509NameEntries {
-+ name: self,
-+ nid: None,
-+ loc: -1,
-+ }
-+ }
-+
-+ /// Compare two names, like [`Ord`] but it may fail.
-+ ///
-+ /// With OpenSSL versions from 3.0.0 this may return an error if the underlying `X509_NAME_cmp`
-+ /// call fails.
-+ /// For OpenSSL versions before 3.0.0 it will never return an error, but due to a bug it may
-+ /// spuriously return `Ordering::Less` if the `X509_NAME_cmp` call fails.
-+ #[corresponds(X509_NAME_cmp)]
-+ pub fn try_cmp(&self, other: &X509NameRef) -> Result<Ordering, ErrorStack> {
-+ let cmp = unsafe { ffi::X509_NAME_cmp(self.as_ptr(), other.as_ptr()) };
-+ if cfg!(ossl300) && cmp == -2 {
-+ return Err(ErrorStack::get());
-+ }
-+ Ok(cmp.cmp(&0))
-+ }
-+
-+ /// Copies the name to a new `X509Name`.
-+ #[corresponds(X509_NAME_dup)]
-+ #[cfg(any(boringssl, ossl110, libressl270))]
-+ pub fn to_owned(&self) -> Result<X509Name, ErrorStack> {
-+ unsafe { cvt_p(ffi::X509_NAME_dup(self.as_ptr())).map(|n| X509Name::from_ptr(n)) }
-+ }
-+
-+ to_der! {
-+ /// Serializes the certificate into a DER-encoded X509 name structure.
-+ ///
-+ /// This corresponds to [`i2d_X509_NAME`].
-+ ///
-+ /// [`i2d_X509_NAME`]: https://www.openssl.org/docs/manmaster/crypto/i2d_X509_NAME.html
-+ to_der,
-+ ffi::i2d_X509_NAME
-+ }
-+}
-+
-+impl fmt::Debug for X509NameRef {
-+ fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
-+ formatter.debug_list().entries(self.entries()).finish()
-+ }
-+}
-+
-+/// A type to destructure and examine an `X509Name`.
-+pub struct X509NameEntries<'a> {
-+ name: &'a X509NameRef,
-+ nid: Option<Nid>,
-+ loc: c_int,
-+}
-+
-+impl<'a> Iterator for X509NameEntries<'a> {
-+ type Item = &'a X509NameEntryRef;
-+
-+ fn next(&mut self) -> Option<&'a X509NameEntryRef> {
-+ unsafe {
-+ match self.nid {
-+ Some(nid) => {
-+ // There is a `Nid` specified to search for
-+ self.loc =
-+ ffi::X509_NAME_get_index_by_NID(self.name.as_ptr(), nid.as_raw(), self.loc);
-+ if self.loc == -1 {
-+ return None;
-+ }
-+ }
-+ None => {
-+ // Iterate over all `Nid`s
-+ self.loc += 1;
-+ if self.loc >= ffi::X509_NAME_entry_count(self.name.as_ptr()) {
-+ return None;
-+ }
-+ }
-+ }
-+
-+ let entry = ffi::X509_NAME_get_entry(self.name.as_ptr(), self.loc);
-+
-+ Some(X509NameEntryRef::from_const_ptr_opt(entry).expect("entry must not be null"))
-+ }
-+ }
-+}
-+
-+foreign_type_and_impl_send_sync! {
-+ type CType = ffi::X509_NAME_ENTRY;
-+ fn drop = ffi::X509_NAME_ENTRY_free;
-+
-+ /// A name entry associated with a `X509Name`.
-+ pub struct X509NameEntry;
-+ /// Reference to `X509NameEntry`.
-+ pub struct X509NameEntryRef;
-+}
-+
-+impl X509NameEntryRef {
-+ /// Returns the field value of an `X509NameEntry`.
-+ ///
-+ /// This corresponds to [`X509_NAME_ENTRY_get_data`].
-+ ///
-+ /// [`X509_NAME_ENTRY_get_data`]: https://www.openssl.org/docs/manmaster/crypto/X509_NAME_ENTRY_get_data.html
-+ pub fn data(&self) -> &Asn1StringRef {
-+ unsafe {
-+ let data = ffi::X509_NAME_ENTRY_get_data(self.as_ptr());
-+ Asn1StringRef::from_ptr(data)
-+ }
-+ }
-+
-+ /// Returns the `Asn1Object` value of an `X509NameEntry`.
-+ /// This is useful for finding out about the actual `Nid` when iterating over all `X509NameEntries`.
-+ ///
-+ /// This corresponds to [`X509_NAME_ENTRY_get_object`].
-+ ///
-+ /// [`X509_NAME_ENTRY_get_object`]: https://www.openssl.org/docs/manmaster/crypto/X509_NAME_ENTRY_get_object.html
-+ pub fn object(&self) -> &Asn1ObjectRef {
-+ unsafe {
-+ let object = ffi::X509_NAME_ENTRY_get_object(self.as_ptr());
-+ Asn1ObjectRef::from_ptr(object)
-+ }
-+ }
-+}
-+
-+impl fmt::Debug for X509NameEntryRef {
-+ fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
-+ formatter.write_fmt(format_args!("{:?} = {:?}", self.object(), self.data()))
-+ }
-+}
-+
-+/// A builder used to construct an `X509Req`.
-+pub struct X509ReqBuilder(X509Req);
-+
-+impl X509ReqBuilder {
-+ /// Returns a builder for a certificate request.
-+ ///
-+ /// This corresponds to [`X509_REQ_new`].
-+ ///
-+ ///[`X509_REQ_new`]: https://www.openssl.org/docs/manmaster/crypto/X509_REQ_new.html
-+ pub fn new() -> Result<X509ReqBuilder, ErrorStack> {
-+ unsafe {
-+ ffi::init();
-+ cvt_p(ffi::X509_REQ_new()).map(|p| X509ReqBuilder(X509Req(p)))
-+ }
-+ }
-+
-+ /// Set the numerical value of the version field.
-+ ///
-+ /// This corresponds to [`X509_REQ_set_version`].
-+ ///
-+ ///[`X509_REQ_set_version`]: https://www.openssl.org/docs/manmaster/crypto/X509_REQ_set_version.html
-+ #[allow(clippy::useless_conversion)]
-+ pub fn set_version(&mut self, version: i32) -> Result<(), ErrorStack> {
-+ unsafe {
-+ cvt(ffi::X509_REQ_set_version(
-+ self.0.as_ptr(),
-+ version as c_long,
-+ ))
-+ .map(|_| ())
-+ }
-+ }
-+
-+ /// Set the issuer name.
-+ ///
-+ /// This corresponds to [`X509_REQ_set_subject_name`].
-+ ///
-+ /// [`X509_REQ_set_subject_name`]: https://www.openssl.org/docs/manmaster/crypto/X509_REQ_set_subject_name.html
-+ pub fn set_subject_name(&mut self, subject_name: &X509NameRef) -> Result<(), ErrorStack> {
-+ unsafe {
-+ cvt(ffi::X509_REQ_set_subject_name(
-+ self.0.as_ptr(),
-+ subject_name.as_ptr(),
-+ ))
-+ .map(|_| ())
-+ }
-+ }
-+
-+ /// Set the public key.
-+ ///
-+ /// This corresponds to [`X509_REQ_set_pubkey`].
-+ ///
-+ /// [`X509_REQ_set_pubkey`]: https://www.openssl.org/docs/manmaster/crypto/X509_REQ_set_pubkey.html
-+ pub fn set_pubkey<T>(&mut self, key: &PKeyRef<T>) -> Result<(), ErrorStack>
-+ where
-+ T: HasPublic,
-+ {
-+ unsafe { cvt(ffi::X509_REQ_set_pubkey(self.0.as_ptr(), key.as_ptr())).map(|_| ()) }
-+ }
-+
-+ /// Return an `X509v3Context`. This context object can be used to construct
-+ /// certain `X509` extensions.
-+ pub fn x509v3_context<'a>(&'a self, conf: Option<&'a ConfRef>) -> X509v3Context<'a> {
-+ unsafe {
-+ let mut ctx = mem::zeroed();
-+
-+ ffi::X509V3_set_ctx(
-+ &mut ctx,
-+ ptr::null_mut(),
-+ ptr::null_mut(),
-+ self.0.as_ptr(),
-+ ptr::null_mut(),
-+ 0,
-+ );
-+
-+ // nodb case taken care of since we zeroed ctx above
-+ if let Some(conf) = conf {
-+ ffi::X509V3_set_nconf(&mut ctx, conf.as_ptr());
-+ }
-+
-+ X509v3Context(ctx, PhantomData)
-+ }
-+ }
-+
-+ /// Permits any number of extension fields to be added to the certificate.
-+ pub fn add_extensions(
-+ &mut self,
-+ extensions: &StackRef<X509Extension>,
-+ ) -> Result<(), ErrorStack> {
-+ unsafe {
-+ cvt(ffi::X509_REQ_add_extensions(
-+ self.0.as_ptr(),
-+ extensions.as_ptr(),
-+ ))
-+ .map(|_| ())
-+ }
-+ }
-+
-+ /// Sign the request using a private key.
-+ ///
-+ /// This corresponds to [`X509_REQ_sign`].
-+ ///
-+ /// [`X509_REQ_sign`]: https://www.openssl.org/docs/manmaster/crypto/X509_REQ_sign.html
-+ pub fn sign<T>(&mut self, key: &PKeyRef<T>, hash: MessageDigest) -> Result<(), ErrorStack>
-+ where
-+ T: HasPrivate,
-+ {
-+ unsafe {
-+ cvt(ffi::X509_REQ_sign(
-+ self.0.as_ptr(),
-+ key.as_ptr(),
-+ hash.as_ptr(),
-+ ))
-+ .map(|_| ())
-+ }
-+ }
-+
-+ /// Sign the request using a private key without a digest.
-+ ///
-+ /// This is the only way to sign with Ed25519 keys as BoringSSL doesn't support the null
-+ /// message digest.
-+ ///
-+ /// This corresponds to [`X509_REQ_sign`].
-+ ///
-+ /// [`X509_REQ_sign`]: https://www.openssl.org/docs/man1.1.0/crypto/X509_REQ_sign.html
-+ #[cfg(boringssl)]
-+ pub fn sign_without_digest<T>(&mut self, key: &PKeyRef<T>) -> Result<(), ErrorStack>
-+ where
-+ T: HasPrivate,
-+ {
-+ unsafe {
-+ cvt(ffi::X509_REQ_sign(
-+ self.0.as_ptr(),
-+ key.as_ptr(),
-+ ptr::null(),
-+ ))
-+ .map(|_| ())
-+ }
-+ }
-+
-+ /// Returns the `X509Req`.
-+ pub fn build(self) -> X509Req {
-+ self.0
-+ }
-+}
-+
-+foreign_type_and_impl_send_sync! {
-+ type CType = ffi::X509_REQ;
-+ fn drop = ffi::X509_REQ_free;
-+
-+ /// An `X509` certificate request.
-+ pub struct X509Req;
-+ /// Reference to `X509Req`.
-+ pub struct X509ReqRef;
-+}
-+
-+impl X509Req {
-+ /// A builder for `X509Req`.
-+ pub fn builder() -> Result<X509ReqBuilder, ErrorStack> {
-+ X509ReqBuilder::new()
-+ }
-+
-+ from_pem! {
-+ /// Deserializes a PEM-encoded PKCS#10 certificate request structure.
-+ ///
-+ /// The input should have a header of `-----BEGIN CERTIFICATE REQUEST-----`.
-+ ///
-+ /// This corresponds to [`PEM_read_bio_X509_REQ`].
-+ ///
-+ /// [`PEM_read_bio_X509_REQ`]: https://www.openssl.org/docs/manmaster/crypto/PEM_read_bio_X509_REQ.html
-+ from_pem,
-+ X509Req,
-+ ffi::PEM_read_bio_X509_REQ
-+ }
-+
-+ from_der! {
-+ /// Deserializes a DER-encoded PKCS#10 certificate request structure.
-+ ///
-+ /// This corresponds to [`d2i_X509_REQ`].
-+ ///
-+ /// [`d2i_X509_REQ`]: https://www.openssl.org/docs/manmaster/crypto/d2i_X509_REQ.html
-+ from_der,
-+ X509Req,
-+ ffi::d2i_X509_REQ
-+ }
-+}
-+
-+impl X509ReqRef {
-+ to_pem! {
-+ /// Serializes the certificate request to a PEM-encoded PKCS#10 structure.
-+ ///
-+ /// The output will have a header of `-----BEGIN CERTIFICATE REQUEST-----`.
-+ ///
-+ /// This corresponds to [`PEM_write_bio_X509_REQ`].
-+ ///
-+ /// [`PEM_write_bio_X509_REQ`]: https://www.openssl.org/docs/manmaster/crypto/PEM_write_bio_X509_REQ.html
-+ to_pem,
-+ ffi::PEM_write_bio_X509_REQ
-+ }
-+
-+ to_der! {
-+ /// Serializes the certificate request to a DER-encoded PKCS#10 structure.
-+ ///
-+ /// This corresponds to [`i2d_X509_REQ`].
-+ ///
-+ /// [`i2d_X509_REQ`]: https://www.openssl.org/docs/manmaster/crypto/i2d_X509_REQ.html
-+ to_der,
-+ ffi::i2d_X509_REQ
-+ }
-+
-+ to_pem! {
-+ /// Converts the request to human readable text.
-+ #[corresponds(X509_Req_print)]
-+ to_text,
-+ ffi::X509_REQ_print
-+ }
-+
-+ /// Returns the numerical value of the version field of the certificate request.
-+ ///
-+ /// This corresponds to [`X509_REQ_get_version`]
-+ ///
-+ /// [`X509_REQ_get_version`]: https://www.openssl.org/docs/manmaster/crypto/X509_REQ_get_version.html
-+ pub fn version(&self) -> i32 {
-+ unsafe { X509_REQ_get_version(self.as_ptr()) as i32 }
-+ }
-+
-+ /// Returns the subject name of the certificate request.
-+ ///
-+ /// This corresponds to [`X509_REQ_get_subject_name`]
-+ ///
-+ /// [`X509_REQ_get_subject_name`]: https://www.openssl.org/docs/manmaster/crypto/X509_REQ_get_subject_name.html
-+ pub fn subject_name(&self) -> &X509NameRef {
-+ unsafe {
-+ let name = X509_REQ_get_subject_name(self.as_ptr());
-+ X509NameRef::from_const_ptr_opt(name).expect("subject name must not be null")
-+ }
-+ }
-+
-+ /// Returns the public key of the certificate request.
-+ ///
-+ /// This corresponds to [`X509_REQ_get_pubkey"]
-+ ///
-+ /// [`X509_REQ_get_pubkey`]: https://www.openssl.org/docs/manmaster/crypto/X509_REQ_get_pubkey.html
-+ pub fn public_key(&self) -> Result<PKey<Public>, ErrorStack> {
-+ unsafe {
-+ let key = cvt_p(ffi::X509_REQ_get_pubkey(self.as_ptr()))?;
-+ Ok(PKey::from_ptr(key))
-+ }
-+ }
-+
-+ /// Check if the certificate request is signed using the given public key.
-+ ///
-+ /// Returns `true` if verification succeeds.
-+ ///
-+ /// This corresponds to [`X509_REQ_verify"].
-+ ///
-+ /// [`X509_REQ_verify`]: https://www.openssl.org/docs/manmaster/crypto/X509_REQ_verify.html
-+ pub fn verify<T>(&self, key: &PKeyRef<T>) -> Result<bool, ErrorStack>
-+ where
-+ T: HasPublic,
-+ {
-+ unsafe { cvt_n(ffi::X509_REQ_verify(self.as_ptr(), key.as_ptr())).map(|n| n != 0) }
-+ }
-+
-+ /// Returns the extensions of the certificate request.
-+ ///
-+ /// This corresponds to [`X509_REQ_get_extensions"]
-+ pub fn extensions(&self) -> Result<Stack<X509Extension>, ErrorStack> {
-+ unsafe {
-+ let extensions = cvt_p(ffi::X509_REQ_get_extensions(self.as_ptr()))?;
-+ Ok(Stack::from_ptr(extensions))
-+ }
-+ }
-+}
-+
-+/// The result of peer certificate verification.
-+#[derive(Copy, Clone, PartialEq, Eq)]
-+pub struct X509VerifyResult(c_int);
-+
-+impl fmt::Debug for X509VerifyResult {
-+ fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result {
-+ fmt.debug_struct("X509VerifyResult")
-+ .field("code", &self.0)
-+ .field("error", &self.error_string())
-+ .finish()
-+ }
-+}
-+
-+impl fmt::Display for X509VerifyResult {
-+ fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result {
-+ fmt.write_str(self.error_string())
-+ }
-+}
-+
-+impl Error for X509VerifyResult {}
-+
-+impl X509VerifyResult {
-+ /// Creates an `X509VerifyResult` from a raw error number.
-+ ///
-+ /// # Safety
-+ ///
-+ /// Some methods on `X509VerifyResult` are not thread safe if the error
-+ /// number is invalid.
-+ pub unsafe fn from_raw(err: c_int) -> X509VerifyResult {
-+ X509VerifyResult(err)
-+ }
-+
-+ /// Return the integer representation of an `X509VerifyResult`.
-+ #[allow(clippy::trivially_copy_pass_by_ref)]
-+ pub fn as_raw(&self) -> c_int {
-+ self.0
-+ }
-+
-+ /// Return a human readable error string from the verification error.
-+ ///
-+ /// This corresponds to [`X509_verify_cert_error_string`].
-+ ///
-+ /// [`X509_verify_cert_error_string`]: https://www.openssl.org/docs/manmaster/crypto/X509_verify_cert_error_string.html
-+ #[allow(clippy::trivially_copy_pass_by_ref)]
-+ pub fn error_string(&self) -> &'static str {
-+ ffi::init();
-+
-+ unsafe {
-+ let s = ffi::X509_verify_cert_error_string(self.0 as c_long);
-+ str::from_utf8(CStr::from_ptr(s).to_bytes()).unwrap()
-+ }
-+ }
-+
-+ /// Successful peer certificate verification.
-+ pub const OK: X509VerifyResult = X509VerifyResult(ffi::X509_V_OK);
-+ /// Application verification failure.
-+ pub const APPLICATION_VERIFICATION: X509VerifyResult =
-+ X509VerifyResult(ffi::X509_V_ERR_APPLICATION_VERIFICATION);
-+}
-+
-+foreign_type_and_impl_send_sync! {
-+ type CType = ffi::GENERAL_NAME;
-+ fn drop = ffi::GENERAL_NAME_free;
-+
-+ /// An `X509` certificate alternative names.
-+ pub struct GeneralName;
-+ /// Reference to `GeneralName`.
-+ pub struct GeneralNameRef;
-+}
-+
-+impl GeneralNameRef {
-+ fn ia5_string(&self, ffi_type: c_int) -> Option<&str> {
-+ unsafe {
-+ if (*self.as_ptr()).type_ != ffi_type {
-+ return None;
-+ }
-+
-+ #[cfg(boringssl)]
-+ let d = (*self.as_ptr()).d.ptr;
-+ #[cfg(not(boringssl))]
-+ let d = (*self.as_ptr()).d;
-+
-+ let ptr = ASN1_STRING_get0_data(d as *mut _);
-+ let len = ffi::ASN1_STRING_length(d as *mut _);
-+
-+ let slice = slice::from_raw_parts(ptr as *const u8, len as usize);
-+ // IA5Strings are stated to be ASCII (specifically IA5). Hopefully
-+ // OpenSSL checks that when loading a certificate but if not we'll
-+ // use this instead of from_utf8_unchecked just in case.
-+ str::from_utf8(slice).ok()
-+ }
-+ }
-+
-+ /// Returns the contents of this `GeneralName` if it is an `rfc822Name`.
-+ pub fn email(&self) -> Option<&str> {
-+ self.ia5_string(ffi::GEN_EMAIL)
-+ }
-+
-+ /// Returns the contents of this `GeneralName` if it is a `dNSName`.
-+ pub fn dnsname(&self) -> Option<&str> {
-+ self.ia5_string(ffi::GEN_DNS)
-+ }
-+
-+ /// Returns the contents of this `GeneralName` if it is an `uniformResourceIdentifier`.
-+ pub fn uri(&self) -> Option<&str> {
-+ self.ia5_string(ffi::GEN_URI)
-+ }
-+
-+ /// Returns the contents of this `GeneralName` if it is an `iPAddress`.
-+ pub fn ipaddress(&self) -> Option<&[u8]> {
-+ unsafe {
-+ if (*self.as_ptr()).type_ != ffi::GEN_IPADD {
-+ return None;
-+ }
-+ #[cfg(boringssl)]
-+ let d: *const ffi::ASN1_STRING = std::mem::transmute((*self.as_ptr()).d);
-+ #[cfg(not(boringssl))]
-+ let d = (*self.as_ptr()).d;
-+
-+ let ptr = ASN1_STRING_get0_data(d as *mut _);
-+ let len = ffi::ASN1_STRING_length(d as *mut _);
-+
-+ Some(slice::from_raw_parts(ptr as *const u8, len as usize))
-+ }
-+ }
-+}
-+
-+impl fmt::Debug for GeneralNameRef {
-+ fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
-+ if let Some(email) = self.email() {
-+ formatter.write_str(email)
-+ } else if let Some(dnsname) = self.dnsname() {
-+ formatter.write_str(dnsname)
-+ } else if let Some(uri) = self.uri() {
-+ formatter.write_str(uri)
-+ } else if let Some(ipaddress) = self.ipaddress() {
-+ let address = <[u8; 16]>::try_from(ipaddress)
-+ .map(IpAddr::from)
-+ .or_else(|_| <[u8; 4]>::try_from(ipaddress).map(IpAddr::from));
-+ match address {
-+ Ok(a) => fmt::Debug::fmt(&a, formatter),
-+ Err(_) => fmt::Debug::fmt(ipaddress, formatter),
-+ }
-+ } else {
-+ formatter.write_str("(empty)")
-+ }
-+ }
-+}
-+
-+impl Stackable for GeneralName {
-+ type StackType = ffi::stack_st_GENERAL_NAME;
-+}
-+
-+foreign_type_and_impl_send_sync! {
-+ type CType = ffi::ACCESS_DESCRIPTION;
-+ fn drop = ffi::ACCESS_DESCRIPTION_free;
-+
-+ /// `AccessDescription` of certificate authority information.
-+ pub struct AccessDescription;
-+ /// Reference to `AccessDescription`.
-+ pub struct AccessDescriptionRef;
-+}
-+
-+impl AccessDescriptionRef {
-+ /// Returns the access method OID.
-+ pub fn method(&self) -> &Asn1ObjectRef {
-+ unsafe { Asn1ObjectRef::from_ptr((*self.as_ptr()).method) }
-+ }
-+
-+ // Returns the access location.
-+ pub fn location(&self) -> &GeneralNameRef {
-+ unsafe { GeneralNameRef::from_ptr((*self.as_ptr()).location) }
-+ }
-+}
-+
-+impl Stackable for AccessDescription {
-+ type StackType = ffi::stack_st_ACCESS_DESCRIPTION;
-+}
-+
-+foreign_type_and_impl_send_sync! {
-+ type CType = ffi::X509_ALGOR;
-+ fn drop = ffi::X509_ALGOR_free;
-+
-+ /// An `X509` certificate signature algorithm.
-+ pub struct X509Algorithm;
-+ /// Reference to `X509Algorithm`.
-+ pub struct X509AlgorithmRef;
-+}
-+
-+impl X509AlgorithmRef {
-+ /// Returns the ASN.1 OID of this algorithm.
-+ pub fn object(&self) -> &Asn1ObjectRef {
-+ unsafe {
-+ let mut oid = ptr::null();
-+ X509_ALGOR_get0(&mut oid, ptr::null_mut(), ptr::null_mut(), self.as_ptr());
-+ Asn1ObjectRef::from_const_ptr_opt(oid).expect("algorithm oid must not be null")
-+ }
-+ }
-+}
-+
-+foreign_type_and_impl_send_sync! {
-+ type CType = ffi::X509_OBJECT;
-+ fn drop = X509_OBJECT_free;
-+
-+ /// An `X509` or an X509 certificate revocation list.
-+ pub struct X509Object;
-+ /// Reference to `X509Object`
-+ pub struct X509ObjectRef;
-+}
-+
-+impl X509ObjectRef {
-+ pub fn x509(&self) -> Option<&X509Ref> {
-+ unsafe {
-+ let ptr = X509_OBJECT_get0_X509(self.as_ptr());
-+ X509Ref::from_const_ptr_opt(ptr)
-+ }
-+ }
-+}
-+
-+impl Stackable for X509Object {
-+ type StackType = ffi::stack_st_X509_OBJECT;
-+}
-+
-+cfg_if! {
-+ if #[cfg(any(boringssl, ossl110, libressl273))] {
-+ use ffi::{X509_getm_notAfter, X509_getm_notBefore, X509_up_ref, X509_get0_signature};
-+ } else {
-+ #[allow(bad_style)]
-+ unsafe fn X509_getm_notAfter(x: *mut ffi::X509) -> *mut ffi::ASN1_TIME {
-+ (*(*(*x).cert_info).validity).notAfter
-+ }
-+
-+ #[allow(bad_style)]
-+ unsafe fn X509_getm_notBefore(x: *mut ffi::X509) -> *mut ffi::ASN1_TIME {
-+ (*(*(*x).cert_info).validity).notBefore
-+ }
-+
-+ #[allow(bad_style)]
-+ unsafe fn X509_up_ref(x: *mut ffi::X509) {
-+ ffi::CRYPTO_add_lock(
-+ &mut (*x).references,
-+ 1,
-+ ffi::CRYPTO_LOCK_X509,
-+ "mod.rs\0".as_ptr() as *const _,
-+ line!() as c_int,
-+ );
-+ }
-+
-+ #[allow(bad_style)]
-+ unsafe fn X509_get0_signature(
-+ psig: *mut *const ffi::ASN1_BIT_STRING,
-+ palg: *mut *const ffi::X509_ALGOR,
-+ x: *const ffi::X509,
-+ ) {
-+ if !psig.is_null() {
-+ *psig = (*x).signature;
-+ }
-+ if !palg.is_null() {
-+ *palg = (*x).sig_alg;
-+ }
-+ }
-+ }
-+}
-+
-+cfg_if! {
-+ if #[cfg(any(boringssl, ossl110, libressl350))] {
-+ use ffi::{
-+ X509_ALGOR_get0, ASN1_STRING_get0_data, X509_STORE_CTX_get0_chain, X509_set1_notAfter,
-+ X509_set1_notBefore, X509_REQ_get_version, X509_REQ_get_subject_name,
-+ };
-+ } else {
-+ use ffi::{
-+ ASN1_STRING_data as ASN1_STRING_get0_data,
-+ X509_STORE_CTX_get_chain as X509_STORE_CTX_get0_chain,
-+ X509_set_notAfter as X509_set1_notAfter,
-+ X509_set_notBefore as X509_set1_notBefore,
-+ };
-+
-+ #[allow(bad_style)]
-+ unsafe fn X509_REQ_get_version(x: *mut ffi::X509_REQ) -> ::libc::c_long {
-+ ffi::ASN1_INTEGER_get((*(*x).req_info).version)
-+ }
-+
-+ #[allow(bad_style)]
-+ unsafe fn X509_REQ_get_subject_name(x: *mut ffi::X509_REQ) -> *mut ::ffi::X509_NAME {
-+ (*(*x).req_info).subject
-+ }
-+
-+ #[allow(bad_style)]
-+ unsafe fn X509_ALGOR_get0(
-+ paobj: *mut *const ffi::ASN1_OBJECT,
-+ pptype: *mut c_int,
-+ pval: *mut *mut ::libc::c_void,
-+ alg: *const ffi::X509_ALGOR,
-+ ) {
-+ if !paobj.is_null() {
-+ *paobj = (*alg).algorithm;
-+ }
-+ assert!(pptype.is_null());
-+ assert!(pval.is_null());
-+ }
-+ }
-+}
-+
-+cfg_if! {
-+ if #[cfg(any(ossl110, boringssl, libressl270))] {
-+ use ffi::X509_OBJECT_get0_X509;
-+ } else {
-+ #[allow(bad_style)]
-+ unsafe fn X509_OBJECT_get0_X509(x: *mut ffi::X509_OBJECT) -> *mut ffi::X509 {
-+ if (*x).type_ == ffi::X509_LU_X509 {
-+ (*x).data.x509
-+ } else {
-+ ptr::null_mut()
-+ }
-+ }
-+ }
-+}
-+
-+cfg_if! {
-+ if #[cfg(any(ossl110, libressl350))] {
-+ use ffi::X509_OBJECT_free;
-+ } else if #[cfg(boringssl)] {
-+ use ffi::X509_OBJECT_free_contents as X509_OBJECT_free;
-+ } else {
-+ #[allow(bad_style)]
-+ unsafe fn X509_OBJECT_free(x: *mut ffi::X509_OBJECT) {
-+ ffi::X509_OBJECT_free_contents(x);
-+ ffi::CRYPTO_free(x as *mut libc::c_void);
-+ }
-+ }
-+}
-+
-+#[derive(Copy, Clone, PartialEq, Eq)]
-+pub struct X509PurposeId(c_int);
-+
-+impl X509PurposeId {
-+ pub const SSL_CLIENT: X509PurposeId = X509PurposeId(ffi::X509_PURPOSE_SSL_CLIENT);
-+ pub const SSL_SERVER: X509PurposeId = X509PurposeId(ffi::X509_PURPOSE_SSL_SERVER);
-+ pub const NS_SSL_SERVER: X509PurposeId = X509PurposeId(ffi::X509_PURPOSE_NS_SSL_SERVER);
-+ pub const SMIME_SIGN: X509PurposeId = X509PurposeId(ffi::X509_PURPOSE_SMIME_SIGN);
-+ pub const SMIME_ENCRYPT: X509PurposeId = X509PurposeId(ffi::X509_PURPOSE_SMIME_ENCRYPT);
-+ pub const CRL_SIGN: X509PurposeId = X509PurposeId(ffi::X509_PURPOSE_CRL_SIGN);
-+ pub const ANY: X509PurposeId = X509PurposeId(ffi::X509_PURPOSE_ANY);
-+ pub const OCSP_HELPER: X509PurposeId = X509PurposeId(ffi::X509_PURPOSE_OCSP_HELPER);
-+ pub const TIMESTAMP_SIGN: X509PurposeId = X509PurposeId(ffi::X509_PURPOSE_TIMESTAMP_SIGN);
-+
-+ /// Constructs an `X509PurposeId` from a raw OpenSSL value.
-+ pub fn from_raw(id: c_int) -> Self {
-+ X509PurposeId(id)
-+ }
-+
-+ /// Returns the raw OpenSSL value represented by this type.
-+ pub fn as_raw(&self) -> c_int {
-+ self.0
-+ }
-+}
-+
-+/// A reference to an [`X509_PURPOSE`].
-+pub struct X509PurposeRef(Opaque);
-+
-+/// Implements a wrapper type for the static `X509_PURPOSE` table in OpenSSL.
-+impl ForeignTypeRef for X509PurposeRef {
-+ type CType = ffi::X509_PURPOSE;
-+}
-+
-+impl X509PurposeRef {
-+ /// Get the internal table index of an X509_PURPOSE for a given short name. Valid short
-+ /// names include
-+ /// - "sslclient",
-+ /// - "sslserver",
-+ /// - "nssslserver",
-+ /// - "smimesign",
-+ /// - "smimeencrypt",
-+ /// - "crlsign",
-+ /// - "any",
-+ /// - "ocsphelper",
-+ /// - "timestampsign"
-+ /// The index can be used with `X509PurposeRef::from_idx()` to get the purpose.
-+ #[allow(clippy::unnecessary_cast)]
-+ pub fn get_by_sname(sname: &str) -> Result<c_int, ErrorStack> {
-+ unsafe {
-+ let sname = CString::new(sname).unwrap();
-+ cfg_if! {
-+ if #[cfg(any(ossl110, libressl280))] {
-+ let purpose = cvt_n(ffi::X509_PURPOSE_get_by_sname(sname.as_ptr() as *const _))?;
-+ } else {
-+ let purpose = cvt_n(ffi::X509_PURPOSE_get_by_sname(sname.as_ptr() as *mut _))?;
-+ }
-+ }
-+ Ok(purpose)
-+ }
-+ }
-+ /// Get an `X509PurposeRef` for a given index value. The index can be obtained from e.g.
-+ /// `X509PurposeRef::get_by_sname()`.
-+ #[corresponds(X509_PURPOSE_get0)]
-+ pub fn from_idx(idx: c_int) -> Result<&'static X509PurposeRef, ErrorStack> {
-+ unsafe {
-+ let ptr = cvt_p(ffi::X509_PURPOSE_get0(idx))?;
-+ Ok(X509PurposeRef::from_ptr(ptr))
-+ }
-+ }
-+
-+ /// Get the purpose value from an X509Purpose structure. This value is one of
-+ /// - `X509_PURPOSE_SSL_CLIENT`
-+ /// - `X509_PURPOSE_SSL_SERVER`
-+ /// - `X509_PURPOSE_NS_SSL_SERVER`
-+ /// - `X509_PURPOSE_SMIME_SIGN`
-+ /// - `X509_PURPOSE_SMIME_ENCRYPT`
-+ /// - `X509_PURPOSE_CRL_SIGN`
-+ /// - `X509_PURPOSE_ANY`
-+ /// - `X509_PURPOSE_OCSP_HELPER`
-+ /// - `X509_PURPOSE_TIMESTAMP_SIGN`
-+ pub fn purpose(&self) -> X509PurposeId {
-+ unsafe {
-+ let x509_purpose: *mut ffi::X509_PURPOSE = self.as_ptr();
-+ X509PurposeId::from_raw((*x509_purpose).purpose)
-+ }
-+ }
-+}
---
-2.40.1.495.gc816e09b53d-goog
-
diff --git a/nearby/scripts/openssl-patches/0001-Apply-android-patches.patch b/nearby/scripts/openssl-patches/0001-Apply-android-patches.patch
new file mode 100644
index 0000000..0ee52b1
--- /dev/null
+++ b/nearby/scripts/openssl-patches/0001-Apply-android-patches.patch
@@ -0,0 +1,1017 @@
+From 5c9103f02cb56f0f04444b16dd67eeaa05429d47 Mon Sep 17 00:00:00 2001
+From: Nabil Wadih <nwadih@google.com>
+Date: Thu, 24 Aug 2023 15:51:26 -0700
+Subject: [PATCH] Apply android patches
+
+---
+ openssl/.cargo/config.toml | 2 +
+ openssl/src/asn1.rs | 2 +-
+ openssl/src/bio.rs | 6 +-
+ openssl/src/bn.rs | 2 +-
+ openssl/src/cipher.rs | 4 +
+ openssl/src/dh.rs | 2 +-
+ openssl/src/dsa.rs | 5 +-
+ openssl/src/ec.rs | 20 ++++
+ openssl/src/ecdsa.rs | 2 +-
+ openssl/src/encrypt.rs | 4 +-
+ openssl/src/hash.rs | 2 +-
+ openssl/src/hkdf.rs | 89 +++++++++++++++
+ openssl/src/hmac.rs | 217 +++++++++++++++++++++++++++++++++++++
+ openssl/src/lib.rs | 12 ++
+ openssl/src/md_ctx.rs | 2 +-
+ openssl/src/pkey.rs | 22 ++--
+ openssl/src/pkey_ctx.rs | 21 +++-
+ openssl/src/rsa.rs | 2 +-
+ openssl/src/sign.rs | 10 +-
+ openssl/src/symm.rs | 7 +-
+ openssl/src/x509/mod.rs | 52 +++++++--
+ 21 files changed, 439 insertions(+), 46 deletions(-)
+ create mode 100644 openssl/.cargo/config.toml
+ create mode 100644 openssl/src/hkdf.rs
+ create mode 100644 openssl/src/hmac.rs
+
+diff --git a/openssl/.cargo/config.toml b/openssl/.cargo/config.toml
+new file mode 100644
+index 00000000..e2b197d8
+--- /dev/null
++++ b/openssl/.cargo/config.toml
+@@ -0,0 +1,2 @@
++[patch.crates-io]
++bssl-ffi = { package = "bssl-sys", version = "0.1.0", path = "../../../boringssl/build/rust", optional=true }
+diff --git a/openssl/src/asn1.rs b/openssl/src/asn1.rs
+index b02f9ac4..939a1732 100644
+--- a/openssl/src/asn1.rs
++++ b/openssl/src/asn1.rs
+@@ -651,7 +651,7 @@ impl fmt::Debug for Asn1ObjectRef {
+ }
+
+ cfg_if! {
+- if #[cfg(any(ossl110, libressl273))] {
++ if #[cfg(any(ossl110, libressl273, boringssl))] {
+ use ffi::ASN1_STRING_get0_data;
+ } else {
+ #[allow(bad_style)]
+diff --git a/openssl/src/bio.rs b/openssl/src/bio.rs
+index 6a72552a..03242188 100644
+--- a/openssl/src/bio.rs
++++ b/openssl/src/bio.rs
+@@ -4,7 +4,7 @@ use std::marker::PhantomData;
+ use std::ptr;
+ use std::slice;
+
+-use crate::cvt_p;
++use crate::{cvt_p, SignedLenType};
+ use crate::error::ErrorStack;
+
+ pub struct MemBioSlice<'a>(*mut ffi::BIO, PhantomData<&'a [u8]>);
+@@ -25,7 +25,7 @@ impl<'a> MemBioSlice<'a> {
+ let bio = unsafe {
+ cvt_p(BIO_new_mem_buf(
+ buf.as_ptr() as *const _,
+- buf.len() as c_int,
++ buf.len() as SignedLenType,
+ ))?
+ };
+
+@@ -78,7 +78,7 @@ cfg_if! {
+ use ffi::BIO_new_mem_buf;
+ } else {
+ #[allow(bad_style)]
+- unsafe fn BIO_new_mem_buf(buf: *const ::libc::c_void, len: ::libc::c_int) -> *mut ffi::BIO {
++ unsafe fn BIO_new_mem_buf(buf: *const ::libc::c_void, len: SignedLenType) -> *mut ffi::BIO {
+ ffi::BIO_new_mem_buf(buf as *mut _, len)
+ }
+ }
+diff --git a/openssl/src/bn.rs b/openssl/src/bn.rs
+index 1cd00dd4..dbd7ae94 100644
+--- a/openssl/src/bn.rs
++++ b/openssl/src/bn.rs
+@@ -814,7 +814,7 @@ impl BigNumRef {
+ /// assert_eq!(&bn_vec, &[0, 0, 0x45, 0x43]);
+ /// ```
+ #[corresponds(BN_bn2binpad)]
+- #[cfg(ossl110)]
++ #[cfg(any(boringssl, ossl110))]
+ pub fn to_vec_padded(&self, pad_to: i32) -> Result<Vec<u8>, ErrorStack> {
+ let mut v = Vec::with_capacity(pad_to as usize);
+ unsafe {
+diff --git a/openssl/src/cipher.rs b/openssl/src/cipher.rs
+index ab5f49d2..84a82654 100644
+--- a/openssl/src/cipher.rs
++++ b/openssl/src/cipher.rs
+@@ -208,6 +208,7 @@ impl Cipher {
+ unsafe { CipherRef::from_ptr(ffi::EVP_aes_192_cfb1() as *mut _) }
+ }
+
++ #[cfg(not(boringssl))]
+ pub fn aes_192_cfb128() -> &'static CipherRef {
+ unsafe { CipherRef::from_ptr(ffi::EVP_aes_192_cfb128() as *mut _) }
+ }
+@@ -253,6 +254,7 @@ impl Cipher {
+ unsafe { CipherRef::from_ptr(ffi::EVP_aes_256_cfb1() as *mut _) }
+ }
+
++ #[cfg(not(boringssl))]
+ pub fn aes_256_cfb128() -> &'static CipherRef {
+ unsafe { CipherRef::from_ptr(ffi::EVP_aes_256_cfb128() as *mut _) }
+ }
+@@ -282,11 +284,13 @@ impl Cipher {
+ }
+
+ #[cfg(not(osslconf = "OPENSSL_NO_BF"))]
++ #[cfg(not(boringssl))]
+ pub fn bf_cbc() -> &'static CipherRef {
+ unsafe { CipherRef::from_ptr(ffi::EVP_bf_cbc() as *mut _) }
+ }
+
+ #[cfg(not(osslconf = "OPENSSL_NO_BF"))]
++ #[cfg(not(boringssl))]
+ pub fn bf_ecb() -> &'static CipherRef {
+ unsafe { CipherRef::from_ptr(ffi::EVP_bf_ecb() as *mut _) }
+ }
+diff --git a/openssl/src/dh.rs b/openssl/src/dh.rs
+index 12170b99..e781543e 100644
+--- a/openssl/src/dh.rs
++++ b/openssl/src/dh.rs
+@@ -239,7 +239,7 @@ where
+ }
+
+ cfg_if! {
+- if #[cfg(any(ossl110, libressl270))] {
++ if #[cfg(any(ossl110, libressl270, boringssl))] {
+ use ffi::{DH_set0_pqg, DH_get0_pqg, DH_get0_key, DH_set0_key};
+ } else {
+ #[allow(bad_style)]
+diff --git a/openssl/src/dsa.rs b/openssl/src/dsa.rs
+index 5f59ba8a..0aceeb55 100644
+--- a/openssl/src/dsa.rs
++++ b/openssl/src/dsa.rs
+@@ -7,6 +7,7 @@
+
+ use cfg_if::cfg_if;
+ use foreign_types::{ForeignType, ForeignTypeRef};
++#[cfg(not(boringssl))]
+ use libc::c_int;
+ use std::fmt;
+ use std::mem;
+@@ -283,7 +284,7 @@ impl<T> fmt::Debug for Dsa<T> {
+ }
+
+ cfg_if! {
+- if #[cfg(any(ossl110, libressl273))] {
++ if #[cfg(any(ossl110, libressl273, boringssl))] {
+ use ffi::{DSA_get0_key, DSA_get0_pqg, DSA_set0_key, DSA_set0_pqg};
+ } else {
+ #[allow(bad_style)]
+@@ -462,7 +463,7 @@ impl DsaSigRef {
+ }
+
+ cfg_if! {
+- if #[cfg(any(ossl110, libressl273))] {
++ if #[cfg(any(ossl110, libressl273, boringssl))] {
+ use ffi::{DSA_SIG_set0, DSA_SIG_get0};
+ } else {
+ #[allow(bad_style)]
+diff --git a/openssl/src/ec.rs b/openssl/src/ec.rs
+index 24b38322..20785428 100644
+--- a/openssl/src/ec.rs
++++ b/openssl/src/ec.rs
+@@ -954,6 +954,26 @@ impl EcKey<Private> {
+ EcKey<Private>,
+ ffi::d2i_ECPrivateKey
+ }
++
++ /// Decodes a DER-encoded elliptic curve private key structure for the specified curve.
++ #[corresponds(EC_KEY_parse_private_key)]
++ #[cfg(boringssl)]
++ pub fn private_key_from_der_for_group(
++ der: &[u8],
++ group: &EcGroupRef,
++ ) -> Result<EcKey<Private>, ErrorStack> {
++ unsafe {
++ let mut cbs = ffi::CBS {
++ data: der.as_ptr(),
++ len: der.len(),
++ };
++ cvt_p(ffi::EC_KEY_parse_private_key(
++ &mut cbs as *mut ffi::CBS,
++ group.as_ptr(),
++ ))
++ .map(|p| EcKey::from_ptr(p))
++ }
++ }
+ }
+
+ impl<T> Clone for EcKey<T> {
+diff --git a/openssl/src/ecdsa.rs b/openssl/src/ecdsa.rs
+index 0a960e7b..f3b27b39 100644
+--- a/openssl/src/ecdsa.rs
++++ b/openssl/src/ecdsa.rs
+@@ -110,7 +110,7 @@ impl EcdsaSigRef {
+ }
+
+ cfg_if! {
+- if #[cfg(any(ossl110, libressl273))] {
++ if #[cfg(any(ossl110, libressl273, boringssl))] {
+ use ffi::{ECDSA_SIG_set0, ECDSA_SIG_get0};
+ } else {
+ #[allow(bad_style)]
+diff --git a/openssl/src/encrypt.rs b/openssl/src/encrypt.rs
+index 3cb10fcc..34a9eb8b 100644
+--- a/openssl/src/encrypt.rs
++++ b/openssl/src/encrypt.rs
+@@ -148,7 +148,7 @@ impl<'a> Encrypter<'a> {
+ /// This corresponds to [`EVP_PKEY_CTX_set_rsa_oaep_md`].
+ ///
+ /// [`EVP_PKEY_CTX_set_rsa_oaep_md`]: https://www.openssl.org/docs/manmaster/man3/EVP_PKEY_CTX_set_rsa_oaep_md.html
+- #[cfg(any(ossl102, libressl310))]
++ #[cfg(any(ossl102, libressl310, boringssl))]
+ pub fn set_rsa_oaep_md(&mut self, md: MessageDigest) -> Result<(), ErrorStack> {
+ unsafe {
+ cvt(ffi::EVP_PKEY_CTX_set_rsa_oaep_md(
+@@ -352,7 +352,7 @@ impl<'a> Decrypter<'a> {
+ /// This corresponds to [`EVP_PKEY_CTX_set_rsa_oaep_md`].
+ ///
+ /// [`EVP_PKEY_CTX_set_rsa_oaep_md`]: https://www.openssl.org/docs/manmaster/man3/EVP_PKEY_CTX_set_rsa_oaep_md.html
+- #[cfg(any(ossl102, libressl310))]
++ #[cfg(any(ossl102, libressl310, boringssl))]
+ pub fn set_rsa_oaep_md(&mut self, md: MessageDigest) -> Result<(), ErrorStack> {
+ unsafe {
+ cvt(ffi::EVP_PKEY_CTX_set_rsa_oaep_md(
+diff --git a/openssl/src/hash.rs b/openssl/src/hash.rs
+index 8e27505a..7f6fa89e 100644
+--- a/openssl/src/hash.rs
++++ b/openssl/src/hash.rs
+@@ -43,7 +43,7 @@ use crate::nid::Nid;
+ use crate::{cvt, cvt_p};
+
+ cfg_if! {
+- if #[cfg(ossl110)] {
++ if #[cfg(any(ossl110, boringssl))] {
+ use ffi::{EVP_MD_CTX_free, EVP_MD_CTX_new};
+ } else {
+ use ffi::{EVP_MD_CTX_create as EVP_MD_CTX_new, EVP_MD_CTX_destroy as EVP_MD_CTX_free};
+diff --git a/openssl/src/hkdf.rs b/openssl/src/hkdf.rs
+new file mode 100644
+index 00000000..cc7e5b3a
+--- /dev/null
++++ b/openssl/src/hkdf.rs
+@@ -0,0 +1,89 @@
++use crate::cvt;
++use crate::error::ErrorStack;
++use crate::md::MdRef;
++use foreign_types::ForeignTypeRef;
++use openssl_macros::corresponds;
++
++/// Computes HKDF (as specified by RFC 5869).
++///
++/// HKDF is an Extract-and-Expand algorithm. It does not do any key stretching,
++/// and as such, is not suited to be used alone to generate a key from a
++/// password.
++#[corresponds(HKDF)]
++#[inline]
++pub fn hkdf(
++ out_key: &mut [u8],
++ md: &MdRef,
++ secret: &[u8],
++ salt: &[u8],
++ info: &[u8],
++) -> Result<(), ErrorStack> {
++ unsafe {
++ cvt(ffi::HKDF(
++ out_key.as_mut_ptr(),
++ out_key.len(),
++ md.as_ptr(),
++ secret.as_ptr(),
++ secret.len(),
++ salt.as_ptr(),
++ salt.len(),
++ info.as_ptr(),
++ info.len(),
++ ))?;
++ }
++
++ Ok(())
++}
++
++/// Computes a HKDF PRK (as specified by RFC 5869).
++///
++/// WARNING: This function orders the inputs differently from RFC 5869
++/// specification. Double-check which parameter is the secret/IKM and which is
++/// the salt when using.
++#[corresponds(HKDF_extract)]
++#[inline]
++pub fn hkdf_extract<'a>(
++ out_key: &'a mut [u8],
++ md: &MdRef,
++ secret: &[u8],
++ salt: &[u8],
++) -> Result<&'a [u8], ErrorStack> {
++ let mut out_len = out_key.len();
++ unsafe {
++ cvt(ffi::HKDF_extract(
++ out_key.as_mut_ptr(),
++ &mut out_len,
++ md.as_ptr(),
++ secret.as_ptr(),
++ secret.len(),
++ salt.as_ptr(),
++ salt.len(),
++ ))?;
++ }
++
++ Ok(&out_key[..out_len])
++}
++
++/// Computes a HKDF OKM (as specified by RFC 5869).
++#[corresponds(HKDF_expand)]
++#[inline]
++pub fn hkdf_expand(
++ out_key: &mut [u8],
++ md: &MdRef,
++ prk: &[u8],
++ info: &[u8],
++) -> Result<(), ErrorStack> {
++ unsafe {
++ cvt(ffi::HKDF_expand(
++ out_key.as_mut_ptr(),
++ out_key.len(),
++ md.as_ptr(),
++ prk.as_ptr(),
++ prk.len(),
++ info.as_ptr(),
++ info.len(),
++ ))?;
++ }
++
++ Ok(())
++}
+diff --git a/openssl/src/hmac.rs b/openssl/src/hmac.rs
+new file mode 100644
+index 00000000..465781e2
+--- /dev/null
++++ b/openssl/src/hmac.rs
+@@ -0,0 +1,217 @@
++use crate::error::ErrorStack;
++use crate::md::MdRef;
++use crate::{cvt, cvt_p};
++use ffi::HMAC_CTX;
++use foreign_types::ForeignTypeRef;
++use libc::{c_uint, c_void};
++use openssl_macros::corresponds;
++use std::convert::TryFrom;
++use std::ptr;
++
++/// Computes the HMAC as a one-shot operation.
++///
++/// Calculates the HMAC of data, using the given |key|
++/// and hash function |md|, and returns the result re-using the space from
++/// buffer |out|. On entry, |out| must contain at least |EVP_MD_size| bytes
++/// of space. The actual length of the result is used to resize the returned
++/// slice. An output size of |EVP_MAX_MD_SIZE| will always be large enough.
++/// It returns a resized |out| or ErrorStack on error.
++#[corresponds(HMAC)]
++#[inline]
++pub fn hmac<'a>(
++ md: &MdRef,
++ key: &[u8],
++ data: &[u8],
++ out: &'a mut [u8],
++) -> Result<&'a [u8], ErrorStack> {
++ assert!(out.len() >= md.size());
++ let mut out_len = c_uint::try_from(out.len()).unwrap();
++ unsafe {
++ cvt_p(ffi::HMAC(
++ md.as_ptr(),
++ key.as_ptr() as *const c_void,
++ key.len(),
++ data.as_ptr(),
++ data.len(),
++ out.as_mut_ptr(),
++ &mut out_len,
++ ))?;
++ }
++ Ok(&out[..out_len as usize])
++}
++
++/// A context object used to perform HMAC operations.
++///
++/// HMAC is a MAC (message authentication code), i.e. a keyed hash function used for message
++/// authentication, which is based on a hash function.
++///
++/// Note: Only available in boringssl. For openssl, use `PKey::hmac` instead.
++#[cfg(boringssl)]
++pub struct HmacCtx {
++ ctx: *mut HMAC_CTX,
++ output_size: usize,
++}
++
++#[cfg(boringssl)]
++impl HmacCtx {
++ /// Creates a new [HmacCtx] to use the hash function `md` and key `key`.
++ #[corresponds(HMAC_Init_ex)]
++ pub fn new(key: &[u8], md: &MdRef) -> Result<Self, ErrorStack> {
++ unsafe {
++ // Safety: If an error occurred, the resulting null from HMAC_CTX_new is converted into
++ // ErrorStack in the returned result by `cvt_p`.
++ let ctx = cvt_p(ffi::HMAC_CTX_new())?;
++ // Safety:
++ // - HMAC_Init_ex must be called with a context previously created with HMAC_CTX_new,
++ // which is the line above.
++ // - HMAC_Init_ex may return an error if key is null but the md is different from
++ // before. This is avoided here since key is guaranteed to be non-null.
++ cvt(ffi::HMAC_Init_ex(
++ ctx,
++ key.as_ptr() as *const c_void,
++ key.len(),
++ md.as_ptr(),
++ ptr::null_mut(),
++ ))?;
++ Ok(Self {
++ ctx,
++ output_size: md.size(),
++ })
++ }
++ }
++
++ /// `update` can be called repeatedly with chunks of the message `data` to be authenticated.
++ #[corresponds(HMAC_Update)]
++ pub fn update(&mut self, data: &[u8]) -> Result<(), ErrorStack> {
++ unsafe {
++ // Safety: HMAC_Update returns 0 on error, and that is converted into ErrorStack in the
++ // returned result by `cvt`.
++ cvt(ffi::HMAC_Update(self.ctx, data.as_ptr(), data.len())).map(|_| ())
++ }
++ }
++
++ /// Finishes the HMAC process, and places the message authentication code in `output`.
++ /// The number of bytes written to `output` is returned.
++ ///
++ /// # Panics
++ ///
++ /// Panics if the `output` is smaller than the required size. The output size is indicated by
++ /// `md.size()` for the `Md` instance passed in [new]. An output size of |EVP_MAX_MD_SIZE| will
++ /// always be large enough.
++ #[corresponds(HMAC_Final)]
++ pub fn finalize(&mut self, output: &mut [u8]) -> Result<usize, ErrorStack> {
++ assert!(output.len() >= self.output_size);
++ unsafe {
++ // Safety: The length assertion above makes sure that `HMAC_Final` will not write longer
++ // than the length of `output`.
++ let mut size: c_uint = 0;
++ cvt(ffi::HMAC_Final(
++ self.ctx,
++ output.as_mut_ptr(),
++ &mut size as *mut c_uint,
++ ))
++ .map(|_| size as usize)
++ }
++ }
++}
++
++impl Drop for HmacCtx {
++ #[corresponds(HMAC_CTX_free)]
++ fn drop(&mut self) {
++ unsafe {
++ ffi::HMAC_CTX_free(self.ctx);
++ }
++ }
++}
++
++#[cfg(test)]
++mod tests {
++ use super::*;
++ use crate::md::Md;
++
++ const SHA_256_DIGEST_SIZE: usize = 32;
++
++ #[test]
++ fn hmac_sha256_test() {
++ let expected_hmac = [
++ 0xb0, 0x34, 0x4c, 0x61, 0xd8, 0xdb, 0x38, 0x53, 0x5c, 0xa8, 0xaf, 0xce, 0xaf, 0xb,
++ 0xf1, 0x2b, 0x88, 0x1d, 0xc2, 0x0, 0xc9, 0x83, 0x3d, 0xa7, 0x26, 0xe9, 0x37, 0x6c,
++ 0x2e, 0x32, 0xcf, 0xf7,
++ ];
++ let mut out: [u8; SHA_256_DIGEST_SIZE] = [0; SHA_256_DIGEST_SIZE];
++ let key: [u8; 20] = [0x0b; 20];
++ let data = b"Hi There";
++ let hmac_result =
++ hmac(Md::sha256(), &key, data, &mut out).expect("Couldn't calculate sha256 hmac");
++ assert_eq!(&hmac_result, &expected_hmac);
++ }
++
++ #[test]
++ #[should_panic]
++ fn hmac_sha256_output_too_short() {
++ let mut out = vec![0_u8; 1];
++ let key: [u8; 20] = [0x0b; 20];
++ let data = b"Hi There";
++ hmac(Md::sha256(), &key, data, &mut out).expect("Couldn't calculate sha256 hmac");
++ }
++
++ #[test]
++ fn hmac_sha256_test_big_buffer() {
++ let expected_hmac = [
++ 0xb0, 0x34, 0x4c, 0x61, 0xd8, 0xdb, 0x38, 0x53, 0x5c, 0xa8, 0xaf, 0xce, 0xaf, 0xb,
++ 0xf1, 0x2b, 0x88, 0x1d, 0xc2, 0x0, 0xc9, 0x83, 0x3d, 0xa7, 0x26, 0xe9, 0x37, 0x6c,
++ 0x2e, 0x32, 0xcf, 0xf7,
++ ];
++ let mut out: [u8; 100] = [0; 100];
++ let key: [u8; 20] = [0x0b; 20];
++ let data = b"Hi There";
++ let hmac_result =
++ hmac(Md::sha256(), &key, data, &mut out).expect("Couldn't calculate sha256 hmac");
++ assert_eq!(hmac_result.len(), SHA_256_DIGEST_SIZE);
++ assert_eq!(&hmac_result, &expected_hmac);
++ }
++
++ #[test]
++ fn hmac_sha256_update_test() {
++ let expected_hmac = [
++ 0xb0, 0x34, 0x4c, 0x61, 0xd8, 0xdb, 0x38, 0x53, 0x5c, 0xa8, 0xaf, 0xce, 0xaf, 0xb,
++ 0xf1, 0x2b, 0x88, 0x1d, 0xc2, 0x0, 0xc9, 0x83, 0x3d, 0xa7, 0x26, 0xe9, 0x37, 0x6c,
++ 0x2e, 0x32, 0xcf, 0xf7,
++ ];
++ let mut out: [u8; SHA_256_DIGEST_SIZE] = [0; SHA_256_DIGEST_SIZE];
++ let key: [u8; 20] = [0x0b; 20];
++ let data = b"Hi There";
++ let mut hmac_ctx = HmacCtx::new(&key, Md::sha256()).unwrap();
++ hmac_ctx.update(data).unwrap();
++ let size = hmac_ctx.finalize(&mut out).unwrap();
++ assert_eq!(&out, &expected_hmac);
++ assert_eq!(size, SHA_256_DIGEST_SIZE);
++ }
++
++ #[test]
++ fn hmac_sha256_update_chunks_test() {
++ let expected_hmac = [
++ 0xb0, 0x34, 0x4c, 0x61, 0xd8, 0xdb, 0x38, 0x53, 0x5c, 0xa8, 0xaf, 0xce, 0xaf, 0xb,
++ 0xf1, 0x2b, 0x88, 0x1d, 0xc2, 0x0, 0xc9, 0x83, 0x3d, 0xa7, 0x26, 0xe9, 0x37, 0x6c,
++ 0x2e, 0x32, 0xcf, 0xf7,
++ ];
++ let mut out: [u8; SHA_256_DIGEST_SIZE] = [0; SHA_256_DIGEST_SIZE];
++ let key: [u8; 20] = [0x0b; 20];
++ let mut hmac_ctx = HmacCtx::new(&key, Md::sha256()).unwrap();
++ hmac_ctx.update(b"Hi").unwrap();
++ hmac_ctx.update(b" There").unwrap();
++ let size = hmac_ctx.finalize(&mut out).unwrap();
++ assert_eq!(&out, &expected_hmac);
++ assert_eq!(size, SHA_256_DIGEST_SIZE);
++ }
++
++ #[test]
++ #[should_panic]
++ fn hmac_sha256_update_output_too_short() {
++ let mut out = vec![0_u8; 1];
++ let key: [u8; 20] = [0x0b; 20];
++ let mut hmac_ctx = HmacCtx::new(&key, Md::sha256()).unwrap();
++ hmac_ctx.update(b"Hi There").unwrap();
++ hmac_ctx.finalize(&mut out).unwrap();
++ }
++}
+diff --git a/openssl/src/lib.rs b/openssl/src/lib.rs
+index 891651ec..e8d07d8a 100644
+--- a/openssl/src/lib.rs
++++ b/openssl/src/lib.rs
+@@ -120,6 +120,9 @@
+ #![doc(html_root_url = "https://docs.rs/openssl/0.10")]
+ #![warn(rust_2018_idioms)]
+
++#[cfg(all(soong, boringssl))]
++extern crate bssl_ffi as ffi;
++
+ #[doc(inline)]
+ pub use ffi::init;
+
+@@ -155,6 +158,10 @@ pub mod ex_data;
+ #[cfg(not(any(libressl, ossl300)))]
+ pub mod fips;
+ pub mod hash;
++#[cfg(boringssl)]
++pub mod hkdf;
++#[cfg(boringssl)]
++pub mod hmac;
+ #[cfg(ossl300)]
+ pub mod lib_ctx;
+ pub mod md;
+@@ -189,6 +196,11 @@ type LenType = libc::size_t;
+ #[cfg(not(boringssl))]
+ type LenType = libc::c_int;
+
++#[cfg(boringssl)]
++type SignedLenType = libc::ssize_t;
++#[cfg(not(boringssl))]
++type SignedLenType = libc::c_int;
++
+ #[inline]
+ fn cvt_p<T>(r: *mut T) -> Result<*mut T, ErrorStack> {
+ if r.is_null() {
+diff --git a/openssl/src/md_ctx.rs b/openssl/src/md_ctx.rs
+index c4d3f06b..156f3c2f 100644
+--- a/openssl/src/md_ctx.rs
++++ b/openssl/src/md_ctx.rs
+@@ -93,7 +93,7 @@ use std::convert::TryFrom;
+ use std::ptr;
+
+ cfg_if! {
+- if #[cfg(ossl110)] {
++ if #[cfg(any(ossl110, boringssl))] {
+ use ffi::{EVP_MD_CTX_free, EVP_MD_CTX_new};
+ } else {
+ use ffi::{EVP_MD_CTX_create as EVP_MD_CTX_new, EVP_MD_CTX_destroy as EVP_MD_CTX_free};
+diff --git a/openssl/src/pkey.rs b/openssl/src/pkey.rs
+index 2039e7e9..21ba7118 100644
+--- a/openssl/src/pkey.rs
++++ b/openssl/src/pkey.rs
+@@ -47,7 +47,7 @@ use crate::dh::Dh;
+ use crate::dsa::Dsa;
+ use crate::ec::EcKey;
+ use crate::error::ErrorStack;
+-#[cfg(ossl110)]
++#[cfg(any(boringssl, ossl110))]
+ use crate::pkey_ctx::PkeyCtx;
+ use crate::rsa::Rsa;
+ use crate::symm::Cipher;
+@@ -86,14 +86,14 @@ impl Id {
+ pub const DH: Id = Id(ffi::EVP_PKEY_DH);
+ pub const EC: Id = Id(ffi::EVP_PKEY_EC);
+
+- #[cfg(ossl110)]
++ #[cfg(any(boringssl, ossl110))]
+ pub const HKDF: Id = Id(ffi::EVP_PKEY_HKDF);
+
+- #[cfg(ossl111)]
++ #[cfg(any(boringssl, ossl111))]
+ pub const ED25519: Id = Id(ffi::EVP_PKEY_ED25519);
+ #[cfg(ossl111)]
+ pub const ED448: Id = Id(ffi::EVP_PKEY_ED448);
+- #[cfg(ossl111)]
++ #[cfg(any(boringssl, ossl111))]
+ pub const X25519: Id = Id(ffi::EVP_PKEY_X25519);
+ #[cfg(ossl111)]
+ pub const X448: Id = Id(ffi::EVP_PKEY_X448);
+@@ -252,7 +252,7 @@ where
+ /// This function only works for algorithms that support raw public keys.
+ /// Currently this is: [`Id::X25519`], [`Id::ED25519`], [`Id::X448`] or [`Id::ED448`].
+ #[corresponds(EVP_PKEY_get_raw_public_key)]
+- #[cfg(ossl111)]
++ #[cfg(any(boringssl, ossl111))]
+ pub fn raw_public_key(&self) -> Result<Vec<u8>, ErrorStack> {
+ unsafe {
+ let mut len = 0;
+@@ -303,7 +303,7 @@ where
+ /// This function only works for algorithms that support raw private keys.
+ /// Currently this is: [`Id::HMAC`], [`Id::X25519`], [`Id::ED25519`], [`Id::X448`] or [`Id::ED448`].
+ #[corresponds(EVP_PKEY_get_raw_private_key)]
+- #[cfg(ossl111)]
++ #[cfg(any(boringssl, ossl111))]
+ pub fn raw_private_key(&self) -> Result<Vec<u8>, ErrorStack> {
+ unsafe {
+ let mut len = 0;
+@@ -484,7 +484,7 @@ impl PKey<Private> {
+ ctx.keygen()
+ }
+
+- #[cfg(ossl111)]
++ #[cfg(any(boringssl, ossl111))]
+ fn generate_eddsa(id: Id) -> Result<PKey<Private>, ErrorStack> {
+ let mut ctx = PkeyCtx::new_id(id)?;
+ ctx.keygen_init()?;
+@@ -514,7 +514,7 @@ impl PKey<Private> {
+ /// assert_eq!(secret.len(), 32);
+ /// # Ok(()) }
+ /// ```
+- #[cfg(ossl111)]
++ #[cfg(any(boringssl, ossl111))]
+ pub fn generate_x25519() -> Result<PKey<Private>, ErrorStack> {
+ PKey::generate_eddsa(Id::X25519)
+ }
+@@ -568,7 +568,7 @@ impl PKey<Private> {
+ /// assert_eq!(signature.len(), 64);
+ /// # Ok(()) }
+ /// ```
+- #[cfg(ossl111)]
++ #[cfg(any(boringssl, ossl111))]
+ pub fn generate_ed25519() -> Result<PKey<Private>, ErrorStack> {
+ PKey::generate_eddsa(Id::ED25519)
+ }
+@@ -718,7 +718,7 @@ impl PKey<Private> {
+ ///
+ /// Algorithm types that support raw private keys are HMAC, X25519, ED25519, X448 or ED448
+ #[corresponds(EVP_PKEY_new_raw_private_key)]
+- #[cfg(ossl111)]
++ #[cfg(any(boringssl, ossl111))]
+ pub fn private_key_from_raw_bytes(
+ bytes: &[u8],
+ key_type: Id,
+@@ -759,7 +759,7 @@ impl PKey<Public> {
+ ///
+ /// Algorithm types that support raw public keys are X25519, ED25519, X448 or ED448
+ #[corresponds(EVP_PKEY_new_raw_public_key)]
+- #[cfg(ossl111)]
++ #[cfg(any(boringssl, ossl111))]
+ pub fn public_key_from_raw_bytes(
+ bytes: &[u8],
+ key_type: Id,
+diff --git a/openssl/src/pkey_ctx.rs b/openssl/src/pkey_ctx.rs
+index f79372fb..3d4203fa 100644
+--- a/openssl/src/pkey_ctx.rs
++++ b/openssl/src/pkey_ctx.rs
+@@ -470,7 +470,7 @@ impl<T> PkeyCtxRef<T> {
+ ///
+ /// Requires OpenSSL 1.1.0 or newer.
+ #[corresponds(EVP_PKEY_CTX_set_hkdf_md)]
+- #[cfg(ossl110)]
++ #[cfg(any(ossl110, boringssl))]
+ #[inline]
+ pub fn set_hkdf_md(&mut self, digest: &MdRef) -> Result<(), ErrorStack> {
+ unsafe {
+@@ -503,10 +503,13 @@ impl<T> PkeyCtxRef<T> {
+ ///
+ /// Requires OpenSSL 1.1.0 or newer.
+ #[corresponds(EVP_PKEY_CTX_set1_hkdf_key)]
+- #[cfg(ossl110)]
++ #[cfg(any(ossl110, boringssl))]
+ #[inline]
+ pub fn set_hkdf_key(&mut self, key: &[u8]) -> Result<(), ErrorStack> {
++ #[cfg(not(boringssl))]
+ let len = c_int::try_from(key.len()).unwrap();
++ #[cfg(boringssl)]
++ let len = key.len();
+
+ unsafe {
+ cvt(ffi::EVP_PKEY_CTX_set1_hkdf_key(
+@@ -523,10 +526,13 @@ impl<T> PkeyCtxRef<T> {
+ ///
+ /// Requires OpenSSL 1.1.0 or newer.
+ #[corresponds(EVP_PKEY_CTX_set1_hkdf_salt)]
+- #[cfg(ossl110)]
++ #[cfg(any(ossl110, boringssl))]
+ #[inline]
+ pub fn set_hkdf_salt(&mut self, salt: &[u8]) -> Result<(), ErrorStack> {
++ #[cfg(not(boringssl))]
+ let len = c_int::try_from(salt.len()).unwrap();
++ #[cfg(boringssl)]
++ let len = salt.len();
+
+ unsafe {
+ cvt(ffi::EVP_PKEY_CTX_set1_hkdf_salt(
+@@ -543,10 +549,13 @@ impl<T> PkeyCtxRef<T> {
+ ///
+ /// Requires OpenSSL 1.1.0 or newer.
+ #[corresponds(EVP_PKEY_CTX_add1_hkdf_info)]
+- #[cfg(ossl110)]
++ #[cfg(any(ossl110, boringssl))]
+ #[inline]
+ pub fn add_hkdf_info(&mut self, info: &[u8]) -> Result<(), ErrorStack> {
++ #[cfg(not(boringssl))]
+ let len = c_int::try_from(info.len()).unwrap();
++ #[cfg(boringssl)]
++ let len = info.len();
+
+ unsafe {
+ cvt(ffi::EVP_PKEY_CTX_add1_hkdf_info(
+@@ -604,7 +613,7 @@ mod test {
+ #[cfg(not(boringssl))]
+ use crate::cipher::Cipher;
+ use crate::ec::{EcGroup, EcKey};
+- #[cfg(any(ossl102, libressl310))]
++ #[cfg(any(ossl102, libressl310, boringssl))]
+ use crate::md::Md;
+ use crate::nid::Nid;
+ use crate::pkey::PKey;
+@@ -689,7 +698,7 @@ mod test {
+ }
+
+ #[test]
+- #[cfg(ossl110)]
++ #[cfg(any(ossl110, boringssl))]
+ fn hkdf() {
+ let mut ctx = PkeyCtx::new_id(Id::HKDF).unwrap();
+ ctx.derive_init().unwrap();
+diff --git a/openssl/src/rsa.rs b/openssl/src/rsa.rs
+index 68cf64b0..f155b12d 100644
+--- a/openssl/src/rsa.rs
++++ b/openssl/src/rsa.rs
+@@ -581,7 +581,7 @@ impl<T> fmt::Debug for Rsa<T> {
+ }
+
+ cfg_if! {
+- if #[cfg(any(ossl110, libressl273))] {
++ if #[cfg(any(ossl110, libressl273, boringssl))] {
+ use ffi::{
+ RSA_get0_key, RSA_get0_factors, RSA_get0_crt_params, RSA_set0_key, RSA_set0_factors,
+ RSA_set0_crt_params,
+diff --git a/openssl/src/sign.rs b/openssl/src/sign.rs
+index b675825e..e5e80608 100644
+--- a/openssl/src/sign.rs
++++ b/openssl/src/sign.rs
+@@ -290,7 +290,7 @@ impl<'a> Signer<'a> {
+ self.len_intern()
+ }
+
+- #[cfg(not(ossl111))]
++ #[cfg(not(any(boringssl, ossl111)))]
+ fn len_intern(&self) -> Result<usize, ErrorStack> {
+ unsafe {
+ let mut len = 0;
+@@ -303,7 +303,7 @@ impl<'a> Signer<'a> {
+ }
+ }
+
+- #[cfg(ossl111)]
++ #[cfg(any(boringssl, ossl111))]
+ fn len_intern(&self) -> Result<usize, ErrorStack> {
+ unsafe {
+ let mut len = 0;
+@@ -360,7 +360,7 @@ impl<'a> Signer<'a> {
+ /// OpenSSL documentation at [`EVP_DigestSign`].
+ ///
+ /// [`EVP_DigestSign`]: https://www.openssl.org/docs/man1.1.1/man3/EVP_DigestSign.html
+- #[cfg(ossl111)]
++ #[cfg(any(boringssl, ossl111))]
+ pub fn sign_oneshot(
+ &mut self,
+ sig_buf: &mut [u8],
+@@ -382,7 +382,7 @@ impl<'a> Signer<'a> {
+ /// Returns the signature.
+ ///
+ /// This is a simple convenience wrapper over `len` and `sign_oneshot`.
+- #[cfg(ossl111)]
++ #[cfg(any(boringssl, ossl111))]
+ pub fn sign_oneshot_to_vec(&mut self, data_buf: &[u8]) -> Result<Vec<u8>, ErrorStack> {
+ let mut sig_buf = vec![0; self.len()?];
+ let len = self.sign_oneshot(&mut sig_buf, data_buf)?;
+@@ -596,7 +596,7 @@ impl<'a> Verifier<'a> {
+ /// OpenSSL documentation at [`EVP_DigestVerify`].
+ ///
+ /// [`EVP_DigestVerify`]: https://www.openssl.org/docs/man1.1.1/man3/EVP_DigestVerify.html
+- #[cfg(ossl111)]
++ #[cfg(any(boringssl, ossl111))]
+ pub fn verify_oneshot(&mut self, signature: &[u8], buf: &[u8]) -> Result<bool, ErrorStack> {
+ unsafe {
+ let r = ffi::EVP_DigestVerify(
+diff --git a/openssl/src/symm.rs b/openssl/src/symm.rs
+index c75bbc0c..beff5fc2 100644
+--- a/openssl/src/symm.rs
++++ b/openssl/src/symm.rs
+@@ -119,6 +119,7 @@ impl Cipher {
+ unsafe { Cipher(ffi::EVP_aes_128_cfb1()) }
+ }
+
++ #[cfg(not(boringssl))]
+ pub fn aes_128_cfb128() -> Cipher {
+ unsafe { Cipher(ffi::EVP_aes_128_cfb128()) }
+ }
+@@ -164,6 +165,7 @@ impl Cipher {
+ unsafe { Cipher(ffi::EVP_aes_192_cfb1()) }
+ }
+
++ #[cfg(not(boringssl))]
+ pub fn aes_192_cfb128() -> Cipher {
+ unsafe { Cipher(ffi::EVP_aes_192_cfb128()) }
+ }
+@@ -214,6 +216,7 @@ impl Cipher {
+ unsafe { Cipher(ffi::EVP_aes_256_cfb1()) }
+ }
+
++ #[cfg(not(boringssl))]
+ pub fn aes_256_cfb128() -> Cipher {
+ unsafe { Cipher(ffi::EVP_aes_256_cfb128()) }
+ }
+@@ -242,12 +245,12 @@ impl Cipher {
+ unsafe { Cipher(ffi::EVP_aes_256_ocb()) }
+ }
+
+- #[cfg(not(osslconf = "OPENSSL_NO_BF"))]
++ #[cfg(not(any(boringssl, osslconf = "OPENSSL_NO_BF")))]
+ pub fn bf_cbc() -> Cipher {
+ unsafe { Cipher(ffi::EVP_bf_cbc()) }
+ }
+
+- #[cfg(not(osslconf = "OPENSSL_NO_BF"))]
++ #[cfg(not(any(boringssl, osslconf = "OPENSSL_NO_BF")))]
+ pub fn bf_ecb() -> Cipher {
+ unsafe { Cipher(ffi::EVP_bf_ecb()) }
+ }
+diff --git a/openssl/src/x509/mod.rs b/openssl/src/x509/mod.rs
+index edd54aa8..a03a8aa6 100644
+--- a/openssl/src/x509/mod.rs
++++ b/openssl/src/x509/mod.rs
+@@ -353,6 +353,19 @@ impl X509Builder {
+ unsafe { cvt(ffi::X509_sign(self.0.as_ptr(), key.as_ptr(), hash.as_ptr())).map(|_| ()) }
+ }
+
++ /// Signs the certificate with a private key but without a digest.
++ ///
++ /// This is the only way to sign with Ed25519 keys as BoringSSL doesn't support the null
++ /// message digest.
++ #[cfg(boringssl)]
++ #[corresponds(X509_sign)]
++ pub fn sign_without_digest<T>(&mut self, key: &PKeyRef<T>) -> Result<(), ErrorStack>
++ where
++ T: HasPrivate,
++ {
++ unsafe { cvt(ffi::X509_sign(self.0.as_ptr(), key.as_ptr(), ptr::null())).map(|_| ()) }
++ }
++
+ /// Consumes the builder, returning the certificate.
+ pub fn build(self) -> X509 {
+ self.0
+@@ -880,13 +893,13 @@ impl X509NameBuilder {
+ pub fn append_entry_by_text(&mut self, field: &str, value: &str) -> Result<(), ErrorStack> {
+ unsafe {
+ let field = CString::new(field).unwrap();
+- assert!(value.len() <= c_int::max_value() as usize);
++ assert!(value.len() <= isize::max_value() as usize);
+ cvt(ffi::X509_NAME_add_entry_by_txt(
+ self.0.as_ptr(),
+ field.as_ptr() as *mut _,
+ ffi::MBSTRING_UTF8,
+ value.as_ptr(),
+- value.len() as c_int,
++ value.len() as isize,
+ -1,
+ 0,
+ ))
+@@ -907,13 +920,13 @@ impl X509NameBuilder {
+ ) -> Result<(), ErrorStack> {
+ unsafe {
+ let field = CString::new(field).unwrap();
+- assert!(value.len() <= c_int::max_value() as usize);
++ assert!(value.len() <= isize::max_value() as usize);
+ cvt(ffi::X509_NAME_add_entry_by_txt(
+ self.0.as_ptr(),
+ field.as_ptr() as *mut _,
+ ty.as_raw(),
+ value.as_ptr(),
+- value.len() as c_int,
++ value.len() as isize,
+ -1,
+ 0,
+ ))
+@@ -928,13 +941,13 @@ impl X509NameBuilder {
+ /// [`X509_NAME_add_entry_by_NID`]: https://www.openssl.org/docs/man1.1.0/crypto/X509_NAME_add_entry_by_NID.html
+ pub fn append_entry_by_nid(&mut self, field: Nid, value: &str) -> Result<(), ErrorStack> {
+ unsafe {
+- assert!(value.len() <= c_int::max_value() as usize);
++ assert!(value.len() <= isize::max_value() as usize);
+ cvt(ffi::X509_NAME_add_entry_by_NID(
+ self.0.as_ptr(),
+ field.as_raw(),
+ ffi::MBSTRING_UTF8,
+ value.as_ptr() as *mut _,
+- value.len() as c_int,
++ value.len() as isize,
+ -1,
+ 0,
+ ))
+@@ -954,13 +967,13 @@ impl X509NameBuilder {
+ ty: Asn1Type,
+ ) -> Result<(), ErrorStack> {
+ unsafe {
+- assert!(value.len() <= c_int::max_value() as usize);
++ assert!(value.len() <= isize::max_value() as usize);
+ cvt(ffi::X509_NAME_add_entry_by_NID(
+ self.0.as_ptr(),
+ field.as_raw(),
+ ty.as_raw(),
+ value.as_ptr() as *mut _,
+- value.len() as c_int,
++ value.len() as isize,
+ -1,
+ 0,
+ ))
+@@ -1260,6 +1273,29 @@ impl X509ReqBuilder {
+ }
+ }
+
++ /// Sign the request using a private key without a digest.
++ ///
++ /// This is the only way to sign with Ed25519 keys as BoringSSL doesn't support the null
++ /// message digest.
++ ///
++ /// This corresponds to [`X509_REQ_sign`].
++ ///
++ /// [`X509_REQ_sign`]: https://www.openssl.org/docs/man1.1.0/crypto/X509_REQ_sign.html
++ #[cfg(boringssl)]
++ pub fn sign_without_digest<T>(&mut self, key: &PKeyRef<T>) -> Result<(), ErrorStack>
++ where
++ T: HasPrivate,
++ {
++ unsafe {
++ cvt(ffi::X509_REQ_sign(
++ self.0.as_ptr(),
++ key.as_ptr(),
++ ptr::null(),
++ ))
++ .map(|_| ())
++ }
++ }
++
+ /// Returns the `X509Req`.
+ pub fn build(self) -> X509Req {
+ self.0
+--
+2.42.0.rc2.253.gd59a3bf2b4-goog
+
diff --git a/nearby/scripts/openssl-patches/0002-fix-boringssl-dsa-build-errors.patch b/nearby/scripts/openssl-patches/0002-fix-boringssl-dsa-build-errors.patch
deleted file mode 100644
index 030e46e..0000000
--- a/nearby/scripts/openssl-patches/0002-fix-boringssl-dsa-build-errors.patch
+++ /dev/null
@@ -1,25 +0,0 @@
-From 097eaa7166ad1f6298c41bc66e094a15a9a4e73e Mon Sep 17 00:00:00 2001
-From: Nabil Wadih <nwadih@google.com>
-Date: Tue, 6 Jun 2023 15:57:04 -0700
-Subject: [PATCH 2/2] fix boringssl dsa build errors
-
----
- openssl/src/dsa.rs | 2 +-
- 1 file changed, 1 insertion(+), 1 deletion(-)
-
-diff --git a/openssl/src/dsa.rs b/openssl/src/dsa.rs
-index c550f654..ffebdf8a 100644
---- a/openssl/src/dsa.rs
-+++ b/openssl/src/dsa.rs
-@@ -283,7 +283,7 @@ impl<T> fmt::Debug for Dsa<T> {
- }
-
- cfg_if! {
-- if #[cfg(any(ossl110, libressl273))] {
-+ if #[cfg(any(ossl110, libressl273, boringssl))] {
- use ffi::{DSA_get0_key, DSA_get0_pqg, DSA_set0_key, DSA_set0_pqg};
- } else {
- #[allow(bad_style)]
---
-2.41.0.162.gfafddb0af9-goog
-
diff --git a/nearby/src/crypto_ffi.rs b/nearby/src/crypto_ffi.rs
index 3118b8a..20d1d5d 100644
--- a/nearby/src/crypto_ffi.rs
+++ b/nearby/src/crypto_ffi.rs
@@ -12,9 +12,8 @@
// See the License for the specific language governing permissions and
// limitations under the License.
-use crate::support::{run_cmd_shell, run_cmd_shell_with_color, YellowStderr};
-use crate::BuildBoringSslOptions;
use anyhow::anyhow;
+use cmd_runner::{run_cmd_shell, run_cmd_shell_with_color, YellowStderr};
use owo_colors::OwoColorize as _;
use semver::{Version, VersionReq};
use std::{
@@ -22,7 +21,15 @@ use std::{
path::{Path, PathBuf},
};
-pub fn build_boringssl(root: &Path, options: &BuildBoringSslOptions) -> anyhow::Result<()> {
+use crate::CargoOptions;
+
+pub fn boringssl_check_everything(root: &Path, cargo_options: &CargoOptions) -> anyhow::Result<()> {
+ check_boringssl(root, cargo_options)?;
+ check_openssl(root, cargo_options)?;
+ Ok(())
+}
+
+pub fn build_boringssl(root: &Path) -> anyhow::Result<()> {
let bindgen_version_req = VersionReq::parse(">=0.61.0")?;
let bindgen_version = get_bindgen_version()?;
@@ -30,23 +37,16 @@ pub fn build_boringssl(root: &Path, options: &BuildBoringSslOptions) -> anyhow::
return Err(anyhow!("Bindgen does not match expected version: {bindgen_version_req}"));
}
- let mut vendor_dir =
- root.parent().ok_or_else(|| anyhow!("project root dir no parent dir"))?.to_path_buf();
- vendor_dir.push("boringssl-build");
+ let vendor_dir = root
+ .parent()
+ .ok_or_else(|| anyhow!("project root dir no parent dir"))?
+ .join("boringssl-build");
fs::create_dir_all(&vendor_dir)?;
- let mut build_dir = clone_repo_if_needed(
- &vendor_dir,
- "boringssl",
- "https://boringssl.googlesource.com/boringssl",
- )?;
-
- run_cmd_shell_with_color::<YellowStderr>(
- &build_dir,
- format!("git checkout {}", &options.commit_hash),
- )?;
-
- build_dir.push("build");
+ let build_dir = root
+ .parent()
+ .ok_or_else(|| anyhow!("project root dir no parent dir"))?
+ .join("third_party/boringssl/build");
fs::create_dir_all(&build_dir)?;
let target = run_cmd_shell_with_color::<YellowStderr>(&vendor_dir, "rustc -vV")?
@@ -70,19 +70,23 @@ pub fn build_boringssl(root: &Path, options: &BuildBoringSslOptions) -> anyhow::
Ok(())
}
-pub fn check_boringssl(root: &Path, options: &BuildBoringSslOptions) -> anyhow::Result<()> {
+pub fn check_boringssl(root: &Path, cargo_options: &CargoOptions) -> anyhow::Result<()> {
log::info!("Checking boringssl");
- build_boringssl(root, options)?;
+ build_boringssl(root)?;
- let mut bssl_dir = root.to_path_buf();
- bssl_dir.push("crypto/crypto_provider_boringssl");
+ let bssl_dir = root.join("crypto/crypto_provider_boringssl");
- run_cmd_shell(&bssl_dir, "cargo check")?;
+ let locked_arg = if cargo_options.locked { "--locked" } else { "" };
+
+ run_cmd_shell(&bssl_dir, format!("cargo check {locked_arg}"))?;
run_cmd_shell(&bssl_dir, "cargo fmt --check")?;
run_cmd_shell(&bssl_dir, "cargo clippy --all-targets")?;
- run_cmd_shell(&bssl_dir, "cargo test -- --color=always")?;
+ run_cmd_shell(&bssl_dir, format!("cargo test {locked_arg} -- --color=always"))?;
run_cmd_shell(&bssl_dir, "cargo doc --no-deps")?;
+
+ run_cmd_shell(root, "cargo test -p ukey2_connections -p ukey2_rs --no-default-features --features test_boringssl")?;
+
Ok(())
}
@@ -100,7 +104,7 @@ pub fn prepare_patched_rust_openssl(root: &Path) -> anyhow::Result<()> {
run_cmd_shell_with_color::<YellowStderr>(
&repo_dir,
- "git checkout 11797d9ecb73e94b7f55a49274318abc9dc074d2",
+ "git checkout 7df56869c5e1e32369091ab106750d644d3aa0c4",
)?;
run_cmd_shell_with_color::<YellowStderr>(&repo_dir, "git branch -f BASE_COMMIT")?;
run_cmd_shell_with_color::<YellowStderr>(
@@ -121,16 +125,20 @@ You can now build and test with boringssl using the following command
Ok(())
}
-pub fn check_openssl(root: &Path) -> anyhow::Result<()> {
+pub fn check_openssl(root: &Path, cargo_options: &CargoOptions) -> anyhow::Result<()> {
log::info!("Checking rust openssl");
prepare_patched_rust_openssl(root)?;
+ let locked_arg = if cargo_options.locked { "--locked" } else { "" };
// test the openssl crate with the boringssl feature
run_cmd_shell(
root,
- concat!(
- "cargo --config .cargo/config-boringssl.toml test -p crypto_provider_openssl ",
- "--features=boringssl -- --color=always"
+ format!(
+ concat!(
+ "cargo --config .cargo/config-boringssl.toml test {locked_arg} -p crypto_provider_openssl ",
+ "--features=boringssl -- --color=always"
+ ),
+ locked_arg=locked_arg
),
)?;
diff --git a/nearby/src/ffi.rs b/nearby/src/ffi.rs
index c1b8073..01fe58a 100644
--- a/nearby/src/ffi.rs
+++ b/nearby/src/ffi.rs
@@ -12,37 +12,42 @@
// See the License for the specific language governing permissions and
// limitations under the License.
-use crate::{run_cmd_shell, run_cmd_shell_with_color, YellowStderr};
+use crate::CargoOptions;
+use cmd_runner::{run_cmd_shell, run_cmd_shell_with_color, YellowStderr};
use std::{fs, path};
// wrapper for checking all ffi related things
-pub fn check_everything(root: &path::Path) -> anyhow::Result<()> {
- check_np_ffi(root)?;
- check_ldt_ffi(root)?;
- check_cmake_projects(root)?;
+pub fn check_everything(root: &path::Path, cargo_options: &CargoOptions) -> anyhow::Result<()> {
+ check_np_ffi_rust(root, cargo_options)?;
+ check_ldt_ffi_rust(root)?;
+ check_ldt_cmake(root, cargo_options)?;
+ check_np_ffi_cmake(root, cargo_options)?;
Ok(())
}
-pub fn check_np_ffi(root: &path::Path) -> anyhow::Result<()> {
+pub fn check_np_ffi_rust(root: &path::Path, cargo_options: &CargoOptions) -> anyhow::Result<()> {
log::info!("Checking np_c_ffi cargo build");
- let mut ffi_dir = root.to_path_buf();
- ffi_dir.push("presence/np_c_ffi");
+ let ffi_dir = root.join("presence/np_c_ffi");
+ let locked_arg = if cargo_options.locked { "--locked" } else { "" };
for cargo_cmd in [
"fmt --check",
- // Default build, RustCrypto + no_std
- "check --quiet",
+ // Default build, RustCrypto
+ format!("check {locked_arg} --quiet").as_str(),
+ // Build with BoringSSL for crypto
+ format!("check {locked_arg} --no-default-features --features=boringssl").as_str(),
"clippy",
+ "deny check",
+ "doc --quiet --no-deps",
] {
run_cmd_shell(&ffi_dir, format!("cargo {}", cargo_cmd))?;
}
Ok(())
}
-pub fn check_ldt_ffi(root: &path::Path) -> anyhow::Result<()> {
+pub fn check_ldt_ffi_rust(root: &path::Path) -> anyhow::Result<()> {
log::info!("Checking LFT ffi cargo build");
- let mut ffi_dir = root.to_path_buf();
- ffi_dir.push("presence/ldt_np_adv_ffi");
+ let ffi_dir = root.to_path_buf().join("presence/ldt_np_adv_ffi");
for cargo_cmd in [
"fmt --check",
@@ -52,16 +57,13 @@ pub fn check_ldt_ffi(root: &path::Path) -> anyhow::Result<()> {
"check --quiet --features=std",
// Turn off default features and try to build with std",
"check --quiet --no-default-features --features=std",
- // Turn off RustCrypto and use openssl
- "check --quiet --no-default-features --features=openssl",
// Turn off RustCrypto and use boringssl
- "--config .cargo/config-boringssl.toml check --quiet --no-default-features --features=boringssl",
+ "check --quiet --no-default-features --features=boringssl",
"doc --quiet --no-deps",
"clippy --release",
"clippy --features=std",
"clippy --no-default-features --features=openssl",
"clippy --no-default-features --features=std",
- // TODO also clippy for boringssl?
"deny check",
] {
run_cmd_shell(&ffi_dir, format!("cargo {}", cargo_cmd))?;
@@ -70,53 +72,91 @@ pub fn check_ldt_ffi(root: &path::Path) -> anyhow::Result<()> {
Ok(())
}
-pub fn check_cmake_projects(root: &path::Path) -> anyhow::Result<()> {
- // plain rustcrypto build to prepare a .a for the np_cpp_ffi tests below
- // TODO: make this a target in the cmake build so there isn't an implicit dependency
- let mut ldt_ffi_crate_dir = root.to_path_buf();
- ldt_ffi_crate_dir.push("presence/ldt_np_adv_ffi");
- let mut c_ffi_crate_dir = root.to_path_buf();
- c_ffi_crate_dir.push("presence/np_c_ffi");
- run_cmd_shell(&ldt_ffi_crate_dir, "cargo build --quiet --release")?;
- run_cmd_shell(&c_ffi_crate_dir, "cargo build --quiet --release")?;
-
- log::info!("Checking CMake build and tests (for ffi c/c++ code)");
- let mut build_dir = root.to_path_buf();
- build_dir.push("presence/cmake-build");
+pub fn check_np_ffi_cmake(root: &path::Path, cargo_options: &CargoOptions) -> anyhow::Result<()> {
+ log::info!("Checking CMake build and tests for np ffi c/c++ code");
+ let build_dir = root.to_path_buf().join("presence/cmake-build");
fs::create_dir_all(&build_dir)?;
+ let locked_arg = if cargo_options.locked { "--locked" } else { "" };
+
run_cmd_shell_with_color::<YellowStderr>(
&build_dir,
"cmake -G Ninja -DENABLE_TESTS=true -DCMAKE_BUILD_TYPE=Release ..",
)?;
- run_cmd_shell_with_color::<YellowStderr>(&build_dir, "cmake --build .")?;
- // run the np_cpp_ffi unit tests
- let mut np_cpp_tests_dir = build_dir.clone();
- np_cpp_tests_dir.push("np_cpp_ffi/tests");
- run_cmd_shell_with_color::<YellowStderr>(&np_cpp_tests_dir, "ctest")?;
+ // verify sample and benchmarks build
+ let np_ffi_crate_dir = root.to_path_buf().join("presence/np_c_ffi");
+ run_cmd_shell(&np_ffi_crate_dir, format!("cargo build {locked_arg} --release"))?;
+ run_cmd_shell_with_color::<YellowStderr>(&build_dir, "cmake --build . --target np_cpp_sample")?;
+ run_cmd_shell_with_color::<YellowStderr>(&build_dir, "cmake --build . --target np_ffi_bench")?;
+
+ // Run tests with different crypto backends
+ let tests_dir = build_dir.to_path_buf().join("np_cpp_ffi/tests");
+ for build_config in [
+ // test with default build settings (rustcrypto)
+ format!("build {locked_arg} --quiet --release"),
+ // test with boringssl
+ format!("build {locked_arg} --quiet --no-default-features --features=boringssl"),
+ ] {
+ let _ = run_cmd_shell_with_color::<YellowStderr>(
+ &build_dir,
+ "rm np_cpp_ffi/tests/np_ffi_tests",
+ );
+ run_cmd_shell(&np_ffi_crate_dir, format!("cargo {}", build_config))?;
+ run_cmd_shell_with_color::<YellowStderr>(
+ &build_dir,
+ "cmake --build . --target np_ffi_tests",
+ )?;
+ run_cmd_shell_with_color::<YellowStderr>(&tests_dir, "ctest")?;
+ }
+
+ Ok(())
+}
+
+pub fn check_ldt_cmake(root: &path::Path, cargo_options: &CargoOptions) -> anyhow::Result<()> {
+ log::info!("Checking CMake build and tests for ldt c/c++ code");
+ let build_dir = root.to_path_buf().join("presence/cmake-build");
+ fs::create_dir_all(&build_dir)?;
+
+ let locked_arg = if cargo_options.locked { "--locked" } else { "" };
+
+ run_cmd_shell_with_color::<YellowStderr>(
+ &build_dir,
+ "cmake -G Ninja -DENABLE_TESTS=true -DCMAKE_BUILD_TYPE=Release ..",
+ )?;
+
+ // verify sample and benchmarks build
+ let ldt_ffi_crate_dir = root.to_path_buf().join("presence/ldt_np_adv_ffi");
+ run_cmd_shell(&ldt_ffi_crate_dir, format!("cargo build {locked_arg} --release"))?;
+ run_cmd_shell_with_color::<YellowStderr>(&build_dir, "cmake --build . --target ldt_c_sample")?;
+ run_cmd_shell_with_color::<YellowStderr>(
+ &build_dir,
+ "cmake --build . --target ldt_benchmarks",
+ )?;
// Run the LDT ffi unit tests. These are rebuilt and tested against all of the different
// Cargo build configurations based on the feature flags.
- let mut ldt_tests_dir = build_dir.clone();
- ldt_tests_dir.push("ldt_np_c_sample/tests");
-
+ let ldt_tests_dir = build_dir.to_path_buf().join("ldt_np_c_sample/tests");
for build_config in [
// test with default build settings (rustcrypto, no_std)
- "build --quiet --release",
+ format!("build {locked_arg} --quiet --release"),
// test with std and default features
- "build --quiet --features std --release",
+ format!("build {locked_arg} --quiet --features std --release"),
// test with boringssl crypto feature flag
- "--config .cargo/config-boringssl.toml build --quiet --no-default-features --features boringssl --release",
- // test with openssl feature flag
- "build --quiet --no-default-features --features openssl --release",
+ format!("build {locked_arg} --quiet --no-default-features --features boringssl --release"),
// test without defaults and std feature flag
- "build --quiet --no-default-features --features std --release",
+ format!("build {locked_arg} --quiet --no-default-features --features std --release"),
] {
run_cmd_shell(&ldt_ffi_crate_dir, format!("cargo {}", build_config))?;
// Force detection of updated `ldt_np_adv_ffi` static lib
- run_cmd_shell_with_color::<YellowStderr>(&build_dir, "rm ldt_np_c_sample/tests/ldt_ffi_tests")?;
- run_cmd_shell_with_color::<YellowStderr>(&build_dir, "cmake --build .")?;
+ let _ = run_cmd_shell_with_color::<YellowStderr>(
+ &build_dir,
+ "rm ldt_np_c_sample/tests/ldt_ffi_tests",
+ );
+ run_cmd_shell_with_color::<YellowStderr>(
+ &build_dir,
+ "cmake --build . --target ldt_ffi_tests",
+ )?;
run_cmd_shell_with_color::<YellowStderr>(&ldt_tests_dir, "ctest")?;
}
diff --git a/nearby/src/file_header/license.rs b/nearby/src/file_header/license.rs
deleted file mode 100644
index b463ad0..0000000
--- a/nearby/src/file_header/license.rs
+++ /dev/null
@@ -1,45 +0,0 @@
-// Copyright 2023 Google LLC
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-// http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-//! Support for license-oriented usage of `file_header`.
-use super::*;
-use chrono::Datelike as _;
-
-/// The Apache 2 license for the current year and provided `copyright_holder`.
-pub fn apache_2(copyright_holder: &str) -> Header<impl HeaderChecker> {
- Header::new(
- asl2_checker(),
- format!(
- r#"Copyright {} {}
-
-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."#,
- chrono::prelude::Utc::now().year(),
- copyright_holder
- ),
- )
-}
-
-pub(crate) fn asl2_checker() -> impl HeaderChecker {
- SingleLineChecker::new("Licensed under the Apache License, Version 2.0".to_string(), 10)
-}
diff --git a/nearby/src/file_header/mod.rs b/nearby/src/file_header/mod.rs
deleted file mode 100644
index 275a7d0..0000000
--- a/nearby/src/file_header/mod.rs
+++ /dev/null
@@ -1,559 +0,0 @@
-// Copyright 2023 Google LLC
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-// http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-//! Tools for checking for, or adding, headers (e.g. licenses, etc) in files.
-
-use std::{
- fs,
- io::{self, BufRead as _, Write as _},
- iter::FromIterator,
- path, thread,
-};
-
-pub mod license;
-
-/// A file header to check for or add to files.
-#[derive(Clone)]
-pub struct Header<C: HeaderChecker> {
- checker: C,
- header: String,
-}
-
-impl<C: HeaderChecker> Header<C> {
- /// Construct a new `Header` with the `checker` used to determine if the header is already
- /// present, and the plain `header` text to add (without any applicable comment syntax, etc).
- pub fn new(checker: C, header: String) -> Self {
- Self { checker, header }
- }
-
- /// Return true if the file has the desired header, false otherwise.
- pub fn header_present(&self, input: &mut impl io::Read) -> io::Result<bool> {
- self.checker.check(input)
- }
-
- /// Add the header, with appropriate formatting for the type of file indicated by `p`'s
- /// extension, if the header is not already present.
- /// Returns true if the header was added.
- pub fn add_header_if_missing(&self, p: &path::Path) -> Result<bool, AddHeaderError> {
- let err_mapper = |e| AddHeaderError::IoError(p.to_path_buf(), e);
- let contents = fs::read_to_string(p).map_err(err_mapper)?;
-
- if self.header_present(&mut contents.as_bytes()).map_err(err_mapper)? {
- return Ok(false);
- }
-
- let mut effective_header = header_delimiters(p)
- .ok_or_else(|| AddHeaderError::UnknownExtension(p.to_path_buf()))
- .map(|d| wrap_header(&self.header, d))?;
-
- let mut after_header = contents.as_str();
- // check for a magic first line
- if let Some((first_line, rest)) = contents.split_once('\n') {
- if MAGIC_FIRST_LINES.iter().any(|l| first_line.contains(l)) {
- let mut first_line = first_line.to_string();
- first_line.push('\n');
- effective_header.insert_str(0, &first_line);
- after_header = rest;
- }
- }
-
- // write the license
- let mut f =
- fs::OpenOptions::new().write(true).truncate(true).open(p).map_err(err_mapper)?;
- f.write_all(effective_header.as_bytes()).map_err(err_mapper)?;
- // newline to separate the header from previous contents
- f.write_all("\n".as_bytes()).map_err(err_mapper)?;
- f.write_all(after_header.as_bytes()).map_err(err_mapper)?;
-
- Ok(true)
- }
-}
-
-/// Errors that can occur when adding a header
-#[derive(Debug, thiserror::Error)]
-pub enum AddHeaderError {
- #[error("I/O error at {0:?}: {1}")]
- IoError(path::PathBuf, io::Error),
- #[error("Unknown file extension: {0:?}")]
- UnknownExtension(path::PathBuf),
-}
-
-/// Checks for headers in files, like licenses or author attribution.
-pub trait HeaderChecker: Send + Clone {
- /// Return true if the file has the desired header, false otherwise.
- fn check(&self, file: &mut impl io::Read) -> io::Result<bool>;
-}
-
-/// Checks for a in the first several lines of each file.
-#[derive(Clone)]
-pub struct SingleLineChecker {
- /// Pattern to do a substring match on in each of the first `max_lines` lines of the file
- pattern: String,
- /// Number of lines to search through
- max_lines: usize,
-}
-
-impl SingleLineChecker {
- /// Construct a `SingleLineChecker` that looks for `pattern` in the first `max_lines` of a file.
- pub(crate) fn new(pattern: String, max_lines: usize) -> Self {
- Self { pattern, max_lines }
- }
-}
-
-impl HeaderChecker for SingleLineChecker {
- fn check(&self, input: &mut impl io::Read) -> io::Result<bool> {
- let mut reader = io::BufReader::new(input);
- let mut lines_read = 0;
- // reuse buffer to minimize allocation
- let mut line = String::new();
- // only read the first bit of the file
- while lines_read < self.max_lines {
- line.clear();
- let bytes = reader.read_line(&mut line)?;
- if bytes == 0 {
- // EOF
- return Ok(false);
- }
- lines_read += 1;
-
- if line.contains(&self.pattern) {
- return Ok(true);
- }
- }
-
- Ok(false)
- }
-}
-
-#[derive(Copy, Clone)]
-enum CheckStatus {
- MisMatchedHeader,
- BinaryFile,
-}
-
-#[derive(Clone)]
-struct FileResult {
- path: path::PathBuf,
- status: CheckStatus,
-}
-
-#[derive(Clone, Default)]
-pub struct FileResults {
- pub mismatched_files: Vec<path::PathBuf>,
- pub binary_files: Vec<path::PathBuf>,
-}
-
-impl FileResults {
- pub fn has_failure(&self) -> bool {
- !self.mismatched_files.is_empty() || !self.binary_files.is_empty()
- }
-}
-
-impl FromIterator<FileResult> for FileResults {
- fn from_iter<I>(iter: I) -> FileResults
- where
- I: IntoIterator<Item = FileResult>,
- {
- let mut results = FileResults::default();
- for result in iter {
- match result.status {
- CheckStatus::MisMatchedHeader => results.mismatched_files.push(result.path),
- CheckStatus::BinaryFile => results.binary_files.push(result.path),
- }
- }
- results
- }
-}
-
-/// Recursively check for `header` in every file in `root` that matches `path_predicate`.
-///
-/// Returns a [`FileResults`] object containing the paths without headers detected.
-pub fn check_headers_recursively(
- root: &path::Path,
- path_predicate: impl Fn(&path::Path) -> bool,
- header: Header<impl HeaderChecker + 'static>,
- num_threads: usize,
-) -> Result<FileResults, CheckHeadersRecursivelyError> {
- let (path_tx, path_rx) = crossbeam::channel::unbounded::<path::PathBuf>();
- let (result_tx, result_rx) = crossbeam::channel::unbounded();
-
- // spawn a few threads to handle files in parallel
- let handles = (0..num_threads)
- .map(|_| {
- let path_rx = path_rx.clone();
- let result_tx = result_tx.clone();
- let header = header.clone();
- thread::spawn(move || {
- for p in path_rx {
- match fs::File::open(&p).and_then(|mut f| header.header_present(&mut f)) {
- Ok(header_present) => {
- if header_present {
- // no op
- } else {
- let res =
- FileResult { path: p, status: CheckStatus::MisMatchedHeader };
- result_tx.send(Ok(res)).unwrap();
- }
- }
- Err(e) if e.kind() == io::ErrorKind::InvalidData => {
- // Binary file - add to ignore in license.rs
- let res = FileResult { path: p, status: CheckStatus::BinaryFile };
- result_tx.send(Ok(res)).unwrap();
- }
- Err(e) => result_tx
- .send(Err(CheckHeadersRecursivelyError::IoError(p, e)))
- .unwrap(),
- }
- }
-
- // no more files
- })
- })
- .collect::<Vec<thread::JoinHandle<()>>>();
- // make sure result channel closes when threads complete
- drop(result_tx);
-
- find_files(root, path_predicate, path_tx)?;
-
- let res: FileResults = result_rx.into_iter().collect::<Result<_, _>>()?;
-
- for h in handles {
- h.join().unwrap();
- }
-
- Ok(res)
-}
-
-/// Errors that can occur when checking for headers recursively
-#[derive(Debug, thiserror::Error)]
-pub enum CheckHeadersRecursivelyError {
- #[error("I/O error at {0:?}: {1}")]
- IoError(path::PathBuf, io::Error),
- #[error("Walkdir error: {0}")]
- WalkdirError(#[from] walkdir::Error),
-}
-
-/// Add the provided `header` to any file in `root` that matches `path_predicate` and that doesn't
-/// already have a header as determined by `checker`.
-/// Returns a list of paths that had headers added.
-pub fn add_headers_recursively(
- root: &path::Path,
- path_predicate: impl Fn(&path::Path) -> bool,
- header: Header<impl HeaderChecker>,
-) -> Result<Vec<path::PathBuf>, AddHeadersRecursivelyError> {
- // likely no need for threading since adding headers is only done occasionally
- let (path_tx, path_rx) = crossbeam::channel::unbounded::<path::PathBuf>();
- find_files(root, path_predicate, path_tx)?;
-
- path_rx
- .into_iter()
- // keep the errors, or the ones with added headers
- .filter_map(|p| {
- match header.add_header_if_missing(&p).map_err(|e| match e {
- AddHeaderError::IoError(p, e) => AddHeadersRecursivelyError::IoError(p, e),
- AddHeaderError::UnknownExtension(e) => {
- AddHeadersRecursivelyError::UnknownExtension(e)
- }
- }) {
- Ok(added) => {
- if added {
- Some(Ok(p))
- } else {
- None
- }
- }
- Err(e) => Some(Err(e)),
- }
- })
- .collect::<Result<Vec<_>, _>>()
-}
-
-/// Errors that can occur when adding a header recursively
-#[derive(Debug, thiserror::Error)]
-pub enum AddHeadersRecursivelyError {
- #[error("I/O error at {0:?}: {1}")]
- IoError(path::PathBuf, io::Error),
- #[error("Walkdir error: {0}")]
- WalkdirError(#[from] walkdir::Error),
- #[error("Unknown file extension: {0:?}")]
- UnknownExtension(path::PathBuf),
-}
-
-/// Find all files starting from `root` that do not match the globs in `ignore`, publishing the
-/// resulting paths into `dest`.
-fn find_files(
- root: &path::Path,
- path_predicate: impl Fn(&path::Path) -> bool,
- dest: crossbeam::channel::Sender<path::PathBuf>,
-) -> Result<(), walkdir::Error> {
- for r in walkdir::WalkDir::new(root).into_iter() {
- let entry = r?;
- if entry.path().is_dir() || !path_predicate(entry.path()) {
- continue;
- }
- dest.send(entry.into_path()).unwrap()
- }
-
- Ok(())
-}
-
-/// Prepare a header for inclusion in a particular file syntax by wrapping it with
-/// comment characters as per the provided `delim`.
-fn wrap_header(orig_header: &str, delim: HeaderDelimiters) -> String {
- let mut out = String::new();
-
- if !delim.first_line.is_empty() {
- out.push_str(delim.first_line);
- out.push('\n');
- }
-
- // assumes header uses \n
- for line in orig_header.split('\n') {
- out.push_str(delim.content_line_prefix);
- out.push_str(line);
- // Remove any trailing whitespaces (excluding newlines) from `content_line_prefix + line`.
- // For example, if `content_line_prefix` is `// ` and `line` is empty, the resulting string
- // should be truncated to `//`.
- out.truncate(out.trim_end_matches([' ', '\t']).len());
- out.push('\n');
- }
-
- if !delim.last_line.is_empty() {
- out.push_str(delim.last_line);
- out.push('\n');
- }
-
- out
-}
-
-/// Returns the header prefix line, content line prefix, and suffix line for the extension of the
-/// provided path, or `None` if the extension is not recognized.
-fn header_delimiters(p: &path::Path) -> Option<HeaderDelimiters> {
- match p
- .extension()
- // if the extension isn't UTF-8, oh well
- .and_then(|os_str| os_str.to_str())
- .unwrap_or("")
- {
- "c" | "h" | "gv" | "java" | "scala" | "kt" | "kts" => Some(("/*", " * ", " */")),
- "js" | "mjs" | "cjs" | "jsx" | "tsx" | "css" | "scss" | "sass" | "ts" => {
- Some(("/**", " * ", " */"))
- }
- "cc" | "cpp" | "cs" | "go" | "hcl" | "hh" | "hpp" | "m" | "mm" | "proto" | "rs"
- | "swift" | "dart" | "groovy" | "v" | "sv" => Some(("", "// ", "")),
- "py" | "sh" | "yaml" | "yml" | "dockerfile" | "rb" | "gemfile" | "tcl" | "tf" | "bzl"
- | "pl" | "pp" | "build" => Some(("", "# ", "")),
- "el" | "lisp" => Some(("", ";; ", "")),
- "erl" => Some(("", "% ", "")),
- "hs" | "lua" | "sql" | "sdl" => Some(("", "-- ", "")),
- "html" | "xml" | "vue" | "wxi" | "wxl" | "wxs" => Some(("<!--", " ", "-->")),
- "php" => Some(("", "// ", "")),
- "ml" | "mli" | "mll" | "mly" => Some(("(**", " ", "*)")),
- // also handle whole filenames if extensions didn't match
- _ => match p.file_name().and_then(|os_str| os_str.to_str()).unwrap_or("") {
- "Dockerfile" => Some(("", "# ", "")),
- _ => None,
- },
- }
- .map(|(first_line, content_line_prefix, last_line)| HeaderDelimiters {
- first_line,
- content_line_prefix,
- last_line,
- })
-}
-
-/// Delimiters to use around and inside a header for a particular file syntax.
-#[derive(Clone, Copy)]
-struct HeaderDelimiters {
- /// Line to prepend before the header
- first_line: &'static str,
- /// Prefix before each line of the header itself
- content_line_prefix: &'static str,
- /// Line to append after the header
- last_line: &'static str,
-}
-
-const MAGIC_FIRST_LINES: [&str; 8] = [
- "#!", // shell script
- "<?xml", // XML declaratioon
- "<!doctype", // HTML doctype
- "# encoding:", // Ruby encoding
- "# frozen_string_literal:", // Ruby interpreter instruction
- "<?php", // PHP opening tag
- "# escape", // Dockerfile directive https://docs.docker.com/engine/reference/builder/#parser-directives
- "# syntax", // Dockerfile directive https://docs.docker.com/engine/reference/builder/#parser-directives
-];
-
-#[cfg(test)]
-mod tests {
- use super::*;
-
- #[test]
- fn single_line_checker_finds_header_when_present() {
- let input = r#"foo
- some license
- bar"#;
-
- assert!(test_header().checker.check(&mut input.as_bytes()).unwrap());
- }
-
- #[test]
- fn single_line_checker_doesnt_find_header_when_missing() {
- let input = r#"foo
- wrong license
- bar"#;
-
- assert!(!test_header().checker.check(&mut input.as_bytes()).unwrap());
- }
-
- #[test]
- fn single_line_checker_throws_error_when_missing_and_file_is_non_utf8() {
- let input = b"foo
- \x00\xff
- bar";
-
- assert_eq!(
- io::ErrorKind::InvalidData,
- test_header().checker.check(&mut input.as_slice()).unwrap_err().kind()
- );
- }
-
- #[test]
- fn single_line_checker_doesnt_panic_when_file_is_non_utf8() {
- let inputs: [&'static [u8]; 3] = [
- b"foo
- \x00\xff
- bar",
- b"foo
- some license
- \x00\xff
- bar",
- b"foo
- \x00\xff
- some license
- bar",
- ];
-
- for mut input in inputs {
- // Output is not defined for non-utf-8 files, but we should handle them with grace
- let _ = test_header().checker.check(&mut input);
- }
- }
-
- #[test]
- fn adds_header_with_empty_delimiters() {
- let file = tempfile::Builder::new().suffix(".rs").tempfile().unwrap();
- fs::write(file.path(), r#"not a license"#).unwrap();
-
- test_header().add_header_if_missing(file.path()).unwrap();
-
- assert_eq!(
- "// some license etc etc etc
-
-not a license",
- fs::read_to_string(file.path()).unwrap()
- );
- }
-
- #[test]
- fn adds_header_with_nonempty_delimiters() {
- let file = tempfile::Builder::new().suffix(".c").tempfile().unwrap();
- fs::write(file.path(), r#"not a license"#).unwrap();
-
- test_header().add_header_if_missing(file.path()).unwrap();
-
- assert_eq!(
- "/*
- * some license etc etc etc
- */
-
-not a license",
- fs::read_to_string(file.path()).unwrap()
- );
- }
-
- #[test]
- fn adds_header_trim_trailing_whitespace() {
- let file = tempfile::Builder::new().suffix(".c").tempfile().unwrap();
- fs::write(file.path(), r#"not a license"#).unwrap();
-
- test_header_with_blank_lines_and_trailing_whitespace()
- .add_header_if_missing(file.path())
- .unwrap();
-
- assert_eq!(
- "/*
- * some license
- * line with trailing whitespace.
- *
- * etc
- */
-
-not a license",
- fs::read_to_string(file.path()).unwrap()
- );
- }
-
- #[test]
- fn doesnt_add_header_when_already_present() {
- let file = tempfile::Builder::new().suffix(".rs").tempfile().unwrap();
- let initial_content = r#"
- // some license etc etc etc already present
- not a license"#;
- fs::write(file.path(), initial_content).unwrap();
-
- test_header().add_header_if_missing(file.path()).unwrap();
-
- assert_eq!(initial_content, fs::read_to_string(file.path()).unwrap());
- }
-
- #[test]
- fn adds_header_after_magic_first_line() {
- let file = tempfile::Builder::new().suffix(".xml").tempfile().unwrap();
- fs::write(
- file.path(),
- r#"<?xml version="1.0" encoding="UTF-8"?>
-<root />
-"#,
- )
- .unwrap();
-
- test_header().add_header_if_missing(file.path()).unwrap();
-
- assert_eq!(
- r#"<?xml version="1.0" encoding="UTF-8"?>
-<!--
- some license etc etc etc
--->
-
-<root />
-"#,
- fs::read_to_string(file.path()).unwrap()
- );
- }
-
- fn test_header() -> Header<SingleLineChecker> {
- Header::new(
- SingleLineChecker::new("some license".to_string(), 100),
- r#"some license etc etc etc"#.to_string(),
- )
- }
-
- fn test_header_with_blank_lines_and_trailing_whitespace() -> Header<SingleLineChecker> {
- Header::new(
- SingleLineChecker::new("some license".to_string(), 100),
- "some license\nline with trailing whitespace. \n\netc".to_string(),
- )
- }
-}
diff --git a/nearby/src/fuzzers.rs b/nearby/src/fuzzers.rs
index bfb4bcc..192d59c 100644
--- a/nearby/src/fuzzers.rs
+++ b/nearby/src/fuzzers.rs
@@ -12,7 +12,7 @@
// See the License for the specific language governing permissions and
// limitations under the License.
-use crate::{run_cmd_shell_with_color, YellowStderr};
+use cmd_runner::{run_cmd_shell_with_color, YellowStderr};
use std::{fs, path};
pub(crate) fn run_rust_fuzzers(root: &path::Path) -> anyhow::Result<()> {
diff --git a/nearby/src/jni.rs b/nearby/src/jni.rs
index 903ac6d..efd6e6c 100644
--- a/nearby/src/jni.rs
+++ b/nearby/src/jni.rs
@@ -12,7 +12,7 @@
// See the License for the specific language governing permissions and
// limitations under the License.
-use crate::support::run_cmd_shell;
+use cmd_runner::run_cmd_shell;
use std::path;
// This has to happen after both boringssl has been built and prepare rust openssl patches has been run.
@@ -20,6 +20,17 @@ pub fn check_ldt_jni(root: &path::Path) -> anyhow::Result<()> {
for feature in ["opensslbssl", "boringssl"] {
run_cmd_shell(root, format!("cargo --config .cargo/config-boringssl.toml build -p ldt_np_jni --no-default-features --features={}", feature))?;
}
+ Ok(())
+}
+
+pub fn run_kotlin_tests(root: &path::Path) -> anyhow::Result<()> {
+ let kotlin_lib_path = root.to_path_buf().join("presence/ldt_np_jni/java/LdtNpJni");
+ run_cmd_shell(&kotlin_lib_path, "./gradlew :test")?;
+ Ok(())
+}
+pub fn run_ukey2_jni_tests(root: &path::Path) -> anyhow::Result<()> {
+ let ukey2_jni_path = root.to_path_buf().join("connections/ukey2/ukey2_jni/java");
+ run_cmd_shell(&ukey2_jni_path, "./gradlew :test")?;
Ok(())
}
diff --git a/nearby/src/license.rs b/nearby/src/license.rs
index 657a62e..387ca27 100644
--- a/nearby/src/license.rs
+++ b/nearby/src/license.rs
@@ -12,7 +12,8 @@
// See the License for the specific language governing permissions and
// limitations under the License.
-use crate::file_header::{self, check_headers_recursively};
+use chrono::Datelike;
+use file_header::{check_headers_recursively, license::spdx::*};
use std::path;
pub(crate) fn check_license_headers(root: &path::Path) -> anyhow::Result<()> {
@@ -21,11 +22,14 @@ pub(crate) fn check_license_headers(root: &path::Path) -> anyhow::Result<()> {
let results = check_headers_recursively(
root,
|p| !ignore.is_match(p),
- file_header::license::apache_2("Google LLC"),
+ APACHE_2_0.build_header(YearCopyrightOwnerValue::new(
+ u32::try_from(chrono::Utc::now().year())?,
+ "Google LLC".to_string(),
+ )),
4,
)?;
- for path in results.mismatched_files.iter() {
+ for path in results.no_header_files.iter() {
eprintln!("Header not present: {path:?}");
}
@@ -48,7 +52,10 @@ pub(crate) fn add_license_headers(root: &path::Path) -> anyhow::Result<()> {
for p in file_header::add_headers_recursively(
root,
|p| !ignore.is_match(p),
- file_header::license::apache_2("Google LLC"),
+ APACHE_2_0.build_header(YearCopyrightOwnerValue::new(
+ u32::try_from(chrono::Utc::now().year())?,
+ "Google LLC".to_string(),
+ )),
)? {
println!("Added header: {:?}", p);
}
@@ -91,7 +98,7 @@ fn license_ignore_dirs() -> Vec<&'static str> {
"**/.DS_Store",
"**/fuzz/corpus/**",
"**/.*.swp",
- "**/Session.vim",
+ "**/*.vim",
"**/*.properties",
"**/third_party/**",
"**/*.png",
@@ -99,5 +106,8 @@ fn license_ignore_dirs() -> Vec<&'static str> {
"**/node_modules/**",
"**/.angular/**",
"**/.editorconfig",
+ "**/*.class",
+ "**/fuzz/artifacts/**",
+ "**/cmake-build-debug/**",
]
}
diff --git a/nearby/src/main.rs b/nearby/src/main.rs
index ab55b32..1d1ac16 100644
--- a/nearby/src/main.rs
+++ b/nearby/src/main.rs
@@ -15,17 +15,15 @@
extern crate core;
use clap::Parser as _;
+use cmd_runner::run_cmd_shell;
use env_logger::Env;
use std::{env, path};
-use support::*;
mod crypto_ffi;
mod ffi;
-mod file_header;
mod fuzzers;
mod jni;
mod license;
-mod support;
mod ukey2;
fn main() -> anyhow::Result<()> {
@@ -37,28 +35,31 @@ fn main() -> anyhow::Result<()> {
);
match cli.subcommand {
- Subcommand::CheckEverything { ref check_options, ref bssl_options } => {
- check_everything(&root_dir, check_options, bssl_options)?
+ 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 => ffi::check_everything(&root_dir)?,
- Subcommand::BuildBoringssl(ref bssl_options) => {
- crypto_ffi::build_boringssl(&root_dir, bssl_options)?
- }
- Subcommand::CheckBoringssl(ref bssl_options) => {
- crypto_ffi::check_boringssl(&root_dir, bssl_options)?
+ Subcommand::FfiCheckEverything(ref options) => ffi::check_everything(&root_dir, options)?,
+ Subcommand::BoringsslCheckEverything(ref options) => {
+ crypto_ffi::boringssl_check_everything(&root_dir, options)?
}
+ Subcommand::BuildBoringssl => crypto_ffi::build_boringssl(&root_dir)?,
+ Subcommand::CheckBoringssl(ref options) => crypto_ffi::check_boringssl(&root_dir, options)?,
Subcommand::PrepareRustOpenssl => crypto_ffi::prepare_patched_rust_openssl(&root_dir)?,
- Subcommand::CheckOpenssl => crypto_ffi::check_openssl(&root_dir)?,
+ Subcommand::CheckOpenssl(ref options) => crypto_ffi::check_openssl(&root_dir, options)?,
Subcommand::RunRustFuzzers => fuzzers::run_rust_fuzzers(&root_dir)?,
Subcommand::BuildFfiFuzzers => fuzzers::build_ffi_fuzzers(&root_dir)?,
Subcommand::CheckLicenseHeaders => license::check_license_headers(&root_dir)?,
Subcommand::AddLicenseHeaders => license::add_license_headers(&root_dir)?,
- Subcommand::CheckLdtFfi => ffi::check_ldt_ffi(&root_dir)?,
- Subcommand::CheckUkey2Ffi => ukey2::check_ukey2_ffi(&root_dir)?,
+ Subcommand::CheckLdtFfi => ffi::check_ldt_ffi_rust(&root_dir)?,
+ Subcommand::CheckUkey2Ffi(ref options) => ukey2::check_ukey2_ffi(&root_dir, options)?,
+ Subcommand::RunUkey2JniTests => jni::run_ukey2_jni_tests(&root_dir)?,
Subcommand::CheckLdtJni => jni::check_ldt_jni(&root_dir)?,
- Subcommand::CheckNpFfi => ffi::check_np_ffi(&root_dir)?,
- Subcommand::CheckCmakeProjects => ffi::check_cmake_projects(&root_dir)?,
+ Subcommand::CheckNpFfi(ref options) => ffi::check_np_ffi_rust(&root_dir, options)?,
+ Subcommand::CheckLdtCmake(ref options) => ffi::check_ldt_cmake(&root_dir, options)?,
+ Subcommand::CheckNpFfiCmake(ref options) => ffi::check_np_ffi_cmake(&root_dir, options)?,
+ Subcommand::RunKotlinTests => jni::run_kotlin_tests(&root_dir)?,
}
Ok(())
@@ -79,6 +80,9 @@ pub fn check_workspace(root: &path::Path, options: &CheckOptions) -> anyhow::Res
// upstream rust-openssl crate's handling of empty slices. This repros consistently when
// using the rust-openssl crate backed by openssl-sys on Ubuntu 20.04.
"cargo test --workspace --quiet --exclude crypto_provider_openssl -- --color=always",
+ // Test ukey2 builds with different crypto providers
+ "cargo test -p ukey2_connections -p ukey2_rs --no-default-features --features test_rustcrypto",
+ "cargo test -p ukey2_connections -p ukey2_rs --no-default-features --features test_openssl",
// ensure the docs are valid (cross-references to other code, etc)
concat!(
"RUSTDOCFLAGS='--deny warnings -Z unstable-options --enable-index-page --generate-link-to-definition' ",
@@ -93,24 +97,33 @@ pub fn check_workspace(root: &path::Path, options: &CheckOptions) -> anyhow::Res
Ok(())
}
-pub fn check_everything(
- root: &path::Path,
- check_options: &CheckOptions,
- bssl_options: &BuildBoringSslOptions,
-) -> anyhow::Result<()> {
+
+/// 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)?;
- crypto_ffi::check_boringssl(root, bssl_options)?;
- crypto_ffi::check_openssl(root)?;
- ffi::check_everything(root)?;
+ crypto_ffi::check_boringssl(root, &check_options.cargo_options)?;
+ crypto_ffi::check_openssl(root, &check_options.cargo_options)?;
+ ffi::check_everything(root, &check_options.cargo_options)?;
jni::check_ldt_jni(root)?;
- ukey2::check_ukey2_ffi(root)?;
+ jni::run_kotlin_tests(root)?;
+ jni::run_ukey2_jni_tests(root)?;
+ ukey2::check_ukey2_ffi(root, &check_options.cargo_options)?;
fuzzers::run_rust_fuzzers(root)?;
fuzzers::build_ffi_fuzzers(root)?;
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,56 +136,63 @@ enum Subcommand {
CheckEverything {
#[command(flatten)]
check_options: CheckOptions,
- #[command(flatten)]
- bssl_options: BuildBoringSslOptions,
},
+ /// 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
+ /// + check-openssl)
+ BoringsslCheckEverything(CargoOptions),
/// Clones boringssl and uses bindgen to generate the rust crate
- BuildBoringssl(BuildBoringSslOptions),
+ BuildBoringssl,
/// Run crypto provider tests using boringssl backend
- CheckBoringssl(BuildBoringSslOptions),
+ CheckBoringssl(CargoOptions),
/// Applies AOSP specific patches to the 3p `openssl` crate so that it can use a boringssl
/// backend
PrepareRustOpenssl,
/// Run crypto provider tests using openssl crate with boringssl backend
- CheckOpenssl,
+ CheckOpenssl(CargoOptions),
/// Build and run pure Rust fuzzers for 10000 runs
RunRustFuzzers,
/// Build FFI fuzzers
BuildFfiFuzzers,
/// Builds and runs tests for all C/C++ projects. This is a combination of CheckNpFfi,
/// CheckLdtFfi, and CheckCmakeBuildAndTests
- FfiCheckEverything,
+ FfiCheckEverything(CargoOptions),
/// Builds the crate checks the cbindgen generation of C/C++ bindings
- CheckNpFfi,
+ CheckNpFfi(CargoOptions),
/// Builds ldt_np_adv_ffi crate with all possible different sets of feature flags
CheckLdtFfi,
/// Checks the CMake build and runs all of the C/C++ tests
- CheckCmakeProjects,
+ CheckLdtCmake(CargoOptions),
+ /// Checks the CMake build and runs all of the C/C++ tests
+ CheckNpFfiCmake(CargoOptions),
/// Checks the workspace 3rd party crates and makes sure they have a valid license
CheckLicenseHeaders,
/// Generate new headers for any files that are missing them
AddLicenseHeaders,
/// Builds and runs tests for the UKEY2 FFI
- CheckUkey2Ffi,
+ CheckUkey2Ffi(CargoOptions),
/// Checks the build of ldt_jni wrapper with non default features, ie rust-openssl, and boringssl
CheckLdtJni,
+ /// Runs the kotlin tests of the LDT Jni API
+ RunKotlinTests,
+ /// Checks the build of the ukey2_jni wrapper and runs tests
+ RunUkey2JniTests,
}
#[derive(clap::Args, Debug, Clone, Default)]
pub struct CheckOptions {
#[arg(long, help = "reformat files with cargo fmt")]
reformat: bool,
+ #[command(flatten)]
+ cargo_options: CargoOptions,
}
#[derive(clap::Args, Debug, Clone, Default)]
-pub struct BuildBoringSslOptions {
- #[arg(
- long,
- // the commit after this one causes failures in rust-openssl
- default_value = "d995d82ad53133017e34b009e9c6912b2ef6aeb7",
- help = "Commit hash to use when checking out boringssl"
- )]
- commit_hash: String,
+pub struct CargoOptions {
+ #[arg(long, help = "whether to run cargo with --locked")]
+ locked: bool,
}
diff --git a/nearby/src/ukey2.rs b/nearby/src/ukey2.rs
index e2547e7..e548ab0 100644
--- a/nearby/src/ukey2.rs
+++ b/nearby/src/ukey2.rs
@@ -12,26 +12,34 @@
// See the License for the specific language governing permissions and
// limitations under the License.
-use crate::{run_cmd_shell, run_cmd_shell_with_color, YellowStderr};
+use cmd_runner::{run_cmd_shell, run_cmd_shell_with_color, YellowStderr};
use std::{fs, path};
-pub(crate) fn check_ukey2_ffi(root: &path::Path) -> anyhow::Result<()> {
+use crate::CargoOptions;
+
+pub(crate) fn check_ukey2_ffi(
+ root: &path::Path,
+ cargo_options: &CargoOptions,
+) -> anyhow::Result<()> {
log::info!("Checking Ukey2 ffi");
- let mut ffi_dir = root.to_path_buf();
- ffi_dir.push("connections/ukey2/ukey2_c_ffi");
+ let ffi_dir = root.join("connections/ukey2/ukey2_c_ffi");
+
+ let locked_arg = if cargo_options.locked { "--locked" } else { "" };
// Default build, RustCrypto
- run_cmd_shell(&ffi_dir, "cargo build --quiet --release --lib")?;
+ run_cmd_shell(&ffi_dir, format!("cargo build {locked_arg} --quiet --release --lib"))?;
// OpenSSL
- run_cmd_shell(&ffi_dir, "cargo build --quiet --no-default-features --features=openssl")?;
+ run_cmd_shell(
+ &ffi_dir,
+ format!("cargo build {locked_arg} --quiet --no-default-features --features=openssl"),
+ )?;
run_cmd_shell(&ffi_dir, "cargo doc --quiet --no-deps")?;
run_cmd_shell(&ffi_dir, "cargo clippy --no-default-features --features=openssl")?;
run_cmd_shell(&ffi_dir, "cargo deny check")?;
- let mut ffi_build_dir = ffi_dir.to_path_buf();
- ffi_build_dir.push("cpp/build");
+ let ffi_build_dir = ffi_dir.join("cpp/build");
fs::create_dir_all(&ffi_build_dir)?;
run_cmd_shell_with_color::<YellowStderr>(&ffi_build_dir, "cmake ..")?;
run_cmd_shell_with_color::<YellowStderr>(&ffi_build_dir, "cmake --build .")?;
diff --git a/nearby/presence/handle_map/Cargo.toml b/nearby/util/handle_map/Cargo.toml
index 8f00bf7..13973d4 100644
--- a/nearby/presence/handle_map/Cargo.toml
+++ b/nearby/util/handle_map/Cargo.toml
@@ -4,15 +4,15 @@ version.workspace = true
edition.workspace = true
publish.workspace = true
+[lints]
+workspace = true
+
[dependencies]
-hashbrown.workspace = true
-lock_api.workspace = true
-portable-atomic.workspace = true
-spin.workspace = true
-crypto_provider.workspace = true
+lock_adapter.workspace = true
[dev-dependencies]
criterion.workspace = true
+lazy_static.workspace = true
[[bench]]
name = "benches"
diff --git a/nearby/presence/handle_map/benches/benches.rs b/nearby/util/handle_map/benches/benches.rs
index f1ee427..f1988ba 100644
--- a/nearby/presence/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 @@ fn single_threaded_read_benchmark(c: &mut Criterion) {
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
new file mode 100644
index 0000000..c19a012
--- /dev/null
+++ b/nearby/util/handle_map/src/declare_handle_map.rs
@@ -0,0 +1,168 @@
+// 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.
+
+//! Implementation of the `declare_handle_map!` macro
+
+#[macro_export]
+/// ```ignore
+/// declare_handle_map! {
+/// mod $handle_module_name {
+/// #[dimensions = $map_dimension_provider]
+/// type $handle_type_name: HandleLike<Object = $wrapped_type>;
+/// }
+/// }
+/// ```
+///
+/// Declares a new public module with name `handle_module_name` which includes a new type
+/// `handle_type_name` which is `#[repr(C)]` and represents FFI-accessible handles
+/// to values of type `wrapped_type`.
+///
+/// Internal to the generated module, a new static `SingletonHandleMap` is created, where the
+/// maximum number of active handles and the number of shards are given by
+/// the dimensions returned by evaluation of the `map_dimension_provider` expression.
+///
+/// Note: `map_dimension_provider` will be evaluated within the defined module's scope,
+/// so you will likely need to use `super` to refer to definitions in the enclosing scope.
+///
+/// # Example
+/// The following code defines an FFI-safe type `StringHandle` which references
+/// the `String` data-type, and uses it to define a (contrived)
+/// function `sample` which will print "Hello World".
+///
+/// ```
+/// #[macro_use]///
+/// extern crate lazy_static;
+///
+/// use core::ops::Deref;
+/// use handle_map::{declare_handle_map, HandleMapDimensions, HandleLike};
+///
+/// fn get_string_handle_map_dimensions() -> HandleMapDimensions {
+/// HandleMapDimensions {
+/// num_shards: 8,
+/// max_active_handles: 100,
+/// }
+/// }
+///
+/// declare_handle_map! {
+/// mod string_handle {
+/// #[dimensions = super::get_string_handle_map_dimensions()]
+/// type StringHandle: HandleLike<Object = String>;
+/// }
+/// }
+///
+/// use string_handle::StringHandle;
+///
+/// fn main() {
+/// // Note: this method could panic if there are
+/// // more than 99 outstanding handles.
+///
+/// // Allocate a new string-handle pointing to the string "Hello"
+/// let handle = StringHandle::allocate(|| { "Hello".to_string() }).unwrap();
+/// {
+/// // Obtain a write-guard on the contents of our handle
+/// let mut handle_write_guard = handle.get_mut().unwrap();
+/// handle_write_guard.push_str(" World");
+/// // Write guard is auto-dropped at the end of this block.
+/// }
+/// {
+/// // Obtain a read-guard on the contents of our handle.
+/// // Note that we had to ensure that the write-guard was
+/// // dropped prior to doing this, or else execution
+/// // could potentially hang.
+/// let handle_read_guard = handle.get().unwrap();
+/// println!("{}", handle_read_guard.deref());
+/// }
+/// // Clean up the data behind the created handle
+/// handle.deallocate().unwrap();
+/// }
+///
+/// ```
+macro_rules! declare_handle_map {
+ (
+ mod $handle_module_name:ident {
+ #[dimensions = $map_dimension_provider:expr]
+ type $handle_type_name:ident: HandleLike<Object = $wrapped_type:ty>;
+ }
+ ) => {
+ #[doc = ::core::concat!(
+ "Macro-generated (via `handle_map::declare_handle_map!`) module which",
+ " defines the `", ::core::stringify!($handle_module_name), "::",
+ ::core::stringify!($handle_type_name), "` FFI-transmissible handle type ",
+ " which references values of type `", ::core::stringify!($wrapped_type), "`."
+ )]
+ pub mod $handle_module_name {
+
+ lazy_static! {
+ static ref GLOBAL_HANDLE_MAP: $crate::HandleMap<$wrapped_type> =
+ $crate::HandleMap::with_dimensions($map_dimension_provider);
+ }
+
+ #[doc = ::core::concat!(
+ "A `#[repr(C)]` handle to a value of type `",
+ ::core::stringify!($wrapped_type), "`."
+ )]
+ #[repr(C)]
+ #[derive(Clone, Copy, PartialEq, Eq)]
+ pub struct $handle_type_name {
+ handle_id: u64,
+ }
+
+ impl $handle_type_name {
+ /// Cast the given raw Handle to this HandleLike
+ pub fn from_handle(handle: $crate::Handle) -> Self {
+ Self { handle_id: handle.get_id() }
+ }
+
+ /// Get this HandleLike as a raw Handle.
+ pub fn get_as_handle(&self) -> $crate::Handle {
+ $crate::Handle::from_id(self.handle_id)
+ }
+ }
+ 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> {
+ GLOBAL_HANDLE_MAP
+ .allocate(initial_value_provider)
+ .map(|derived_handle| Self { handle_id: derived_handle.get_id() })
+ }
+ fn get(
+ &self,
+ ) -> Result<$crate::ObjectReadGuardImpl<$wrapped_type>, $crate::HandleNotPresentError>
+ {
+ GLOBAL_HANDLE_MAP.get(self.get_as_handle())
+ }
+ fn get_mut(
+ &self,
+ ) -> Result<
+ $crate::ObjectReadWriteGuardImpl<$wrapped_type>,
+ $crate::HandleNotPresentError,
+ > {
+ GLOBAL_HANDLE_MAP.get_mut(self.get_as_handle())
+ }
+ fn deallocate(self) -> Result<$wrapped_type, $crate::HandleNotPresentError> {
+ GLOBAL_HANDLE_MAP.deallocate(self.get_as_handle())
+ }
+ }
+ }
+ };
+}
diff --git a/nearby/util/handle_map/src/guard.rs b/nearby/util/handle_map/src/guard.rs
new file mode 100644
index 0000000..0c8c9d7
--- /dev/null
+++ b/nearby/util/handle_map/src/guard.rs
@@ -0,0 +1,141 @@
+// 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 crate::Handle;
+use core::ops::{Deref, DerefMut};
+use lock_adapter::std::RwMapping;
+use std::collections::HashMap;
+use std::marker::PhantomData;
+
+/// A RAII read lock guard for an object in a [`HandleMap`](crate::HandleMap)
+/// pointed-to by a given [`Handle`]. When this struct is
+/// dropped, the underlying read lock on the associated
+/// shard will be dropped.
+pub struct ObjectReadGuardImpl<'a, T: 'a> {
+ pub(crate) guard: lock_adapter::std::MappedRwLockReadGuard<
+ 'a,
+ <Self as ObjectReadGuard>::Arg,
+ <Self as ObjectReadGuard>::Ret,
+ <Self as ObjectReadGuard>::Mapping,
+ >,
+}
+
+/// Trait implemented for an ObjectReadGuard which defines the associated types of the guard and a
+/// mapping for how the guard is retrieved from its parent object
+pub trait ObjectReadGuard: Deref<Target = Self::Ret> {
+ /// The mapping which defines how a guard is retrieved for `Self::Ret` from `Self::Arg`
+ type Mapping: RwMapping<Arg = Self::Arg, Ret = Self::Ret>;
+ /// The argument type input to the mapping functions
+ type Arg;
+ /// The Return type of the mapping functions
+ type Ret;
+}
+
+impl<'a, T> Deref for ObjectReadGuardImpl<'a, T> {
+ type Target = T;
+
+ fn deref(&self) -> &Self::Target {
+ self.guard.deref()
+ }
+}
+
+pub struct ObjectReadGuardMapping<'a, T> {
+ pub(crate) handle: Handle,
+ pub(crate) _marker: PhantomData<&'a T>,
+}
+
+impl<'a, T> RwMapping for ObjectReadGuardMapping<'a, T> {
+ type Arg = HashMap<Handle, T>;
+ type Ret = T;
+
+ fn map<'b>(&self, arg: &'b Self::Arg) -> &'b Self::Ret {
+ #[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 {
+ #[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.")
+ }
+}
+
+impl<'a, T> ObjectReadGuard for ObjectReadGuardImpl<'a, T> {
+ type Mapping = ObjectReadGuardMapping<'a, T>;
+ type Arg = HashMap<Handle, T>;
+ type Ret = T;
+}
+
+/// A RAII read-write lock guard for an object in a [`HandleMap`](crate::HandleMap)
+/// pointed-to by a given [`Handle`]. When this struct is
+/// dropped, the underlying read-write lock on the associated
+/// shard will be dropped.
+pub struct ObjectReadWriteGuardImpl<'a, T: 'a> {
+ pub(crate) guard: lock_adapter::std::MappedRwLockWriteGuard<
+ 'a,
+ <Self as ObjectReadWriteGuard>::Arg,
+ <Self as ObjectReadWriteGuard>::Ret,
+ <Self as ObjectReadWriteGuard>::Mapping,
+ >,
+}
+
+/// Trait implemented for an object read guard which defines the associated types of the guard and a
+/// mapping for how the guard is retrieved from its parent object
+pub trait ObjectReadWriteGuard: Deref<Target = Self::Ret> + DerefMut<Target = Self::Ret> {
+ /// The mapping which defines how a guard is retrieved for `Ret` from `Arg`
+ type Mapping: RwMapping<Arg = Self::Arg, Ret = Self::Ret>;
+ /// The argument type input to the mapping functions
+ type Arg;
+ /// The Return type of the mapping functions
+ type Ret;
+}
+
+pub struct ObjectReadWriteGuardMapping<'a, T> {
+ pub(crate) handle: Handle,
+ pub(crate) _marker: PhantomData<&'a T>,
+}
+
+impl<'a, T> Deref for ObjectReadWriteGuardImpl<'a, T> {
+ type Target = T;
+
+ fn deref(&self) -> &Self::Target {
+ self.guard.deref()
+ }
+}
+
+impl<'a, T> DerefMut for ObjectReadWriteGuardImpl<'a, T> {
+ fn deref_mut(&mut self) -> &mut Self::Target {
+ self.guard.deref_mut()
+ }
+}
+
+impl<'a, T> ObjectReadWriteGuard for ObjectReadWriteGuardImpl<'a, T> {
+ type Mapping = ObjectReadWriteGuardMapping<'a, T>;
+ type Arg = HashMap<Handle, T>;
+ type Ret = T;
+}
+
+impl<'a, T> RwMapping for ObjectReadWriteGuardMapping<'a, T> {
+ type Arg = HashMap<Handle, T>;
+ type Ret = T;
+
+ fn map<'b>(&self, arg: &'b Self::Arg) -> &'b Self::Ret {
+ #[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 {
+ #[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
new file mode 100644
index 0000000..22009c1
--- /dev/null
+++ b/nearby/util/handle_map/src/lib.rs
@@ -0,0 +1,302 @@
+// 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 thread-safe implementation of a map for managing object handles,
+//! a safer alternative to raw pointers for FFI interop.
+
+use core::fmt::Debug;
+use std::boxed::Box;
+use std::sync::atomic::{AtomicU32, AtomicU64, Ordering};
+use std::vec::Vec;
+
+pub mod declare_handle_map;
+mod guard;
+pub(crate) mod shard;
+
+#[cfg(test)]
+mod tests;
+
+pub use guard::{ObjectReadGuardImpl, ObjectReadWriteGuardImpl};
+
+use shard::{HandleMapShard, ShardAllocationError};
+
+/// An individual handle to be given out by a [`HandleMap`].
+/// This representation is untyped, and just a wrapper
+/// around a handle-id, in contrast to implementors of `HandleLike`.
+#[derive(Clone, Copy, PartialEq, Eq, Hash)]
+pub struct Handle {
+ handle_id: u64,
+}
+
+impl From<&Handle> for Handle {
+ fn from(handle: &Handle) -> Self {
+ *handle
+ }
+}
+
+impl Handle {
+ /// Constructs a handle wrapping the given ID.
+ ///
+ /// No validity checks are done on the wrapped ID
+ /// to ensure that the given ID is active in
+ /// any specific handle-map, and the type
+ /// of the handle is not represented.
+ ///
+ /// As a result, this method is only useful for
+ /// allowing access to handles from an FFI layer.
+ pub fn from_id(handle_id: u64) -> Self {
+ Self { handle_id }
+ }
+
+ /// Gets the ID for this handle.
+ ///
+ /// Since the underlying handle is un-typed,`
+ /// this method is only suitable for
+ /// transmitting handles across an FFI layer.
+ pub fn get_id(&self) -> u64 {
+ self.handle_id
+ }
+
+ /// Derives the shard index from the handle id
+ fn get_shard_index(&self, num_shards: u8) -> usize {
+ (self.handle_id % (num_shards as u64)) as usize
+ }
+}
+
+/// Error raised when attempting to allocate into a full handle-map.
+#[derive(Debug)]
+pub struct HandleMapFullError;
+
+/// Error raised when the entry for a given [`Handle`] doesn't exist.
+#[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.
+#[repr(C)]
+#[derive(Clone, Copy)]
+pub struct HandleMapDimensions {
+ /// The number of shards which are employed
+ /// by the associated handle-map.
+ pub num_shards: u8,
+ /// The maximum number of active handles which may be
+ /// stored within the associated handle-map.
+ pub max_active_handles: u32,
+}
+
+/// A thread-safe mapping from "handle"s [like pointers, but safer]
+/// to underlying structures, supporting allocations, reads, writes,
+/// and deallocations of objects behind handles.
+pub struct HandleMap<T: Send + Sync> {
+ /// The dimensions of this handle-map
+ dimensions: HandleMapDimensions,
+
+ /// The individually-lockable "shards" of the handle-map,
+ /// among which the keys will be roughly uniformly-distributed.
+ handle_map_shards: Box<[HandleMapShard<T>]>,
+
+ /// An atomically-incrementing counter which tracks the
+ /// next handle ID which allocations will attempt to use.
+ new_handle_id_counter: AtomicU64,
+
+ /// An atomic integer roughly tracking the number of
+ /// currently-outstanding allocated entries in this
+ /// handle-map among all [`HandleMapShard`]s.
+ outstanding_allocations_counter: AtomicU32,
+}
+
+impl<T: Send + Sync> HandleMap<T> {
+ /// Creates a new handle-map with the given `HandleMapDimensions`.
+ pub fn with_dimensions(dimensions: HandleMapDimensions) -> Self {
+ let mut handle_map_shards = Vec::with_capacity(dimensions.num_shards as usize);
+ for _ in 0..dimensions.num_shards {
+ handle_map_shards.push(HandleMapShard::default());
+ }
+ let handle_map_shards = handle_map_shards.into_boxed_slice();
+ Self {
+ dimensions,
+ handle_map_shards,
+ new_handle_id_counter: AtomicU64::new(0),
+ outstanding_allocations_counter: AtomicU32::new(0),
+ }
+ }
+}
+
+impl<T: Send + Sync> HandleMap<T> {
+ /// Allocates 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 `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,
+ // since the only invariant that we want to enforce is that concurrently-running
+ // threads always get distinct new handle-ids.
+ let new_handle_id = self.new_handle_id_counter.fetch_add(1, Ordering::Relaxed);
+ let new_handle = Handle::from_id(new_handle_id);
+ 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.
+ #[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(HandleMapTryAllocateError::HandleMapFull);
+ }
+ Err(ShardAllocationError::EntryOccupied(thrown_back_provider)) => {
+ // We need to do the whole thing again with a new ID
+ initial_value_provider = thrown_back_provider;
+ }
+ }
+ }
+ }
+
+ /// Gets a read-only reference to an object within the given handle-map,
+ /// 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);
+ #[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,
+ /// if the given handle is present. Otherwise, returns [`HandleNotPresentError`].
+ pub fn get_mut(
+ &self,
+ handle: Handle,
+ ) -> Result<ObjectReadWriteGuardImpl<T>, HandleNotPresentError> {
+ let shard_index = handle.get_shard_index(self.dimensions.num_shards);
+ #[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
+ /// the handle-map, returning the removed object if it
+ /// exists. Otherwise, returns [`HandleNotPresentError`].
+ pub fn deallocate(&self, handle: Handle) -> Result<T, HandleNotPresentError> {
+ let shard_index = handle.get_shard_index(self.dimensions.num_shards);
+ #[allow(clippy::expect_used)]
+ self.handle_map_shards
+ .get(shard_index)
+ .expect("shard index is always in range")
+ .deallocate(handle, &self.outstanding_allocations_counter)
+ }
+
+ /// Gets the actual number of elements stored in the entire map.
+ /// Only suitable for single-threaded sections of tests.
+ #[cfg(test)]
+ pub(crate) fn len(&self) -> usize {
+ self.handle_map_shards.iter().map(|s| s.len()).sum()
+ }
+
+ /// Sets the new-handle-id counter to the given value.
+ /// Only suitable for tests.
+ #[cfg(test)]
+ pub(crate) fn set_new_handle_id_counter(&mut self, value: u64) {
+ self.new_handle_id_counter = AtomicU64::new(value);
+ }
+}
+
+/// Externally-facing trait for things which behave like handle-map handles
+/// with a globally-defined handle-map for the type.
+pub trait HandleLike: Sized {
+ /// The underlying object type pointed-to by this handle
+ type Object: Send + Sync;
+
+ /// 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>;
+
+ /// Gets a RAII read-guard on the contents behind this handle.
+ fn get(&self) -> Result<ObjectReadGuardImpl<Self::Object>, HandleNotPresentError>;
+
+ /// Gets a RAII read-write guard on the contents behind this handle.
+ fn get_mut(&self) -> Result<ObjectReadWriteGuardImpl<Self::Object>, HandleNotPresentError>;
+
+ /// Deallocates the contents behind this handle.
+ fn deallocate(self) -> Result<Self::Object, HandleNotPresentError>;
+}
diff --git a/nearby/util/handle_map/src/shard.rs b/nearby/util/handle_map/src/shard.rs
new file mode 100644
index 0000000..e5aad2f
--- /dev/null
+++ b/nearby/util/handle_map/src/shard.rs
@@ -0,0 +1,198 @@
+// 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 core::ops::{Deref, DerefMut};
+use lock_adapter::std::{RwLock, RwLockReadGuard, RwLockWriteGuard};
+use lock_adapter::RwLock as _;
+use std::collections::hash_map::Entry::{Occupied, Vacant};
+use std::collections::HashMap;
+use std::marker::PhantomData;
+use std::sync::atomic::{AtomicU32, Ordering};
+
+use crate::guard::{
+ ObjectReadGuardImpl, ObjectReadGuardMapping, ObjectReadWriteGuardImpl,
+ ObjectReadWriteGuardMapping,
+};
+use crate::{Handle, HandleNotPresentError};
+
+// Bunch o' type aliases to make talking about them much easier in the shard code.
+type ShardMapType<T> = HashMap<Handle, T>;
+type ShardReadWriteLock<T> = RwLock<ShardMapType<T>>;
+type ShardReadGuard<'a, T> = RwLockReadGuard<'a, ShardMapType<T>>;
+type ShardReadWriteGuard<'a, T> = RwLockWriteGuard<'a, ShardMapType<T>>;
+
+/// Internal error enum for failed allocations into a given shard.
+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
+/// just a hash-map behind a lock.
+pub(crate) struct HandleMapShard<T: Send + Sync> {
+ data: RwLock<ShardMapType<T>>,
+}
+
+impl<T: Send + Sync> Default for HandleMapShard<T> {
+ fn default() -> Self {
+ Self { data: RwLock::new(HashMap::new()) }
+ }
+}
+
+impl<T: Send + Sync> HandleMapShard<T> {
+ pub fn get(&self, handle: Handle) -> Result<ObjectReadGuardImpl<T>, HandleNotPresentError> {
+ let map_read_guard = ShardReadWriteLock::<T>::read(&self.data);
+ let read_only_map_ref = map_read_guard.deref();
+ if read_only_map_ref.contains_key(&handle) {
+ let object_read_guard = ShardReadGuard::<T>::map(
+ map_read_guard,
+ ObjectReadGuardMapping { handle, _marker: PhantomData },
+ );
+ Ok(ObjectReadGuardImpl { guard: object_read_guard })
+ } else {
+ // Auto-drop the read guard, and return an error
+ Err(HandleNotPresentError)
+ }
+ }
+ /// Gets a read-write guard on the entire shard map if an entry for the given
+ /// handle exists, but if not, yield [`HandleNotPresentError`].
+ fn get_read_write_guard_if_entry_exists(
+ &self,
+ handle: Handle,
+ ) -> Result<ShardReadWriteGuard<T>, HandleNotPresentError> {
+ let contains_key = {
+ let map_ref = self.data.read();
+ map_ref.contains_key(&handle)
+ };
+ if contains_key {
+ // If we know that the entry exists, and we're currently
+ // holding a read-lock, we know that we're safe to request
+ // an upgrade to a write lock, since only one write or
+ // upgradable read lock can be outstanding at any one time.
+ let write_guard = self.data.write();
+ Ok(write_guard)
+ } else {
+ // Auto-drop the read guard, we don't need to allow a write.
+ Err(HandleNotPresentError)
+ }
+ }
+
+ pub fn get_mut(
+ &self,
+ handle: Handle,
+ ) -> Result<ObjectReadWriteGuardImpl<T>, HandleNotPresentError> {
+ let map_read_write_guard = self.get_read_write_guard_if_entry_exists(handle)?;
+ // Expose only the pointed-to object with a mapped read-write guard
+ let object_read_write_guard = ShardReadWriteGuard::<T>::map(
+ map_read_write_guard,
+ ObjectReadWriteGuardMapping { handle, _marker: PhantomData },
+ );
+ Ok(ObjectReadWriteGuardImpl { guard: object_read_write_guard })
+ }
+
+ pub fn deallocate(
+ &self,
+ handle: Handle,
+ outstanding_allocations_counter: &AtomicU32,
+ ) -> Result<T, HandleNotPresentError> {
+ let mut map_read_write_guard = self.get_read_write_guard_if_entry_exists(handle)?;
+ // We don't need to worry about double-decrements, since the above call
+ // got us an upgradable read guard for our read, which means it's the only
+ // 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.
+ #[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.
+ let _ = outstanding_allocations_counter.fetch_sub(1, Ordering::Release);
+ Ok(removed_object)
+ }
+
+ pub fn try_allocate<E, F>(
+ &self,
+ handle: Handle,
+ object_provider: F,
+ outstanding_allocations_counter: &AtomicU32,
+ max_active_handles: u32,
+ ) -> Result<(), ShardAllocationError<T, E, F>>
+ where
+ F: FnOnce() -> Result<T, E>,
+ {
+ let mut read_write_guard = self.data.write();
+ match read_write_guard.entry(handle) {
+ Occupied(_) => {
+ // We've already allocated for that handle-id, so yield
+ // the object provider back to the caller.
+ Err(ShardAllocationError::EntryOccupied(object_provider))
+ }
+ Vacant(vacant_entry) => {
+ // An entry is open, but we haven't yet checked the allocations count.
+ // Try to increment the total allocations count atomically.
+ // Use acquire ordering on a successful bump, because we don't want
+ // to invoke the allocation closure before we have a guaranteed slot.
+ // On the other hand, upon failure, we don't care about ordering
+ // of surrounding operations, and so we use a relaxed ordering there.
+ let allocation_count_bump_result = outstanding_allocations_counter.fetch_update(
+ Ordering::Acquire,
+ Ordering::Relaxed,
+ |old_total_allocations| {
+ if old_total_allocations >= max_active_handles {
+ None
+ } else {
+ Some(old_total_allocations + 1)
+ }
+ },
+ );
+ match allocation_count_bump_result {
+ 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,
+ // so release all locks and error.
+ Err(ShardAllocationError::ExceedsAllocationLimit)
+ }
+ }
+ }
+ }
+ }
+ /// Gets the actual number of elements stored in this shard.
+ /// Only suitable for single-threaded sections of tests.
+ #[cfg(test)]
+ pub fn len(&self) -> usize {
+ let guard = ShardReadWriteLock::<T>::read(&self.data);
+ guard.deref().len()
+ }
+}
diff --git a/nearby/presence/handle_map/src/tests.rs b/nearby/util/handle_map/src/tests.rs
index d97657f..90e773a 100644
--- a/nearby/presence/handle_map/src/tests.rs
+++ b/nearby/util/handle_map/src/tests.rs
@@ -11,9 +11,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.
+
+#![allow(clippy::unwrap_used, clippy::expect_used)]
+
use crate::*;
-use hashbrown::HashSet;
+use core::ops::{Deref, DerefMut};
+use std::collections::HashSet;
use std::sync::Arc;
use std::thread;
@@ -110,7 +114,7 @@ fn test_overload_allocations_deallocations() {
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);
@@ -122,7 +126,7 @@ fn test_overload_allocations_deallocations() {
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
@@ -218,7 +222,7 @@ fn test_non_overwriting_old_handles() {
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);
@@ -253,8 +257,9 @@ fn test_non_overwriting_old_handles() {
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/connections/ukey2/lock_adapter/Cargo.toml b/nearby/util/lock_adapter/Cargo.toml
index 713f224..6e3174b 100644
--- a/nearby/connections/ukey2/lock_adapter/Cargo.toml
+++ b/nearby/util/lock_adapter/Cargo.toml
@@ -4,10 +4,13 @@ version.workspace = true
edition.workspace = true
publish.workspace = true
+[lints]
+workspace = true
+
[dependencies]
spin = { workspace = true, optional = true }
[features]
-default = ["spin"]
-spin = ["dep:spin"]
+default = ["std"]
std = []
+spin = ["dep:spin"]
diff --git a/nearby/connections/ukey2/lock_adapter/src/lib.rs b/nearby/util/lock_adapter/src/lib.rs
index 098031b..6bf6f0f 100644
--- a/nearby/connections/ukey2/lock_adapter/src/lib.rs
+++ b/nearby/util/lock_adapter/src/lib.rs
@@ -12,6 +12,9 @@
// See the License for the specific language governing permissions and
// limitations under the License.
+//! An abstraction layer for Rust synchronization primitives which provides both no_std and std library
+//! based implementations
+
#![cfg_attr(not(feature = "std"), no_std)]
/// A Spinlock-based implementation of Mutex using the `spin` crate that can be used in `no_std`
@@ -58,3 +61,23 @@ pub trait NoPoisonMutex<T> {
/// Creates a new mutex in an unlocked state ready for use.
fn new(value: T) -> Self;
}
+
+/// A reader-writer lock. This type of lock allows a number of readers or at most one writer at
+/// any point in time.
+pub trait RwLock<T> {
+ /// RAII structure used to release the shared read access of a lock when dropped.
+ type RwLockReadGuard<'a>
+ where
+ Self: 'a;
+
+ /// RAII structure used to release the exclusive write access of a lock when dropped.
+ type RwLockWriteGuard<'a>
+ where
+ Self: 'a;
+
+ /// Locks this RwLock with shared read access, blocking the current thread until it can be acquired.
+ fn read(&self) -> Self::RwLockReadGuard<'_>;
+
+ /// Locks this RwLock with exclusive write access, blocking the current thread until it can be acquired.
+ fn write(&self) -> Self::RwLockWriteGuard<'_>;
+}
diff --git a/nearby/util/lock_adapter/src/spin.rs b/nearby/util/lock_adapter/src/spin.rs
new file mode 100644
index 0000000..e894cef
--- /dev/null
+++ b/nearby/util/lock_adapter/src/spin.rs
@@ -0,0 +1,60 @@
+// 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 crate::NoPoisonMutex;
+
+/// A mutual exclusion primitive useful for protecting shared data
+pub struct Mutex<T>(spin::Mutex<T>);
+
+impl<T> NoPoisonMutex<T> for Mutex<T> {
+ type MutexGuard<'a> = spin::MutexGuard<'a, T> where T: 'a;
+
+ fn lock(&self) -> Self::MutexGuard<'_> {
+ self.0.lock()
+ }
+
+ fn try_lock(&self) -> Option<Self::MutexGuard<'_>> {
+ self.0.try_lock()
+ }
+
+ fn new(value: T) -> Self {
+ Self(spin::Mutex::new(value))
+ }
+}
+
+/// A reader-writer lock
+/// This type of lock allows a number of readers or at most one writer at any point in time.
+/// The write portion of this lock typically allows modification of the underlying data (exclusive access)
+/// and the read portion of this lock typically allows for read-only access (shared access).
+pub struct RwLock<T>(spin::RwLock<T>);
+
+impl<T> RwLock<T> {
+ /// Creates a new instance of an `RwLock<T>` which is unlocked.
+ pub const fn new(inner: T) -> Self {
+ Self(spin::RwLock::new(inner))
+ }
+}
+
+impl<T> crate::RwLock<T> for RwLock<T> {
+ type RwLockReadGuard<'a> = spin::RwLockReadGuard<'a, T> where T: 'a;
+ type RwLockWriteGuard<'a> = spin::RwLockWriteGuard<'a, T> where T: 'a;
+
+ fn read(&self) -> Self::RwLockReadGuard<'_> {
+ self.0.read()
+ }
+
+ fn write(&self) -> Self::RwLockWriteGuard<'_> {
+ self.0.write()
+ }
+}
diff --git a/nearby/util/lock_adapter/src/std.rs b/nearby/util/lock_adapter/src/std.rs
new file mode 100644
index 0000000..c950d85
--- /dev/null
+++ b/nearby/util/lock_adapter/src/std.rs
@@ -0,0 +1,175 @@
+// 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 crate::NoPoisonMutex;
+use std::ops::{Deref, DerefMut};
+
+/// A mutual exclusion primitive useful for protecting shared data
+pub struct Mutex<T>(std::sync::Mutex<T>);
+
+impl<T> NoPoisonMutex<T> for Mutex<T> {
+ type MutexGuard<'a> = std::sync::MutexGuard<'a, T> where T: 'a;
+
+ fn lock(&self) -> Self::MutexGuard<'_> {
+ self.0.lock().unwrap_or_else(|poison| poison.into_inner())
+ }
+
+ fn try_lock(&self) -> Option<Self::MutexGuard<'_>> {
+ match self.0.try_lock() {
+ Ok(guard) => Some(guard),
+ Err(std::sync::TryLockError::Poisoned(guard)) => Some(guard.into_inner()),
+ Err(std::sync::TryLockError::WouldBlock) => None,
+ }
+ }
+
+ fn new(value: T) -> Self {
+ Self(std::sync::Mutex::new(value))
+ }
+}
+
+/// A reader-writer lock
+/// This type of lock allows a number of readers or at most one writer at any point in time.
+/// The write portion of this lock typically allows modification of the underlying data (exclusive access)
+/// and the read portion of this lock typically allows for read-only access (shared access).
+pub struct RwLock<T>(std::sync::RwLock<T>);
+
+impl<T> RwLock<T> {
+ /// Creates a new instance of an `RwLock<T>` which is unlocked.
+ pub const fn new(value: T) -> Self {
+ Self(std::sync::RwLock::new(value))
+ }
+}
+
+impl<T> crate::RwLock<T> for RwLock<T> {
+ type RwLockReadGuard<'a> = RwLockReadGuard<'a, T> where T: 'a;
+
+ type RwLockWriteGuard<'a> = RwLockWriteGuard<'a, T> where T: 'a;
+
+ fn read(&self) -> Self::RwLockReadGuard<'_> {
+ RwLockReadGuard(self.0.read().unwrap_or_else(|e| e.into_inner()))
+ }
+
+ fn write(&self) -> Self::RwLockWriteGuard<'_> {
+ RwLockWriteGuard(self.0.write().unwrap_or_else(|e| e.into_inner()))
+ }
+}
+
+/// RAII structure used to release the shared read access of a lock when dropped.
+pub struct RwLockReadGuard<'a, T>(std::sync::RwLockReadGuard<'a, T>);
+
+impl<'a, T> RwLockReadGuard<'a, T> {
+ /// Make a new MappedRwLockReadGuard for a component of the locked data.
+ pub fn map<U, M>(s: Self, mapping: M) -> MappedRwLockReadGuard<'a, T, U, M>
+ where
+ M: RwMapping<Arg = T, Ret = U>,
+ {
+ MappedRwLockReadGuard { mapping, guard: s }
+ }
+}
+
+impl<'a, T> Deref for RwLockReadGuard<'a, T> {
+ type Target = T;
+
+ fn deref(&self) -> &Self::Target {
+ self.0.deref()
+ }
+}
+
+/// An RAII read lock guard returned by RwLockReadGuard::map, which can point to a subfield of the protected data.
+pub struct MappedRwLockReadGuard<'a, T, U, M>
+where
+ M: RwMapping<Arg = T, Ret = U>,
+{
+ mapping: M,
+ guard: RwLockReadGuard<'a, T>,
+}
+
+impl<'a, T, U, M> Deref for MappedRwLockReadGuard<'a, T, U, M>
+where
+ M: RwMapping<Arg = T, Ret = U>,
+{
+ type Target = U;
+
+ fn deref(&self) -> &Self::Target {
+ self.mapping.map(&*self.guard)
+ }
+}
+
+/// RAII structure used to release the exclusive write access of a lock when dropped.
+pub struct RwLockWriteGuard<'a, T>(std::sync::RwLockWriteGuard<'a, T>);
+
+impl<'a, T> RwLockWriteGuard<'a, T> {
+ /// Make a new MappedRwLockWriteGuard for a component of the locked data.
+ pub fn map<U, M>(s: Self, mapping: M) -> MappedRwLockWriteGuard<'a, T, U, M>
+ where
+ M: RwMapping<Arg = T, Ret = U>,
+ {
+ MappedRwLockWriteGuard { mapping, guard: s }
+ }
+}
+
+impl<'a, T> Deref for RwLockWriteGuard<'a, T> {
+ type Target = T;
+
+ fn deref(&self) -> &Self::Target {
+ self.0.deref()
+ }
+}
+
+impl<'a, T> DerefMut for RwLockWriteGuard<'a, T> {
+ fn deref_mut(&mut self) -> &mut Self::Target {
+ self.0.deref_mut()
+ }
+}
+
+/// An RAII read lock guard returned by RwLockWriteGuard::map, which can point to a subfield of the protected data.
+pub struct MappedRwLockWriteGuard<'a, T, U, M>
+where
+ M: RwMapping<Arg = T, Ret = U>,
+{
+ mapping: M,
+ guard: RwLockWriteGuard<'a, T>,
+}
+
+impl<'a, P, T, M> Deref for MappedRwLockWriteGuard<'a, P, T, M>
+where
+ M: RwMapping<Arg = P, Ret = T>,
+{
+ type Target = T;
+
+ fn deref(&self) -> &Self::Target {
+ self.mapping.map(&*self.guard)
+ }
+}
+
+impl<'a, P, T, M> DerefMut for MappedRwLockWriteGuard<'a, P, T, M>
+where
+ M: RwMapping<Arg = P, Ret = T>,
+{
+ fn deref_mut(&mut self) -> &mut Self::Target {
+ self.mapping.map_mut(&mut *self.guard)
+ }
+}
+
+/// Mapping functions which define how to map from one locked data type to a component of that locked data
+pub trait RwMapping {
+ /// The original locked data type
+ type Arg;
+ /// The returned mapped locked data type which is a component of the original locked data
+ type Ret;
+ /// Maps from Arg into Ret
+ fn map<'a>(&self, arg: &'a Self::Arg) -> &'a Self::Ret;
+ /// Mutably maps from Arg into Ret
+ fn map_mut<'a>(&self, arg: &'a mut Self::Arg) -> &'a mut Self::Ret;
+}
diff --git a/remoteauth/Cargo.lock b/remoteauth/Cargo.lock
index 91b8fa0..55841b9 100644
--- a/remoteauth/Cargo.lock
+++ b/remoteauth/Cargo.lock
@@ -3,149 +3,261 @@
version = 3
[[package]]
+name = "aho-corasick"
+version = "1.0.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "0c378d78423fdad8089616f827526ee33c19f2fddbd5de1629152c9593ba4783"
+dependencies = [
+ "memchr",
+]
+
+[[package]]
+name = "anstream"
+version = "0.5.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "b1f58811cfac344940f1a400b6e6231ce35171f614f26439e80f8c1465c5cc0c"
+dependencies = [
+ "anstyle",
+ "anstyle-parse",
+ "anstyle-query",
+ "anstyle-wincon",
+ "colorchoice",
+ "utf8parse",
+]
+
+[[package]]
+name = "anstyle"
+version = "1.0.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "15c4c2c83f81532e5845a733998b6971faca23490340a418e9b72a3ec9de12ea"
+
+[[package]]
+name = "anstyle-parse"
+version = "0.2.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "938874ff5980b03a87c5524b3ae5b59cf99b1d6bc836848df7bc5ada9643c333"
+dependencies = [
+ "utf8parse",
+]
+
+[[package]]
+name = "anstyle-query"
+version = "1.0.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "5ca11d4be1bab0c8bc8734a9aa7bf4ee8316d462a08c6ac5052f888fef5b494b"
+dependencies = [
+ "windows-sys",
+]
+
+[[package]]
+name = "anstyle-wincon"
+version = "2.1.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "58f54d10c6dfa51283a066ceab3ec1ab78d13fae00aa49243a45e4571fb79dfd"
+dependencies = [
+ "anstyle",
+ "windows-sys",
+]
+
+[[package]]
name = "anyhow"
version = "1.0.72"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3b13c32d80ecc7ab747b80c3784bce54ee8a7a0cc4fbda9bf4cda2cf6fe90854"
[[package]]
-name = "async-trait"
-version = "0.1.72"
+name = "bitflags"
+version = "2.4.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "b4682ae6287fcf752ecaabbfcc7b6f9b72aa33933dc23a554d853aea8eea8635"
+
+[[package]]
+name = "build-scripts"
+version = "0.1.0"
+dependencies = [
+ "anyhow",
+ "clap",
+ "cmd-runner",
+ "env_logger",
+ "log",
+]
+
+[[package]]
+name = "cc"
+version = "1.0.83"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "cc6dde6e4ed435a4c1ee4e73592f5ba9da2151af10076cc04858746af9352d09"
+checksum = "f1174fb0b6ec23863f8b971027804a42614e347eafb0a95bf0b12cdae21fc4d0"
dependencies = [
+ "libc",
+]
+
+[[package]]
+name = "clap"
+version = "4.4.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "7c8d502cbaec4595d2e7d5f61e318f05417bd2b66fdc3809498f0d3fdf0bea27"
+dependencies = [
+ "clap_builder",
+ "clap_derive",
+ "once_cell",
+]
+
+[[package]]
+name = "clap_builder"
+version = "4.4.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "5891c7bc0edb3e1c2204fc5e94009affabeb1821c9e5fdc3959536c5c0bb984d"
+dependencies = [
+ "anstream",
+ "anstyle",
+ "clap_lex",
+ "strsim",
+]
+
+[[package]]
+name = "clap_derive"
+version = "4.4.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "c9fd1a5729c4548118d7d70ff234a44868d00489a4b6597b0b020918a0e91a1a"
+dependencies = [
+ "heck",
"proc-macro2",
"quote",
"syn",
]
[[package]]
-name = "autocfg"
-version = "1.1.0"
+name = "clap_lex"
+version = "0.5.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa"
+checksum = "cd7cc57abe963c6d3b9d8be5b06ba7c8957a930305ca90304f24ef040aa6f961"
[[package]]
-name = "ctap_protocol"
+name = "cmd-runner"
version = "0.1.0"
dependencies = [
"anyhow",
+ "owo-colors",
+ "shell-escape",
]
[[package]]
-name = "futures"
-version = "0.3.28"
+name = "colorchoice"
+version = "1.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "23342abe12aba583913b2e62f22225ff9c950774065e4bfb61a19cd9770fec40"
+checksum = "acbf1af155f9b9ef647e42cdc158db4b64a1b61f743629225fde6f3e0be2a7c7"
+
+[[package]]
+name = "ctap_protocol"
+version = "0.1.0"
dependencies = [
- "futures-channel",
- "futures-core",
- "futures-executor",
- "futures-io",
- "futures-sink",
- "futures-task",
- "futures-util",
+ "anyhow",
]
[[package]]
-name = "futures-channel"
-version = "0.3.28"
+name = "env_logger"
+version = "0.10.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "955518d47e09b25bbebc7a18df10b81f0c766eaf4c4f1cccef2fca5f2a4fb5f2"
+checksum = "85cdab6a89accf66733ad5a1693a4dcced6aeff64602b634530dd73c1f3ee9f0"
dependencies = [
- "futures-core",
- "futures-sink",
+ "humantime",
+ "is-terminal",
+ "log",
+ "regex",
+ "termcolor",
]
[[package]]
-name = "futures-core"
-version = "0.3.28"
+name = "errno"
+version = "0.3.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "4bca583b7e26f571124fe5b7561d49cb2868d79116cfa0eefce955557c6fee8c"
+checksum = "136526188508e25c6fef639d7927dfb3e0e3084488bf202267829cf7fc23dbdd"
+dependencies = [
+ "errno-dragonfly",
+ "libc",
+ "windows-sys",
+]
[[package]]
-name = "futures-executor"
-version = "0.3.28"
+name = "errno-dragonfly"
+version = "0.1.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "ccecee823288125bd88b4d7f565c9e58e41858e47ab72e8ea2d64e93624386e0"
+checksum = "aa68f1b12764fab894d2755d2518754e71b4fd80ecfb822714a1206c2aab39bf"
dependencies = [
- "futures-core",
- "futures-task",
- "futures-util",
+ "cc",
+ "libc",
]
[[package]]
-name = "futures-io"
-version = "0.3.28"
+name = "heck"
+version = "0.4.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "95505c38b4572b2d910cecb0281560f54b440a19336cbbcb27bf6ce6adc6f5a8"
+
+[[package]]
+name = "hermit-abi"
+version = "0.3.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "443144c8cdadd93ebf52ddb4056d257f5b52c04d3c804e657d19eb73fc33668b"
+
+[[package]]
+name = "humantime"
+version = "2.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "4fff74096e71ed47f8e023204cfd0aa1289cd54ae5430a9523be060cdb849964"
+checksum = "9a3a5bfb195931eeb336b2a7b4d761daec841b97f947d34394601737a7bba5e4"
[[package]]
-name = "futures-macro"
-version = "0.3.28"
+name = "is-terminal"
+version = "0.4.9"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "89ca545a94061b6365f2c7355b4b32bd20df3ff95f02da9329b34ccc3bd6ee72"
+checksum = "cb0889898416213fab133e1d33a0e5858a48177452750691bde3666d0fdbaf8b"
dependencies = [
- "proc-macro2",
- "quote",
- "syn",
+ "hermit-abi",
+ "rustix",
+ "windows-sys",
]
[[package]]
-name = "futures-sink"
-version = "0.3.28"
+name = "libc"
+version = "0.2.147"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "f43be4fe21a13b9781a69afa4985b0f6ee0e1afab2c6f454a8cf30e2b2237b6e"
+checksum = "b4668fb0ea861c1df094127ac5f1da3409a82116a4ba74fca2e58ef927159bb3"
[[package]]
-name = "futures-task"
-version = "0.3.28"
+name = "linux-raw-sys"
+version = "0.4.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "76d3d132be6c0e6aa1534069c705a74a5997a356c0dc2f86a47765e5617c5b65"
+checksum = "57bcfdad1b858c2db7c38303a6d2ad4dfaf5eb53dfeb0910128b2c26d6158503"
[[package]]
-name = "futures-util"
-version = "0.3.28"
+name = "log"
+version = "0.4.20"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "26b01e40b772d54cf6c6d721c1d1abd0647a0106a12ecaa1c186273392a69533"
-dependencies = [
- "futures-channel",
- "futures-core",
- "futures-io",
- "futures-macro",
- "futures-sink",
- "futures-task",
- "memchr",
- "pin-project-lite",
- "pin-utils",
- "slab",
-]
+checksum = "b5e6163cb8c49088c2c36f57875e58ccd8c87c7427f7fbd50ea6710b2f3f2e8f"
[[package]]
name = "memchr"
-version = "2.5.0"
+version = "2.6.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "2dffe52ecf27772e601905b7522cb4ef790d2cc203488bbd0e2fe85fcb74566d"
+checksum = "f478948fd84d9f8e86967bf432640e46adfb5a4bd4f14ef7e864ab38220534ae"
[[package]]
-name = "pin-project-lite"
-version = "0.2.12"
+name = "once_cell"
+version = "1.18.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "12cc1b0bf1727a77a54b6654e7b5f1af8604923edc8b81885f8ec92f9e3f0a05"
+checksum = "dd8b5dd2ae5ed71462c540258bedcb51965123ad7e7ccf4b9a8cafaa4a63576d"
[[package]]
-name = "pin-utils"
-version = "0.1.0"
+name = "owo-colors"
+version = "3.5.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184"
+checksum = "c1b04fb49957986fdce4d6ee7a65027d55d4b6d2265e5848bbb507b58ccfdb6f"
[[package]]
name = "platform"
version = "0.1.0"
dependencies = [
"anyhow",
- "async-trait",
- "futures",
]
[[package]]
@@ -167,24 +279,67 @@ dependencies = [
]
[[package]]
-name = "remote_auth_protool"
+name = "regex"
+version = "1.9.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "12de2eff854e5fa4b1295edd650e227e9d8fb0c9e90b12e7f36d6a6811791a29"
+dependencies = [
+ "aho-corasick",
+ "memchr",
+ "regex-automata",
+ "regex-syntax",
+]
+
+[[package]]
+name = "regex-automata"
+version = "0.3.7"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "49530408a136e16e5b486e883fbb6ba058e8e4e8ae6621a77b048b314336e629"
+dependencies = [
+ "aho-corasick",
+ "memchr",
+ "regex-syntax",
+]
+
+[[package]]
+name = "regex-syntax"
+version = "0.7.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "dbb5fb1acd8a1a18b3dd5be62d25485eb770e05afb408a9627d14d451bae12da"
+
+[[package]]
+name = "remote_auth_protocol"
version = "0.1.0"
dependencies = [
"anyhow",
- "async-trait",
- "futures",
]
[[package]]
-name = "slab"
-version = "0.4.8"
+name = "rustix"
+version = "0.38.10"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "6528351c9bc8ab22353f9d776db39a20288e8d6c37ef8cfe3317cf875eecfc2d"
+checksum = "ed6248e1caa625eb708e266e06159f135e8c26f2bb7ceb72dc4b2766d0340964"
dependencies = [
- "autocfg",
+ "bitflags",
+ "errno",
+ "libc",
+ "linux-raw-sys",
+ "windows-sys",
]
[[package]]
+name = "shell-escape"
+version = "0.1.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "45bb67a18fa91266cc7807181f62f9178a6873bfad7dc788c42e6430db40184f"
+
+[[package]]
+name = "strsim"
+version = "0.10.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "73473c0e59e6d5812c5dfe2a064a6444949f089e20eec9a2e5506596494e4623"
+
+[[package]]
name = "syn"
version = "2.0.28"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -196,7 +351,119 @@ dependencies = [
]
[[package]]
+name = "termcolor"
+version = "1.2.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "be55cf8942feac5c765c2c993422806843c9a9a45d4d5c407ad6dd2ea95eb9b6"
+dependencies = [
+ "winapi-util",
+]
+
+[[package]]
name = "unicode-ident"
version = "1.0.11"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "301abaae475aa91687eb82514b328ab47a211a533026cb25fc3e519b86adfc3c"
+
+[[package]]
+name = "utf8parse"
+version = "0.2.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "711b9620af191e0cdc7468a8d14e709c3dcdb115b36f838e601583af800a370a"
+
+[[package]]
+name = "winapi"
+version = "0.3.9"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419"
+dependencies = [
+ "winapi-i686-pc-windows-gnu",
+ "winapi-x86_64-pc-windows-gnu",
+]
+
+[[package]]
+name = "winapi-i686-pc-windows-gnu"
+version = "0.4.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6"
+
+[[package]]
+name = "winapi-util"
+version = "0.1.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "70ec6ce85bb158151cae5e5c87f95a8e97d2c0c4b001223f33a334e3ce5de178"
+dependencies = [
+ "winapi",
+]
+
+[[package]]
+name = "winapi-x86_64-pc-windows-gnu"
+version = "0.4.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f"
+
+[[package]]
+name = "windows-sys"
+version = "0.48.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "677d2418bec65e3338edb076e806bc1ec15693c5d0104683f2efe857f61056a9"
+dependencies = [
+ "windows-targets",
+]
+
+[[package]]
+name = "windows-targets"
+version = "0.48.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "9a2fa6e2155d7247be68c096456083145c183cbbbc2764150dda45a87197940c"
+dependencies = [
+ "windows_aarch64_gnullvm",
+ "windows_aarch64_msvc",
+ "windows_i686_gnu",
+ "windows_i686_msvc",
+ "windows_x86_64_gnu",
+ "windows_x86_64_gnullvm",
+ "windows_x86_64_msvc",
+]
+
+[[package]]
+name = "windows_aarch64_gnullvm"
+version = "0.48.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "2b38e32f0abccf9987a4e3079dfb67dcd799fb61361e53e2882c3cbaf0d905d8"
+
+[[package]]
+name = "windows_aarch64_msvc"
+version = "0.48.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "dc35310971f3b2dbbf3f0690a219f40e2d9afcf64f9ab7cc1be722937c26b4bc"
+
+[[package]]
+name = "windows_i686_gnu"
+version = "0.48.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "a75915e7def60c94dcef72200b9a8e58e5091744960da64ec734a6c6e9b3743e"
+
+[[package]]
+name = "windows_i686_msvc"
+version = "0.48.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "8f55c233f70c4b27f66c523580f78f1004e8b5a8b659e05a4eb49d4166cca406"
+
+[[package]]
+name = "windows_x86_64_gnu"
+version = "0.48.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "53d40abd2583d23e4718fddf1ebec84dbff8381c07cae67ff7768bbf19c6718e"
+
+[[package]]
+name = "windows_x86_64_gnullvm"
+version = "0.48.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "0b7b52767868a23d5bab768e390dc5f5c55825b6d30b86c844ff2dc7414044cc"
+
+[[package]]
+name = "windows_x86_64_msvc"
+version = "0.48.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ed94fce61571a4006852b7389a063ab983c02eb1bb37b47f8272ce92d06d9538"
diff --git a/remoteauth/Cargo.toml b/remoteauth/Cargo.toml
index ca3213b..20b2ec3 100644
--- a/remoteauth/Cargo.toml
+++ b/remoteauth/Cargo.toml
@@ -2,7 +2,7 @@
members = [
"ctap_protocol",
"platform",
- "remote_auth_protool",
+ "remote_auth_protocol",
]
[workspace.package]
@@ -12,5 +12,16 @@ publish = false
[workspace.dependencies]
anyhow = "1.0.72"
-async-trait = "0.1.72"
-futures = "0.3.28"
+
+[package]
+name = "build-scripts"
+version.workspace = true
+edition.workspace = true
+publish.workspace = true
+
+[dependencies]
+anyhow.workspace = true
+clap = { version = "4.0.25", features = ["derive"] }
+cmd-runner = { path = "../cmd-runner" }
+env_logger = "0.10.0"
+log = "0.4.17" \ No newline at end of file
diff --git a/remoteauth/ctap_protocol/src/command.rs b/remoteauth/ctap_protocol/src/command.rs
new file mode 100644
index 0000000..e512755
--- /dev/null
+++ b/remoteauth/ctap_protocol/src/command.rs
@@ -0,0 +1,227 @@
+// 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.
+
+//! # CTAP Protocol
+//!
+//! This crate represents CTAP messages and turns them into a binary representation to be sent to a
+//! remote device.
+
+use anyhow::anyhow;
+
+/// The Rust representation of CTAP values (or identifiers).
+#[derive(Debug, PartialEq)]
+pub enum Value {
+ AuthenticatorMakeCredential = 0x01,
+ AuthenticatorGetAssertion = 0x02,
+ AuthenticatorGetInfo = 0x04,
+ AuthenticatorClientPIN = 0x06,
+ AuthenticatorReset = 0x07,
+ AuthenticatorGetNextAssertion = 0x08,
+}
+
+/// The Rust representation of CTAP parameters.
+#[derive(Debug, PartialEq)]
+pub enum Parameters {
+ AuthenticatorMakeCredential,
+ AuthenticatorGetAssertion,
+ AuthenticatorClientPIN,
+}
+
+impl From<Parameters> for Vec<u8> {
+ fn from(message: Parameters) -> Vec<u8> {
+ // TODO: serialize parameters correctly
+ match message {
+ Parameters::AuthenticatorMakeCredential => vec![],
+ Parameters::AuthenticatorGetAssertion => vec![],
+ Parameters::AuthenticatorClientPIN => vec![],
+ }
+ }
+}
+
+pub struct Command {
+ pub value: Value,
+ pub parameters: Option<Parameters>,
+}
+
+impl From<Command> for Vec<u8> {
+ /// Converts the given CTAP command into its binary representation.
+ fn from(message: Command) -> Vec<u8> {
+ let mut result = vec![message.value as u8];
+ if let Some(p) = message.parameters {
+ result.append(&mut p.into());
+ }
+ result
+ }
+}
+
+impl TryFrom<Vec<u8>> for Command {
+ type Error = anyhow::Error;
+ /// Convert a binary message to its Rust representation.
+ fn try_from(bytes: Vec<u8>) -> anyhow::Result<Self> {
+ if bytes.is_empty() {
+ Err(anyhow!("Binary message was empty."))
+ } else {
+ match bytes[0] {
+ _x if _x == Value::AuthenticatorMakeCredential as u8 => Ok(Self {
+ value: Value::AuthenticatorMakeCredential,
+ parameters: Some(Parameters::AuthenticatorMakeCredential),
+ }),
+ _x if _x == Value::AuthenticatorGetAssertion as u8 => Ok(Self {
+ value: Value::AuthenticatorGetAssertion,
+ parameters: Some(Parameters::AuthenticatorGetAssertion),
+ }),
+ _x if _x == Value::AuthenticatorGetInfo as u8 => Ok(Self {
+ value: Value::AuthenticatorGetInfo,
+ parameters: None,
+ }),
+ _x if _x == Value::AuthenticatorClientPIN as u8 => Ok(Self {
+ value: Value::AuthenticatorClientPIN,
+ parameters: Some(Parameters::AuthenticatorClientPIN),
+ }),
+ _x if _x == Value::AuthenticatorReset as u8 => Ok(Self {
+ value: Value::AuthenticatorReset,
+ parameters: None,
+ }),
+ _x if _x == Value::AuthenticatorGetNextAssertion as u8 => Ok(Self {
+ value: Value::AuthenticatorGetNextAssertion,
+ parameters: None,
+ }),
+ _ => Err(anyhow!("Unknown message type.")),
+ }
+ }
+ }
+}
+
+#[cfg(test)]
+mod tests {
+ use super::*;
+ use anyhow::bail;
+
+ #[test]
+ fn translate_message_to_bytes() {
+ let mut message = Command {
+ value: Value::AuthenticatorReset,
+ parameters: None,
+ };
+ let mut bytes: Vec<u8> = message.into();
+
+ assert_eq!(bytes, vec![Value::AuthenticatorReset as u8]);
+
+ message = Command {
+ value: Value::AuthenticatorMakeCredential,
+ parameters: None,
+ };
+ bytes = message.into();
+
+ assert_eq!(bytes, vec![Value::AuthenticatorMakeCredential as u8]);
+
+ message = Command {
+ value: Value::AuthenticatorGetAssertion,
+ parameters: None,
+ };
+ bytes = message.into();
+
+ assert_eq!(bytes, vec![Value::AuthenticatorGetAssertion as u8]);
+
+ message = Command {
+ value: Value::AuthenticatorGetInfo,
+ parameters: None,
+ };
+ bytes = message.into();
+
+ assert_eq!(bytes, vec![Value::AuthenticatorGetInfo as u8]);
+
+ message = Command {
+ value: Value::AuthenticatorClientPIN,
+ parameters: None,
+ };
+ bytes = message.into();
+
+ assert_eq!(bytes, vec![Value::AuthenticatorClientPIN as u8]);
+
+ message = Command {
+ value: Value::AuthenticatorGetNextAssertion,
+ parameters: None,
+ };
+ bytes = message.into();
+
+ assert_eq!(bytes, vec![Value::AuthenticatorGetNextAssertion as u8]);
+ }
+
+ #[test]
+ fn translate_bytes_to_message() -> anyhow::Result<()> {
+ let mut bytes = vec![Value::AuthenticatorReset as u8];
+ let mut message: Command = bytes.try_into()?;
+ assert_eq!(message.value, Value::AuthenticatorReset);
+ assert_eq!(message.parameters, None);
+
+ bytes = vec![Value::AuthenticatorMakeCredential as u8];
+ message = bytes.try_into()?;
+ assert_eq!(message.value, Value::AuthenticatorMakeCredential);
+ assert_eq!(
+ message.parameters,
+ Some(Parameters::AuthenticatorMakeCredential)
+ );
+
+ bytes = vec![Value::AuthenticatorGetAssertion as u8];
+ message = bytes.try_into()?;
+ assert_eq!(message.value, Value::AuthenticatorGetAssertion);
+ assert_eq!(
+ message.parameters,
+ Some(Parameters::AuthenticatorGetAssertion)
+ );
+
+ bytes = vec![Value::AuthenticatorGetInfo as u8];
+ message = bytes.try_into()?;
+ assert_eq!(message.value, Value::AuthenticatorGetInfo);
+ assert_eq!(message.parameters, None);
+
+ bytes = vec![Value::AuthenticatorClientPIN as u8];
+ message = bytes.try_into()?;
+ assert_eq!(message.value, Value::AuthenticatorClientPIN);
+ assert_eq!(message.parameters, Some(Parameters::AuthenticatorClientPIN));
+
+ bytes = vec![Value::AuthenticatorGetNextAssertion as u8];
+ message = bytes.try_into()?;
+ assert_eq!(message.value, Value::AuthenticatorGetNextAssertion);
+ assert_eq!(message.parameters, None);
+ Ok(())
+ }
+
+ #[test]
+ fn translate_empty_bytes() -> anyhow::Result<()> {
+ let bytes = vec![];
+ let res: anyhow::Result<Command> = bytes.try_into();
+ match res {
+ Err(e) => {
+ assert_eq!(e.to_string(), "Binary message was empty.");
+ Ok(())
+ }
+ _ => bail!("Should not parse empty bytes"),
+ }
+ }
+
+ #[test]
+ fn translate_unknown_message() -> anyhow::Result<()> {
+ let bytes = vec![0x10];
+ let res: anyhow::Result<Command> = bytes.try_into();
+ match res {
+ Err(e) => {
+ assert_eq!(e.to_string(), "Unknown message type.");
+ Ok(())
+ }
+ _ => bail!("Should not parse unknown message."),
+ }
+ }
+}
diff --git a/remoteauth/ctap_protocol/src/lib.rs b/remoteauth/ctap_protocol/src/lib.rs
index 9b7eef2..b42193e 100644
--- a/remoteauth/ctap_protocol/src/lib.rs
+++ b/remoteauth/ctap_protocol/src/lib.rs
@@ -17,74 +17,4 @@
//! This crate represents CTAP messages and turns them into a binary representation to be sent to a
//! remote device.
-use anyhow::anyhow;
-
-/// The Rust representation of CTAP messages.
-#[derive(Debug, PartialEq)]
-pub enum CtapMessage {
- AuthenticatorReset,
-}
-
-impl CtapMessage {
- /// Converts the given CTAP message into its binary representation.
- pub fn to_bytes(&self) -> Vec<u8> {
- match self {
- CtapMessage::AuthenticatorReset => vec![0x07],
- }
- }
-
- /// Convert a binary message to its Rust representation.
- pub fn from_bytes(bytes: Vec<u8>) -> anyhow::Result<CtapMessage> {
- if bytes.len() == 0 {
- Err(anyhow!("Binary message was empty."))
- } else {
- match bytes[0] {
- 0x07 => Ok(CtapMessage::AuthenticatorReset),
- _ => Err(anyhow!("Unknown message type.")),
- }
- }
- }
-}
-
-#[cfg(test)]
-mod tests {
- use super::*;
-
- #[test]
- fn translate_message_to_bytes() {
- let message = CtapMessage::AuthenticatorReset;
- let bytes = message.to_bytes();
-
- assert_eq!(bytes, vec![0x07]);
- }
-
- #[test]
- fn translate_bytes_to_message() {
- let bytes = vec![0x07];
- let message = CtapMessage::from_bytes(bytes);
-
- assert_eq!(message.is_ok(), true);
- assert_eq!(message.unwrap(), CtapMessage::AuthenticatorReset);
- }
-
- #[test]
- fn translate_empty_bytes() {
- let bytes = vec![];
- let message = CtapMessage::from_bytes(bytes);
-
- assert_eq!(message.is_err(), true);
- assert_eq!(
- message.unwrap_err().to_string(),
- "Binary message was empty."
- );
- }
-
- #[test]
- fn translate_unknown_message() {
- let bytes = vec![0x01];
- let message = CtapMessage::from_bytes(bytes);
-
- assert_eq!(message.is_err(), true);
- assert_eq!(message.unwrap_err().to_string(), "Unknown message type.");
- }
-}
+pub mod command;
diff --git a/remoteauth/deny.toml b/remoteauth/deny.toml
new file mode 100644
index 0000000..2bf5920
--- /dev/null
+++ b/remoteauth/deny.toml
@@ -0,0 +1,219 @@
+# This template contains all of the possible sections and their default values
+
+# Note that all fields that take a lint level have these possible values:
+# * deny - An error will be produced and the check will fail
+# * warn - A warning will be produced, but the check will not fail
+# * allow - No warning or error will be produced, though in some cases a note
+# will be
+
+# The values provided in this template are the default values that will be used
+# when any section or field is not specified in your own configuration
+
+# If 1 or more target triples (and optionally, target_features) are specified,
+# only the specified targets will be checked when running `cargo deny check`.
+# This means, if a particular package is only ever used as a target specific
+# dependency, such as, for example, the `nix` crate only being used via the
+# `target_family = "unix"` configuration, that only having windows targets in
+# this list would mean the nix crate, as well as any of its exclusive
+# dependencies not shared by any other crates, would be ignored, as the target
+# list here is effectively saying which targets you are building for.
+targets = [
+ # The triple can be any string, but only the target triples built in to
+ # rustc (as of 1.40) can be checked against actual config expressions
+ #{ triple = "x86_64-unknown-linux-musl" },
+ # You can also specify which target_features you promise are enabled for a
+ # particular target. target_features are currently not validated against
+ # the actual valid features supported by the target architecture.
+ #{ triple = "wasm32-unknown-unknown", features = ["atomics"] },
+]
+
+# This section is considered when running `cargo deny check advisories`
+# More documentation for the advisories section can be found here:
+# https://embarkstudios.github.io/cargo-deny/checks/advisories/cfg.html
+[advisories]
+# The path where the advisory database is cloned/fetched into
+db-path = "~/.cargo/advisory-db"
+# The url(s) of the advisory databases to use
+db-urls = ["https://github.com/rustsec/advisory-db"]
+# The lint level for security vulnerabilities
+vulnerability = "deny"
+# The lint level for unmaintained crates
+unmaintained = "warn"
+# The lint level for crates that have been yanked from their source registry
+yanked = "warn"
+# The lint level for crates with security notices. Note that as of
+# 2019-12-17 there are no security notice advisories in
+# https://github.com/rustsec/advisory-db
+notice = "warn"
+# A list of advisory IDs to ignore. Note that ignored advisories will still
+# output a note when they are encountered.
+ignore = [
+ # criterion 0.4.0 depends on a version of atty w/unaligned reads
+]
+# Threshold for security vulnerabilities, any vulnerability with a CVSS score
+# lower than the range specified will be ignored. Note that ignored advisories
+# will still output a note when they are encountered.
+# * None - CVSS Score 0.0
+# * Low - CVSS Score 0.1 - 3.9
+# * Medium - CVSS Score 4.0 - 6.9
+# * High - CVSS Score 7.0 - 8.9
+# * Critical - CVSS Score 9.0 - 10.0
+#severity-threshold =
+
+# If this is true, then cargo deny will use the git executable to fetch advisory database.
+# If this is false, then it uses a built-in git library.
+# Setting this to true can be helpful if you have special authentication requirements that cargo-deny does not support.
+# See Git Authentication for more information about setting up git authentication.
+#git-fetch-with-cli = true
+
+# This section is considered when running `cargo deny check licenses`
+# More documentation for the licenses section can be found here:
+# https://embarkstudios.github.io/cargo-deny/checks/licenses/cfg.html
+[licenses]
+# The lint level for crates which do not have a detectable license
+unlicensed = "deny"
+unused-allowed-license = "allow"
+# List of explicitly allowed licenses
+# See https://spdx.org/licenses/ for list of possible licenses
+# [possible values: any SPDX 3.11 short identifier (+ optional exception)].
+allow = [
+ "MIT",
+ "Apache-2.0",
+ "Apache-2.0 WITH LLVM-exception",
+ "BSD-3-Clause",
+ "BSD-2-Clause",
+ "ISC",
+ "Unicode-DFS-2016",
+ "OpenSSL",
+ "Unlicense"
+]
+# List of explicitly disallowed licenses
+# See https://spdx.org/licenses/ for list of possible licenses
+# [possible values: any SPDX 3.11 short identifier (+ optional exception)].
+deny = [
+ #"Nokia",
+]
+# Lint level for licenses considered copyleft
+copyleft = "warn"
+# Blanket approval or denial for OSI-approved or FSF Free/Libre licenses
+# * both - The license will be approved if it is both OSI-approved *AND* FSF
+# * either - The license will be approved if it is either OSI-approved *OR* FSF
+# * osi-only - The license will be approved if is OSI-approved *AND NOT* FSF
+# * fsf-only - The license will be approved if is FSF *AND NOT* OSI-approved
+# * neither - This predicate is ignored and the default lint level is used
+allow-osi-fsf-free = "neither"
+# Lint level used when no other predicates are matched
+# 1. License isn't in the allow or deny lists
+# 2. License isn't copyleft
+# 3. License isn't OSI/FSF, or allow-osi-fsf-free = "neither"
+default = "deny"
+# The confidence threshold for detecting a license from license text.
+# The higher the value, the more closely the license text must be to the
+# canonical license text of a valid SPDX license file.
+# [possible values: any between 0.0 and 1.0].
+confidence-threshold = 0.8
+# Allow 1 or more licenses on a per-crate basis, so that particular licenses
+# aren't accepted for every possible crate as with the normal allow list
+exceptions = [
+ # Each entry is the crate and version constraint, and its specific allow
+ # list
+ #{ allow = ["Zlib"], name = "adler32", version = "*" },
+]
+
+# Some crates don't have (easily) machine readable licensing information,
+# adding a clarification entry for it allows you to manually specify the
+# licensing information
+#[[licenses.clarify]]
+# The name of the crate the clarification applies to
+#name = "ring"
+# The optional version constraint for the crate
+#version = "*"
+# The SPDX expression for the license requirements of the crate
+#expression = "MIT AND ISC AND OpenSSL"
+# One or more files in the crate's source used as the "source of truth" for
+# the license expression. If the contents match, the clarification will be used
+# when running the license check, otherwise the clarification will be ignored
+# and the crate will be checked normally, which may produce warnings or errors
+# depending on the rest of your configuration
+#license-files = [
+ # Each entry is a crate relative path, and the (opaque) hash of its contents
+ #{ path = "LICENSE", hash = 0xbd0eed23 }
+#]
+
+[[licenses.clarify]]
+name = "ring"
+version = "*"
+expression = "MIT AND ISC AND OpenSSL"
+license-files = [
+ # Each entry is a crate relative path, and the (opaque) hash of its contents
+ { path = "LICENSE", hash = 0xbd0eed23 }
+]
+
+[licenses.private]
+# If true, ignores workspace crates that aren't published, or are only
+# published to private registries.
+# To see how to mark a crate as unpublished (to the official registry),
+# visit https://doc.rust-lang.org/cargo/reference/manifest.html#the-publish-field.
+ignore = true
+# One or more private registries that you might publish crates to, if a crate
+# is only published to private registries, and ignore is true, the crate will
+# not have its license(s) checked
+registries = [
+ #"https://sekretz.com/registry
+]
+
+# This section is considered when running `cargo deny check bans`.
+# More documentation about the 'bans' section can be found here:
+# https://embarkstudios.github.io/cargo-deny/checks/bans/cfg.html
+[bans]
+# Lint level for when multiple versions of the same crate are detected
+multiple-versions = "allow"
+# Lint level for when a crate version requirement is `*`
+wildcards = "allow"
+# The graph highlighting used when creating dotgraphs for crates
+# with multiple versions
+# * lowest-version - The path to the lowest versioned duplicate is highlighted
+# * simplest-path - The path to the version with the fewest edges is highlighted
+# * all - Both lowest-version and simplest-path are used
+highlight = "all"
+# List of crates that are allowed. Use with care!
+allow = [
+ #{ name = "ansi_term", version = "=0.11.0" },
+]
+# List of crates to deny
+deny = [
+ # Each entry the name of a crate and a version range. If version is
+ # not specified, all versions will be matched.
+ #{ name = "ansi_term", version = "=0.11.0" },
+ #
+ # Wrapper crates can optionally be specified to allow the crate when it
+ # is a direct dependency of the otherwise banned crate
+ #{ name = "ansi_term", version = "=0.11.0", wrappers = [] },
+]
+# Certain crates/versions that will be skipped when doing duplicate detection.
+skip = [
+ #{ name = "ansi_term", version = "=0.11.0" },
+]
+# Similarly to `skip` allows you to skip certain crates during duplicate
+# detection. Unlike skip, it also includes the entire tree of transitive
+# dependencies starting at the specified crate, up to a certain depth, which is
+# by default infinite
+skip-tree = [
+ #{ name = "ansi_term", version = "=0.11.0", depth = 20 },
+]
+
+# This section is considered when running `cargo deny check sources`.
+# More documentation about the 'sources' section can be found here:
+# https://embarkstudios.github.io/cargo-deny/checks/sources/cfg.html
+[sources]
+# Lint level for what to happen when a crate from a crate registry that is not
+# in the allow list is encountered
+unknown-registry = "warn"
+# Lint level for what to happen when a crate from a git repository that is not
+# in the allow list is encountered
+unknown-git = "warn"
+# List of URLs for allowed crate registries. Defaults to the crates.io index
+# if not specified. If it is specified but empty, no registries are allowed.
+allow-registry = ["https://github.com/rust-lang/crates.io-index"]
+# List of URLs for allowed Git repositories
+allow-git = [] \ No newline at end of file
diff --git a/remoteauth/platform/Cargo.toml b/remoteauth/platform/Cargo.toml
index 730f196..0136c27 100644
--- a/remoteauth/platform/Cargo.toml
+++ b/remoteauth/platform/Cargo.toml
@@ -8,5 +8,3 @@ publish.workspace = true
[dependencies]
anyhow.workspace = true
-async-trait.workspace = true
-futures.workspace = true
diff --git a/remoteauth/platform/src/lib.rs b/remoteauth/platform/src/lib.rs
index 286ca58..798c756 100644
--- a/remoteauth/platform/src/lib.rs
+++ b/remoteauth/platform/src/lib.rs
@@ -12,14 +12,18 @@
// See the License for the specific language governing permissions and
// limitations under the License.
-//! # RemoteAuthPlatform
-//!
-//! This trait represents the capabilities that RemoteAuth requires from the platform.
+pub mod listeners;
-use async_trait::async_trait;
+use crate::listeners::SendRequestListener;
-#[async_trait]
+/// # RemoteAuth Platform
+/// This trait represents the capabilities that RemoteAuth requires from the platform.
pub trait Platform {
/// Send a binary message to the remote with the given connection id and return the response.
- async fn send_request(&self, connection_id: i32, request: &[u8]) -> anyhow::Result<Vec<u8>>;
+ fn send_request(
+ &self,
+ connection_id: i32,
+ request: &[u8],
+ listener: Box<dyn SendRequestListener + Send>,
+ );
}
diff --git a/nearby/connections/ukey2/lock_adapter/src/spin.rs b/remoteauth/platform/src/listeners.rs
index be363d4..e354fea 100644
--- a/nearby/connections/ukey2/lock_adapter/src/spin.rs
+++ b/remoteauth/platform/src/listeners.rs
@@ -12,22 +12,8 @@
// See the License for the specific language governing permissions and
// limitations under the License.
-use crate::NoPoisonMutex;
-
-pub struct Mutex<T>(spin::Mutex<T>);
-
-impl<T> NoPoisonMutex<T> for Mutex<T> {
- type MutexGuard<'a> = spin::MutexGuard<'a, T> where T: 'a;
-
- fn lock(&self) -> Self::MutexGuard<'_> {
- self.0.lock()
- }
-
- fn try_lock(&self) -> Option<Self::MutexGuard<'_>> {
- self.0.try_lock()
- }
-
- fn new(value: T) -> Self {
- Self(spin::Mutex::new(value))
- }
+/// SendRequestListener handles the result or an error from calling send_request in the Platform.
+pub trait SendRequestListener {
+ fn on_response(&mut self, response: Vec<u8>);
+ fn on_error(&mut self, error_code: i32);
}
diff --git a/nearby/crypto/bssl-crypto/Cargo.toml b/remoteauth/remote_auth_protocol/Cargo.toml
index bfe3964..e9fd6d2 100644
--- a/nearby/crypto/bssl-crypto/Cargo.toml
+++ b/remoteauth/remote_auth_protocol/Cargo.toml
@@ -1,5 +1,5 @@
[package]
-name = "bssl-crypto"
+name = "remote_auth_protocol"
version.workspace = true
edition.workspace = true
publish.workspace = true
@@ -7,3 +7,4 @@ publish.workspace = true
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[dependencies]
+anyhow.workspace = true
diff --git a/remoteauth/remote_auth_protool/src/lib.rs b/remoteauth/remote_auth_protocol/src/lib.rs
index f019785..bc8315b 100644
--- a/remoteauth/remote_auth_protool/src/lib.rs
+++ b/remoteauth/remote_auth_protocol/src/lib.rs
@@ -16,7 +16,7 @@ pub mod remote_auth_service;
/// Struct representing the remote device.
pub struct RemoteDevice {
- id: i32,
+ pub id: i32,
}
/// Trait to be implemented by anything that wants to be notified when remote devices are discovered
diff --git a/remoteauth/remote_auth_protool/src/remote_auth_service.rs b/remoteauth/remote_auth_protocol/src/remote_auth_service.rs
index 785976d..5da33c7 100644
--- a/remoteauth/remote_auth_protool/src/remote_auth_service.rs
+++ b/remoteauth/remote_auth_protocol/src/remote_auth_service.rs
@@ -47,13 +47,13 @@ impl<'a, T: DeviceDiscoveryListener + PartialEq> DiscoveryPublisher<'a, T>
fn device_discovered(&mut self, remote_device: &RemoteDevice) {
for listener in self.listeners.iter_mut() {
- listener.on_discovered(&remote_device);
+ listener.on_discovered(remote_device);
}
}
fn device_lost(&mut self, remote_device: &RemoteDevice) {
for listener in self.listeners.iter_mut() {
- listener.on_lost(&remote_device);
+ listener.on_lost(remote_device);
}
}
@@ -64,6 +64,12 @@ impl<'a, T: DeviceDiscoveryListener + PartialEq> DiscoveryPublisher<'a, T>
}
}
+impl<'a, T: DeviceDiscoveryListener + PartialEq> Default for RemoteAuthService<'a, T> {
+ fn default() -> Self {
+ Self::new()
+ }
+}
+
#[cfg(test)]
mod tests {
use super::*;
@@ -198,12 +204,11 @@ mod tests {
fn single_listener_timeout_notification() {
let mut listener = TestListener::new();
let mut ras = RemoteAuthService::new();
- let remote_device = RemoteDevice { id: 42 };
ras.add_listener(&mut listener);
ras.timed_out();
- assert_eq!(listener.got_timeout, true);
+ assert!(listener.got_timeout);
}
#[test]
@@ -211,14 +216,13 @@ mod tests {
let mut listener1 = TestListener::new();
let mut listener2 = TestListener::new();
let mut ras = RemoteAuthService::new();
- let remote_device = RemoteDevice { id: 42 };
ras.add_listener(&mut listener1);
ras.add_listener(&mut listener2);
ras.timed_out();
- assert_eq!(listener1.got_timeout, true);
- assert_eq!(listener2.got_timeout, true);
+ assert!(listener1.got_timeout);
+ assert!(listener2.got_timeout);
}
#[test]
diff --git a/remoteauth/remote_auth_protool/Cargo.toml b/remoteauth/remote_auth_protool/Cargo.toml
deleted file mode 100644
index f6c03c3..0000000
--- a/remoteauth/remote_auth_protool/Cargo.toml
+++ /dev/null
@@ -1,12 +0,0 @@
-[package]
-name = "remote_auth_protool"
-version.workspace = true
-edition.workspace = true
-publish.workspace = true
-
-# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
-
-[dependencies]
-anyhow.workspace = true
-async-trait.workspace = true
-futures.workspace = true
diff --git a/remoteauth/src/ctap_protocol.rs b/remoteauth/src/ctap_protocol.rs
new file mode 100644
index 0000000..7e35e6c
--- /dev/null
+++ b/remoteauth/src/ctap_protocol.rs
@@ -0,0 +1,29 @@
+// 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 cmd_runner::run_cmd_shell;
+use std::path;
+
+pub(crate) fn check_ctap_protocol(root: &path::Path) -> anyhow::Result<()> {
+ log::info!("Checking CTAP Protocol");
+ let mut ffi_dir = root.to_path_buf();
+ ffi_dir.push("ctap_protocol");
+
+ run_cmd_shell(&ffi_dir, "cargo build --quiet --release --lib")?;
+ run_cmd_shell(&ffi_dir, "cargo test --quiet -- --color=always")?;
+ run_cmd_shell(&ffi_dir, "cargo doc --quiet --no-deps")?;
+ run_cmd_shell(&ffi_dir, "cargo deny check")?;
+
+ Ok(())
+}
diff --git a/remoteauth/src/main.rs b/remoteauth/src/main.rs
new file mode 100644
index 0000000..e0851eb
--- /dev/null
+++ b/remoteauth/src/main.rs
@@ -0,0 +1,103 @@
+// 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.
+
+extern crate core;
+
+use clap::Parser as _;
+use cmd_runner::run_cmd_shell;
+use env_logger::Env;
+use std::{env, path};
+
+mod ctap_protocol;
+mod platform;
+mod remote_auth_protocol;
+
+fn main() -> anyhow::Result<()> {
+ env_logger::Builder::from_env(Env::default().default_filter_or("info")).init();
+ let cli: Cli = Cli::parse();
+
+ let root_dir: path::PathBuf = env::var("CARGO_MANIFEST_DIR")
+ .expect("Must be run via Cargo to establish root directory")
+ .into();
+
+ match cli.subcommand {
+ Subcommand::CheckEverything(ref options) => check_everything(&root_dir, options)?,
+ Subcommand::CheckWorkspace(ref options) => check_workspace(&root_dir, options)?,
+ Subcommand::CheckCtapProtocol => ctap_protocol::check_ctap_protocol(&root_dir)?,
+ Subcommand::CheckPlatform => platform::check_platform(&root_dir)?,
+ Subcommand::CheckRemoteAuthProtocol => {
+ remote_auth_protocol::check_remote_auth_protocol(&root_dir)?
+ }
+ }
+
+ Ok(())
+}
+
+pub fn check_everything(root: &path::Path, check_options: &CheckOptions) -> anyhow::Result<()> {
+ check_workspace(root, check_options)?;
+ ctap_protocol::check_ctap_protocol(root)?;
+ platform::check_platform(root)?;
+ remote_auth_protocol::check_remote_auth_protocol(root)?;
+ Ok(())
+}
+
+pub fn check_workspace(root: &path::Path, options: &CheckOptions) -> anyhow::Result<()> {
+ log::info!("Running cargo checks on workspace");
+
+ let fmt_command = if options.reformat {
+ "cargo fmt"
+ } else {
+ "cargo fmt --check"
+ };
+
+ for cargo_cmd in [
+ fmt_command,
+ "cargo check --workspace --all-targets --quiet",
+ "cargo test --workspace --quiet -- --color=always",
+ "cargo doc --quiet --no-deps",
+ "cargo deny --workspace check",
+ "cargo clippy --all-targets --workspace -- --deny warnings",
+ ] {
+ run_cmd_shell(root, cargo_cmd)?;
+ }
+
+ Ok(())
+}
+
+#[derive(clap::Parser)]
+struct Cli {
+ #[clap(subcommand)]
+ subcommand: Subcommand,
+}
+
+#[derive(clap::Subcommand, Debug, Clone)]
+#[allow(clippy::enum_variant_names)]
+enum Subcommand {
+ /// Checks everything in remoteauth
+ CheckEverything(CheckOptions),
+ /// Checks everything included in the top level workspace
+ CheckWorkspace(CheckOptions),
+ /// Build and run tests for the CTAP Protocol
+ CheckCtapProtocol,
+ /// Builds and run tests for the Platform
+ CheckPlatform,
+ /// Builds and run tests for the Remote Auth Protocol
+ CheckRemoteAuthProtocol,
+}
+
+#[derive(clap::Args, Debug, Clone, Default)]
+pub struct CheckOptions {
+ #[arg(long, help = "reformat files with cargo fmt")]
+ reformat: bool,
+}
diff --git a/remoteauth/src/platform.rs b/remoteauth/src/platform.rs
new file mode 100644
index 0000000..481b4f7
--- /dev/null
+++ b/remoteauth/src/platform.rs
@@ -0,0 +1,29 @@
+// 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 cmd_runner::run_cmd_shell;
+use std::path;
+
+pub(crate) fn check_platform(root: &path::Path) -> anyhow::Result<()> {
+ log::info!("Checking Platform");
+ let mut ffi_dir = root.to_path_buf();
+ ffi_dir.push("platform");
+
+ run_cmd_shell(&ffi_dir, "cargo build --quiet --release --lib")?;
+ run_cmd_shell(&ffi_dir, "cargo test --quiet -- --color=always")?;
+ run_cmd_shell(&ffi_dir, "cargo doc --quiet --no-deps")?;
+ run_cmd_shell(&ffi_dir, "cargo deny check")?;
+
+ Ok(())
+}
diff --git a/remoteauth/src/remote_auth_protocol.rs b/remoteauth/src/remote_auth_protocol.rs
new file mode 100644
index 0000000..e47517d
--- /dev/null
+++ b/remoteauth/src/remote_auth_protocol.rs
@@ -0,0 +1,29 @@
+// 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 cmd_runner::run_cmd_shell;
+use std::path;
+
+pub(crate) fn check_remote_auth_protocol(root: &path::Path) -> anyhow::Result<()> {
+ log::info!("Checking Remote Auth Protocol");
+ let mut ffi_dir = root.to_path_buf();
+ ffi_dir.push("remote_auth_protocol");
+
+ run_cmd_shell(&ffi_dir, "cargo build --quiet --release --lib")?;
+ run_cmd_shell(&ffi_dir, "cargo test --quiet -- --color=always")?;
+ run_cmd_shell(&ffi_dir, "cargo doc --quiet --no-deps")?;
+ run_cmd_shell(&ffi_dir, "cargo deny check")?;
+
+ Ok(())
+}