Project import generated by Copybara.
GitOrigin-RevId: ae57b289f93d58e7c0c8a0e15db62a9659c85ac8
Change-Id: I5bb44e2cf1472491ae55d154c52f870a45348e0f
diff --git a/BUILD b/BUILD
index db6ef52..5456139 100644
--- a/BUILD
+++ b/BUILD
@@ -114,7 +114,10 @@
rust_library(
name = "handle_map",
srcs = glob(include = ["common/handle_map/src/**/*.rs"]),
- deps = [":lock_adapter"],
+ deps = [
+ ":lock_adapter",
+ "@crate_index//:lazy_static",
+ ],
)
rust_library(
@@ -141,6 +144,7 @@
"@crate_index//:nom",
"@crate_index//:strum",
"@crate_index//:tinyvec",
+ "@crate_index//:itertools"
],
)
diff --git a/bazel_placeholder/Cargo.lock b/bazel_placeholder/Cargo.lock
index bed8428..7f40048 100644
--- a/bazel_placeholder/Cargo.lock
+++ b/bazel_placeholder/Cargo.lock
@@ -219,6 +219,12 @@
]
[[package]]
+name = "either"
+version = "1.13.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "60b1af1c220855b6ceac025d3f6ecdd2b7c4894bfe9cd9bda4fbb4bc7c0d4cf0"
+
+[[package]]
name = "elliptic-curve"
version = "0.13.8"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -331,6 +337,15 @@
]
[[package]]
+name = "itertools"
+version = "0.13.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "413ee7dfc52ee1a4949ceeb7dbc8a33f2d6c088194d9f922fb8318faf1f01186"
+dependencies = [
+ "either",
+]
+
+[[package]]
name = "lazy_static"
version = "1.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -397,6 +412,7 @@
"ed25519-dalek",
"hkdf",
"hmac",
+ "itertools",
"lazy_static",
"nom",
"p256",
diff --git a/bazel_placeholder/Cargo.toml b/bazel_placeholder/Cargo.toml
index e7ee82a..3344be6 100644
--- a/bazel_placeholder/Cargo.toml
+++ b/bazel_placeholder/Cargo.toml
@@ -29,4 +29,5 @@
x25519-dalek = { version = "2.0.0", default-features = false }
subtle = { version = "2.5.0", default-features = false }
sec1 = "0.7.3"
-sha2 = { version = "0.10.8", default-features = false }
\ No newline at end of file
+sha2 = { version = "0.10.8", default-features = false }
+itertools = "0.13.0"
\ No newline at end of file
diff --git a/common/Cargo.lock b/common/Cargo.lock
index 496db0d..1b19b5a 100644
--- a/common/Cargo.lock
+++ b/common/Cargo.lock
@@ -3,6 +3,21 @@
version = 3
[[package]]
+name = "addr2line"
+version = "0.22.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "6e4503c46a5c0c7844e948c9a4d6acd9f50cccb4de1c48eb9e291ea17470c678"
+dependencies = [
+ "gimli",
+]
+
+[[package]]
+name = "adler"
+version = "1.0.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "f26201604c87b1e01bd3d98f8d5d9a8fcbb815e8cedb41ffccbeb4bf593a35fe"
+
+[[package]]
name = "aho-corasick"
version = "1.1.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -34,9 +49,9 @@
[[package]]
name = "anstream"
-version = "0.6.14"
+version = "0.6.15"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "418c75fa768af9c03be99d17643f93f79bbba589895012a80e3452a19ddda15b"
+checksum = "64e15c1ab1f89faffbf04a634d5e1962e9074f2741eef6d97f3c4e322426d526"
dependencies = [
"anstyle",
"anstyle-parse",
@@ -49,33 +64,33 @@
[[package]]
name = "anstyle"
-version = "1.0.7"
+version = "1.0.8"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "038dfcf04a5feb68e9c60b21c9625a54c2c0616e79b72b0fd87075a056ae1d1b"
+checksum = "1bec1de6f59aedf83baf9ff929c98f2ad654b97c9510f4e70cf6f661d49fd5b1"
[[package]]
name = "anstyle-parse"
-version = "0.2.4"
+version = "0.2.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "c03a11a9034d92058ceb6ee011ce58af4a9bf61491aa7e1e59ecd24bd40d22d4"
+checksum = "eb47de1e80c2b463c735db5b217a0ddc39d612e7ac9e2e96a5aed1f57616c1cb"
dependencies = [
"utf8parse",
]
[[package]]
name = "anstyle-query"
-version = "1.0.3"
+version = "1.1.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "a64c907d4e79225ac72e2a354c9ce84d50ebb4586dee56c82b3ee73004f537f5"
+checksum = "6d36fc52c7f6c869915e99412912f22093507da8d9e942ceaf66fe4b7c14422a"
dependencies = [
"windows-sys 0.52.0",
]
[[package]]
name = "anstyle-wincon"
-version = "3.0.3"
+version = "3.0.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "61a38449feb7068f52bb06c12759005cf459ee52bb4adc1d5a7c4322d716fb19"
+checksum = "5bf74e1b6e971609db8ca7a9ce79fd5768ab6ae46441c572e46cf596f59e57f8"
dependencies = [
"anstyle",
"windows-sys 0.52.0",
@@ -83,15 +98,26 @@
[[package]]
name = "anyhow"
-version = "1.0.83"
+version = "1.0.86"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "25bdb32cbbdce2b519a9cd7df3a678443100e265d5e25ca763b7572a5104f5f3"
+checksum = "b3d1d046238990b9cf5bcde22a3fb3584ee5cf65fb2765f454ed428c7a0063da"
[[package]]
-name = "arbitrary"
-version = "1.3.2"
+name = "async-trait"
+version = "0.1.81"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "7d5a26814d8dcb93b0e5a0ff3c6d80a8843bafb21b39e8e18a6f05471870e110"
+checksum = "6e0c28dcc82d7c8ead5cb13beb15405b57b8546e93215673ff8ca0349a028107"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "syn",
+]
+
+[[package]]
+name = "atomic-waker"
+version = "1.1.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "1505bd5d3d116872e7271a6d4e16d81d0c8570876c8de68093a09ac269d8aac0"
[[package]]
name = "autocfg"
@@ -100,31 +126,92 @@
checksum = "0c4b4d0bd25bd0b74681c0ad21497610ce1b7c91b1022cd21c80c6fbdd9476b0"
[[package]]
-name = "bit-set"
-version = "0.5.3"
+name = "axum"
+version = "0.6.20"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "0700ddab506f33b20a03b13996eccd309a48e5ff77d0d95926aa0210fb4e95f1"
+checksum = "3b829e4e32b91e643de6eafe82b1d90675f5874230191a4ffbc1b336dec4d6bf"
dependencies = [
- "bit-vec",
+ "async-trait",
+ "axum-core",
+ "bitflags 1.3.2",
+ "bytes",
+ "futures-util",
+ "http 0.2.12",
+ "http-body 0.4.6",
+ "hyper 0.14.30",
+ "itoa",
+ "matchit",
+ "memchr",
+ "mime",
+ "percent-encoding",
+ "pin-project-lite",
+ "rustversion",
+ "serde",
+ "serde_json",
+ "serde_path_to_error",
+ "serde_urlencoded",
+ "sync_wrapper 0.1.2",
+ "tokio",
+ "tower",
+ "tower-layer",
+ "tower-service",
]
[[package]]
-name = "bit-vec"
-version = "0.6.3"
+name = "axum-core"
+version = "0.3.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "349f9b6a179ed607305526ca489b34ad0a41aed5f7980fa90eb03160b69598fb"
+checksum = "759fa577a247914fd3f7f76d62972792636412fbfd634cd452f6a385a74d2d2c"
+dependencies = [
+ "async-trait",
+ "bytes",
+ "futures-util",
+ "http 0.2.12",
+ "http-body 0.4.6",
+ "mime",
+ "rustversion",
+ "tower-layer",
+ "tower-service",
+]
+
+[[package]]
+name = "backtrace"
+version = "0.3.73"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "5cc23269a4f8976d0a4d2e7109211a419fe30e8d88d677cd60b6bc79c5732e0a"
+dependencies = [
+ "addr2line",
+ "cc",
+ "cfg-if",
+ "libc",
+ "miniz_oxide",
+ "object",
+ "rustc-demangle",
+]
+
+[[package]]
+name = "base64"
+version = "0.22.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "72b3254f16251a8381aa12e40e3c4d2f0199f8c6508fbecb9d91f575e0fbb8c6"
[[package]]
name = "bitflags"
-version = "2.5.0"
+version = "1.3.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "cf4b9d6a944f767f8e5e0db018570623c85f3d925ac718db4e06d0187adb21c1"
+checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a"
+
+[[package]]
+name = "bitflags"
+version = "2.6.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "b048fb63fd8b5923fc5aa7b340d8e156aec7ec02f0c78fa8a6ddc2613f6f71de"
[[package]]
name = "bstr"
-version = "1.9.1"
+version = "1.10.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "05efc5cfd9110c8416e471df0e96702d58690178e206e61b7173706673c93706"
+checksum = "40723b8fb387abc38f4f4a37c09073622e41dd12327033091ef8950659e6dc0c"
dependencies = [
"memchr",
"serde",
@@ -152,9 +239,9 @@
[[package]]
name = "bytes"
-version = "1.6.0"
+version = "1.6.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "514de17de45fdb8dc022b1a7975556c53c86f9f0aa5f534b98977b171857c2c9"
+checksum = "a12916984aab3fa6e39d655a33e09c0071eb36d6ab3aea5c2d78551f1df6d952"
[[package]]
name = "cast"
@@ -164,14 +251,9 @@
[[package]]
name = "cc"
-version = "1.0.97"
+version = "1.1.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "099a5357d84c4c61eb35fc8eafa9a79a902c2f76911e5747ced4e032edd8d9b4"
-dependencies = [
- "jobserver",
- "libc",
- "once_cell",
-]
+checksum = "2aba8f4e9906c7ce3c73463f62a7f0c65183ada1a2d47e397cc8810827f9694f"
[[package]]
name = "cesu8"
@@ -196,7 +278,7 @@
"js-sys",
"num-traits",
"wasm-bindgen",
- "windows-targets 0.52.5",
+ "windows-targets 0.52.6",
]
[[package]]
@@ -228,9 +310,9 @@
[[package]]
name = "clap"
-version = "4.5.4"
+version = "4.5.13"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "90bc066a67923782aa8515dbaea16946c5bcc5addbd668bb80af688e53e548a0"
+checksum = "0fbb260a053428790f3de475e304ff84cdbc4face759ea7a3e64c1edd938a7fc"
dependencies = [
"clap_builder",
"clap_derive",
@@ -238,9 +320,9 @@
[[package]]
name = "clap_builder"
-version = "4.5.2"
+version = "4.5.13"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "ae129e2e766ae0ec03484e609954119f123cc1fe650337e155d03b022f24f7b4"
+checksum = "64b17d7ea74e9f833c7dbf2cbe4fb12ff26783eda4782a8975b72f895c9b4d99"
dependencies = [
"anstream",
"anstyle",
@@ -250,9 +332,9 @@
[[package]]
name = "clap_derive"
-version = "4.5.4"
+version = "4.5.13"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "528131438037fd55894f62d6e9f068b8f45ac57ffa77517819645d10aed04f64"
+checksum = "501d359d5f3dcaf6ecdeee48833ae73ec6e42723a1e52419c79abf9507eec0a0"
dependencies = [
"heck",
"proc-macro2",
@@ -262,9 +344,9 @@
[[package]]
name = "clap_lex"
-version = "0.7.0"
+version = "0.7.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "98cc8fbded0c607b7ba9dd60cd98df59af97e84d24e49c8557331cfc26d301ce"
+checksum = "1462739cb27611015575c0c11df5df7601141071f07518d56fcc1be504cbec97"
[[package]]
name = "cmd_runner"
@@ -277,15 +359,17 @@
"globset",
"log",
"owo-colors",
+ "serde",
+ "serde_json",
"shell-escape",
"xshell",
]
[[package]]
name = "colorchoice"
-version = "1.0.1"
+version = "1.0.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "0b6a852b24ab71dffc585bcb46eaf7959d175cb865a7152e35b348d1b2960422"
+checksum = "d3fd119d74b830634cea2a0f58bbd0d54540518a14397557951e79340abc28c0"
[[package]]
name = "combine"
@@ -298,6 +382,16 @@
]
[[package]]
+name = "core-foundation"
+version = "0.9.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "91e195e091a93c46f7102ec7818a2aa394e1e1771c3ab4825963fa03e45afb8f"
+dependencies = [
+ "core-foundation-sys",
+ "libc",
+]
+
+[[package]]
name = "core-foundation-sys"
version = "0.8.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -354,9 +448,9 @@
[[package]]
name = "crossbeam-channel"
-version = "0.5.12"
+version = "0.5.13"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "ab3db02a9c5b5121e1e42fbdb1aeb65f5e02624cc58c43f2884c6ccac0b82f95"
+checksum = "33480d6946193aa8033910124896ca395333cae7e2d1113d1fef6c3272217df2"
dependencies = [
"crossbeam-utils",
]
@@ -391,9 +485,9 @@
[[package]]
name = "crossbeam-utils"
-version = "0.8.19"
+version = "0.8.20"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "248e3bacc7dc6baa3b21e405ee045c3047101a49145e7e9eca583ab4c2ca5345"
+checksum = "22ec99545bb0ed0ea7bb9b8e1e9122ea386ff8a48c0922e43f36d45ab09e0e80"
[[package]]
name = "crunchy"
@@ -402,65 +496,52 @@
checksum = "7a81dae078cea95a014a339291cec439d2f232ebe854a9d672b796c6afafa9b7"
[[package]]
-name = "derive_fuzz_example"
-version = "0.1.0"
-dependencies = [
- "arbitrary",
- "derive_fuzztest",
- "libfuzzer-sys",
- "quickcheck",
-]
-
-[[package]]
-name = "derive_fuzztest"
-version = "0.1.0"
-dependencies = [
- "arbitrary",
- "derive_fuzztest_macro",
- "proptest",
- "proptest-arbitrary-interop",
- "quickcheck",
-]
-
-[[package]]
-name = "derive_fuzztest_macro"
-version = "0.1.0"
-dependencies = [
- "derive_fuzztest",
- "pretty_assertions",
- "prettyplease",
- "proc-macro2",
- "quote",
- "syn",
-]
-
-[[package]]
-name = "diff"
-version = "0.1.13"
+name = "directories"
+version = "5.0.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "56254986775e3233ffa9c4d7d3faaf6d36a2c09d30b20687e9f88bc8bafc16c8"
+checksum = "9a49173b84e034382284f27f1af4dcbbd231ffa358c0fe316541a7337f376a35"
+dependencies = [
+ "dirs-sys",
+]
+
+[[package]]
+name = "dirs-sys"
+version = "0.4.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "520f05a5cbd335fae5a99ff7a6ab8627577660ee5cfd6a94a6a929b52ff0321c"
+dependencies = [
+ "libc",
+ "option-ext",
+ "redox_users",
+ "windows-sys 0.48.0",
+]
[[package]]
name = "either"
-version = "1.11.0"
+version = "1.13.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "a47c1c47d2f5964e29c61246e81db715514cd532db6b5116a25ea3c03d6780a2"
+checksum = "60b1af1c220855b6ceac025d3f6ecdd2b7c4894bfe9cd9bda4fbb4bc7c0d4cf0"
[[package]]
-name = "env_logger"
-version = "0.8.4"
+name = "encoding_rs"
+version = "0.8.34"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "a19187fea3ac7e84da7dacf48de0c45d63c6a76f9490dae389aead16c243fce3"
+checksum = "b45de904aa0b010bce2ab45264d0631681847fa7b6f2eaa7dab7619943bc4f59"
dependencies = [
- "log",
- "regex",
+ "cfg-if",
]
[[package]]
-name = "errno"
-version = "0.3.8"
+name = "equivalent"
+version = "1.0.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "a258e46cdc063eb8519c00b9fc845fc47bcfca4130e2f08e88665ceda8474245"
+checksum = "5443807d6dff69373d433ab9ef5378ad8df50ca6298caf15de6e52e24aaf54d5"
+
+[[package]]
+name = "errno"
+version = "0.3.9"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "534c5cf6194dfab3db3242765c03bbe257cf92f22b38f6bc0c58d59108a820ba"
dependencies = [
"libc",
"windows-sys 0.52.0",
@@ -492,6 +573,80 @@
checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1"
[[package]]
+name = "foreign-types"
+version = "0.3.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "f6f339eb8adc052cd2ca78910fda869aefa38d22d5cb648e6485e4d3fc06f3b1"
+dependencies = [
+ "foreign-types-shared",
+]
+
+[[package]]
+name = "foreign-types-shared"
+version = "0.1.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "00b0228411908ca8685dba7fc2cdd70ec9990a6e753e89b6ac91a84c40fbaf4b"
+
+[[package]]
+name = "form_urlencoded"
+version = "1.2.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "e13624c2627564efccf4934284bdd98cbaa14e79b0b5a141218e507b3a823456"
+dependencies = [
+ "percent-encoding",
+]
+
+[[package]]
+name = "futures-channel"
+version = "0.3.30"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "eac8f7d7865dcb88bd4373ab671c8cf4508703796caa2b1985a9ca867b3fcb78"
+dependencies = [
+ "futures-core",
+ "futures-sink",
+]
+
+[[package]]
+name = "futures-core"
+version = "0.3.30"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "dfc6580bb841c5a68e9ef15c77ccc837b40a7504914d52e47b8b0e9bbda25a1d"
+
+[[package]]
+name = "futures-io"
+version = "0.3.30"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "a44623e20b9681a318efdd71c299b6b222ed6f231972bfe2f224ebad6311f0c1"
+
+[[package]]
+name = "futures-sink"
+version = "0.3.30"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "9fb8e00e87438d937621c1c6269e53f536c14d3fbd6a042bb24879e57d474fb5"
+
+[[package]]
+name = "futures-task"
+version = "0.3.30"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "38d84fa142264698cdce1a9f9172cf383a0c82de1bddcf3092901442c4097004"
+
+[[package]]
+name = "futures-util"
+version = "0.3.30"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "3d6401deb83407ab3da39eba7e33987a73c3df0c82b4bb5813ee871c19c41d48"
+dependencies = [
+ "futures-core",
+ "futures-io",
+ "futures-sink",
+ "futures-task",
+ "memchr",
+ "pin-project-lite",
+ "pin-utils",
+ "slab",
+]
+
+[[package]]
name = "getrandom"
version = "0.2.15"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -503,6 +658,12 @@
]
[[package]]
+name = "gimli"
+version = "0.29.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "40ecd4077b5ae9fd2e9e169b102c6c330d0605168eb0e8bf79952b256dbefffd"
+
+[[package]]
name = "glob"
version = "0.3.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -522,6 +683,25 @@
]
[[package]]
+name = "h2"
+version = "0.4.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "fa82e28a107a8cc405f0839610bdc9b15f1e25ec7d696aa5cf173edbcb1486ab"
+dependencies = [
+ "atomic-waker",
+ "bytes",
+ "fnv",
+ "futures-core",
+ "futures-sink",
+ "http 1.1.0",
+ "indexmap",
+ "slab",
+ "tokio",
+ "tokio-util",
+ "tracing",
+]
+
+[[package]]
name = "half"
version = "2.4.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -541,6 +721,12 @@
]
[[package]]
+name = "hashbrown"
+version = "0.14.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "e5274423e17b7c9fc20b6e7e208532f9b19825d82dfd615708b70edd83df41f1"
+
+[[package]]
name = "heck"
version = "0.5.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -553,6 +739,176 @@
checksum = "d231dfb89cfffdbc30e7fc41579ed6066ad03abda9e567ccafae602b97ec5024"
[[package]]
+name = "hex"
+version = "0.4.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "7f24254aa9a54b5c858eaee2f5bccdb46aaf0e486a595ed5fd8f86ba55232a70"
+
+[[package]]
+name = "http"
+version = "0.2.12"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "601cbb57e577e2f5ef5be8e7b83f0f63994f25aa94d673e54a92d5c516d101f1"
+dependencies = [
+ "bytes",
+ "fnv",
+ "itoa",
+]
+
+[[package]]
+name = "http"
+version = "1.1.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "21b9ddb458710bc376481b842f5da65cdf31522de232c1ca8146abce2a358258"
+dependencies = [
+ "bytes",
+ "fnv",
+ "itoa",
+]
+
+[[package]]
+name = "http-body"
+version = "0.4.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "7ceab25649e9960c0311ea418d17bee82c0dcec1bd053b5f9a66e265a693bed2"
+dependencies = [
+ "bytes",
+ "http 0.2.12",
+ "pin-project-lite",
+]
+
+[[package]]
+name = "http-body"
+version = "1.0.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "1efedce1fb8e6913f23e0c92de8e62cd5b772a67e7b3946df930a62566c93184"
+dependencies = [
+ "bytes",
+ "http 1.1.0",
+]
+
+[[package]]
+name = "http-body-util"
+version = "0.1.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "793429d76616a256bcb62c2a2ec2bed781c8307e797e2598c50010f2bee2544f"
+dependencies = [
+ "bytes",
+ "futures-util",
+ "http 1.1.0",
+ "http-body 1.0.1",
+ "pin-project-lite",
+]
+
+[[package]]
+name = "httparse"
+version = "1.9.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "0fcc0b4a115bf80b728eb8ea024ad5bd707b615bfed49e0665b6e0f86fd082d9"
+
+[[package]]
+name = "httpdate"
+version = "1.0.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "df3b46402a9d5adb4c86a0cf463f42e19994e3ee891101b1841f30a545cb49a9"
+
+[[package]]
+name = "hyper"
+version = "0.14.30"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "a152ddd61dfaec7273fe8419ab357f33aee0d914c5f4efbf0d96fa749eea5ec9"
+dependencies = [
+ "bytes",
+ "futures-channel",
+ "futures-core",
+ "futures-util",
+ "http 0.2.12",
+ "http-body 0.4.6",
+ "httparse",
+ "httpdate",
+ "itoa",
+ "pin-project-lite",
+ "socket2",
+ "tokio",
+ "tower-service",
+ "tracing",
+ "want",
+]
+
+[[package]]
+name = "hyper"
+version = "1.4.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "50dfd22e0e76d0f662d429a5f80fcaf3855009297eab6a0a9f8543834744ba05"
+dependencies = [
+ "bytes",
+ "futures-channel",
+ "futures-util",
+ "h2",
+ "http 1.1.0",
+ "http-body 1.0.1",
+ "httparse",
+ "itoa",
+ "pin-project-lite",
+ "smallvec",
+ "tokio",
+ "want",
+]
+
+[[package]]
+name = "hyper-rustls"
+version = "0.27.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "5ee4be2c948921a1a5320b629c4193916ed787a7f7f293fd3f7f5a6c9de74155"
+dependencies = [
+ "futures-util",
+ "http 1.1.0",
+ "hyper 1.4.1",
+ "hyper-util",
+ "rustls",
+ "rustls-pki-types",
+ "tokio",
+ "tokio-rustls",
+ "tower-service",
+]
+
+[[package]]
+name = "hyper-tls"
+version = "0.6.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "70206fc6890eaca9fde8a0bf71caa2ddfc9fe045ac9e5c70df101a7dbde866e0"
+dependencies = [
+ "bytes",
+ "http-body-util",
+ "hyper 1.4.1",
+ "hyper-util",
+ "native-tls",
+ "tokio",
+ "tokio-native-tls",
+ "tower-service",
+]
+
+[[package]]
+name = "hyper-util"
+version = "0.1.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "3ab92f4f49ee4fb4f997c784b7a2e0fa70050211e0b6a287f898c3c9785ca956"
+dependencies = [
+ "bytes",
+ "futures-channel",
+ "futures-util",
+ "http 1.1.0",
+ "http-body 1.0.1",
+ "hyper 1.4.1",
+ "pin-project-lite",
+ "socket2",
+ "tokio",
+ "tower",
+ "tower-service",
+ "tracing",
+]
+
+[[package]]
name = "iana-time-zone"
version = "0.1.60"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -576,6 +932,32 @@
]
[[package]]
+name = "idna"
+version = "0.5.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "634d9b1461af396cad843f47fdba5597a4f9e6ddd4bfb6ff5d85028c25cb12f6"
+dependencies = [
+ "unicode-bidi",
+ "unicode-normalization",
+]
+
+[[package]]
+name = "indexmap"
+version = "2.2.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "168fb715dda47215e360912c096649d23d58bf392ac62f73919e831745e40f26"
+dependencies = [
+ "equivalent",
+ "hashbrown",
+]
+
+[[package]]
+name = "ipnet"
+version = "2.9.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "8f518f335dce6725a761382244631d86cf0ccb2863413590b31338feb467f9c3"
+
+[[package]]
name = "is-terminal"
version = "0.4.12"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -588,9 +970,9 @@
[[package]]
name = "is_terminal_polyfill"
-version = "1.70.0"
+version = "1.70.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "f8478577c03552c21db0e2724ffb8986a5ce7af88107e6be5d2ee6e158c12800"
+checksum = "7943c866cc5cd64cbc25b2e01621d07fa8eb2a1a23160ee81ce38704e97b8ecf"
[[package]]
name = "itertools"
@@ -609,9 +991,9 @@
[[package]]
name = "java-locator"
-version = "0.1.5"
+version = "0.1.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "90003f2fd9c52f212c21d8520f1128da0080bad6fff16b68fe6e7f2f0c3780c2"
+checksum = "d2abecabd9961c5e01405a6426687fcf1bd94a269927137e4c3cc1a7419b93fd"
dependencies = [
"glob",
"lazy_static",
@@ -642,15 +1024,6 @@
checksum = "8eaf4bc02d17cbdd7ff4c7438cafcdf7fb9a4613313ad11b4f8fefe7d3fa0130"
[[package]]
-name = "jobserver"
-version = "0.1.31"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "d2b099aaa34a9751c5bf0878add70444e1ed2dd73f347be99003d4577277de6e"
-dependencies = [
- "libc",
-]
-
-[[package]]
name = "js-sys"
version = "0.3.69"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -661,29 +1034,18 @@
[[package]]
name = "lazy_static"
-version = "1.4.0"
+version = "1.5.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646"
+checksum = "bbd2bcb4c963f2ddae06a2efc7e9f3591312473c50c6685e1f298068316e66fe"
dependencies = [
- "spin 0.5.2",
+ "spin",
]
[[package]]
name = "libc"
-version = "0.2.154"
+version = "0.2.155"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "ae743338b92ff9146ce83992f766a31066a91a8c84a45e0e9f21e7cf6de6d346"
-
-[[package]]
-name = "libfuzzer-sys"
-version = "0.4.7"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "a96cfd5557eb82f2b83fed4955246c988d331975a002961b07c81584d107e7f7"
-dependencies = [
- "arbitrary",
- "cc",
- "once_cell",
-]
+checksum = "97b3888a4aecf77e811145cadf6eef5901f4782c53886191b2f693f24761847c"
[[package]]
name = "libloading"
@@ -696,16 +1058,20 @@
]
[[package]]
-name = "libm"
-version = "0.2.8"
+name = "libredox"
+version = "0.1.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "4ec2a862134d2a7d32d7983ddcdd1c4923530833c9f2ea1a44fc5fa473989058"
+checksum = "c0ff37bd590ca25063e35af745c343cb7a0271906fb7b37e4813e8f79f00268d"
+dependencies = [
+ "bitflags 2.6.0",
+ "libc",
+]
[[package]]
name = "license"
-version = "3.3.1"
+version = "3.4.0+3.24.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "3bba2f02ee1d13cd4bea565658939cd851d70e391f34f7c27b45b2077df3a2e4"
+checksum = "a7da1e0d845faf299a9fe5f201a918a0dc0d5fc22c7b9580a6a23fed3a912b37"
dependencies = [
"reword",
"serde",
@@ -714,15 +1080,15 @@
[[package]]
name = "linux-raw-sys"
-version = "0.4.13"
+version = "0.4.14"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "01cda141df6706de531b6c46c3a33ecca755538219bd484262fa09410c13539c"
+checksum = "78b3ae25bc7c8c38cec158d1f2757ee79e9b3740fbc7ccf0e59e4b08d793fa89"
[[package]]
name = "lock_adapter"
version = "0.1.0"
dependencies = [
- "spin 0.9.8",
+ "spin",
]
[[package]]
@@ -737,15 +1103,27 @@
[[package]]
name = "log"
-version = "0.4.21"
+version = "0.4.22"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "90ed8c1e510134f979dbc4f070f87d4313098b704861a105fe34231c70a3901c"
+checksum = "a7a70ba024b9dc04c27ea2f0c0548feb474ec5c54bba33a7f72f873a39d07b24"
+
+[[package]]
+name = "matchit"
+version = "0.7.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "0e7465ac9959cc2b1404e8e2367b43684a6d13790fe23056cc8c6c5a6b7bcb94"
[[package]]
name = "memchr"
-version = "2.7.2"
+version = "2.7.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "6c8640c5d730cb13ebd907d8d04b52f55ac9a2eec55b440c8892f40d56c76c1d"
+checksum = "78ca9ab1a0babb1e7d5695e3530886289c18cf2f87ec19a575a0abdce112e3a3"
+
+[[package]]
+name = "mime"
+version = "0.3.17"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "6877bb514081ee2a7ff5ef9de3281f14a4dd4bceac4c09388074a6b5df8a139a"
[[package]]
name = "minimal-lexical"
@@ -754,6 +1132,44 @@
checksum = "68354c5c6bd36d73ff3feceb05efa59b6acb7626617f4962be322a825e61f79a"
[[package]]
+name = "miniz_oxide"
+version = "0.7.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "b8a240ddb74feaf34a79a7add65a741f3167852fba007066dcac1ca548d89c08"
+dependencies = [
+ "adler",
+]
+
+[[package]]
+name = "mio"
+version = "1.0.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "4569e456d394deccd22ce1c1913e6ea0e54519f577285001215d33557431afe4"
+dependencies = [
+ "hermit-abi",
+ "libc",
+ "wasi",
+ "windows-sys 0.52.0",
+]
+
+[[package]]
+name = "native-tls"
+version = "0.2.12"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "a8614eb2c83d59d1c8cc974dd3f920198647674a0a035e1af1fa58707e317466"
+dependencies = [
+ "libc",
+ "log",
+ "openssl",
+ "openssl-probe",
+ "openssl-sys",
+ "schannel",
+ "security-framework",
+ "security-framework-sys",
+ "tempfile",
+]
+
+[[package]]
name = "nom"
version = "7.1.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -770,7 +1186,32 @@
checksum = "071dfc062690e90b734c0b2273ce72ad0ffa95f0c74596bc250dcfd960262841"
dependencies = [
"autocfg",
- "libm",
+]
+
+[[package]]
+name = "oauth"
+version = "0.1.0"
+dependencies = [
+ "anyhow",
+ "axum",
+ "directories",
+ "hex",
+ "log",
+ "open",
+ "rand",
+ "reqwest",
+ "serde",
+ "tokio",
+ "url",
+]
+
+[[package]]
+name = "object"
+version = "0.36.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "3f203fa8daa7bb185f760ae12bd8e097f63d17041dcdcaf675ac54cdf863170e"
+dependencies = [
+ "memchr",
]
[[package]]
@@ -781,9 +1222,69 @@
[[package]]
name = "oorandom"
-version = "11.1.3"
+version = "11.1.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "0ab1bc2a289d34bd04a330323ac98a1b4bc82c9d9fcb1e66b63caa84da26b575"
+checksum = "b410bbe7e14ab526a0e86877eb47c6996a2bd7746f027ba551028c925390e4e9"
+
+[[package]]
+name = "open"
+version = "3.2.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "2078c0039e6a54a0c42c28faa984e115fb4c2d5bf2208f77d1961002df8576f8"
+dependencies = [
+ "pathdiff",
+ "windows-sys 0.42.0",
+]
+
+[[package]]
+name = "openssl"
+version = "0.10.66"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "9529f4786b70a3e8c61e11179af17ab6188ad8d0ded78c5529441ed39d4bd9c1"
+dependencies = [
+ "bitflags 2.6.0",
+ "cfg-if",
+ "foreign-types",
+ "libc",
+ "once_cell",
+ "openssl-macros",
+ "openssl-sys",
+]
+
+[[package]]
+name = "openssl-macros"
+version = "0.1.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "a948666b637a0f465e8564c73e89d4dde00d72d4d473cc972f390fc3dcee7d9c"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "syn",
+]
+
+[[package]]
+name = "openssl-probe"
+version = "0.1.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ff011a302c396a5197692431fc1948019154afc178baf7d8e37367442a4601cf"
+
+[[package]]
+name = "openssl-sys"
+version = "0.9.103"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "7f9e8deee91df40a943c71b917e5874b951d32a802526c85721ce3b776c929d6"
+dependencies = [
+ "cc",
+ "libc",
+ "pkg-config",
+ "vcpkg",
+]
+
+[[package]]
+name = "option-ext"
+version = "0.2.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "04744f49eae99ab78e0d5c0b603ab218f515ea8cfe5a456d7629ad883a3b6e7d"
[[package]]
name = "owo-colors"
@@ -792,10 +1293,83 @@
checksum = "c1b04fb49957986fdce4d6ee7a65027d55d4b6d2265e5848bbb507b58ccfdb6f"
[[package]]
-name = "plotters"
-version = "0.3.5"
+name = "parking_lot"
+version = "0.12.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "d2c224ba00d7cadd4d5c660deaf2098e5e80e07846537c51f9cfa4be50c1fd45"
+checksum = "f1bf18183cf54e8d6059647fc3063646a1801cf30896933ec2311622cc4b9a27"
+dependencies = [
+ "lock_api",
+ "parking_lot_core",
+]
+
+[[package]]
+name = "parking_lot_core"
+version = "0.9.10"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "1e401f977ab385c9e4e3ab30627d6f26d00e2c73eef317493c4ec6d468726cf8"
+dependencies = [
+ "cfg-if",
+ "libc",
+ "redox_syscall",
+ "smallvec",
+ "windows-targets 0.52.6",
+]
+
+[[package]]
+name = "pathdiff"
+version = "0.2.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "8835116a5c179084a830efb3adc117ab007512b535bc1a21c991d3b32a6b44dd"
+
+[[package]]
+name = "percent-encoding"
+version = "2.3.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "e3148f5046208a5d56bcfc03053e3ca6334e51da8dfb19b6cdc8b306fae3283e"
+
+[[package]]
+name = "pin-project"
+version = "1.1.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "b6bf43b791c5b9e34c3d182969b4abb522f9343702850a2e57f460d00d09b4b3"
+dependencies = [
+ "pin-project-internal",
+]
+
+[[package]]
+name = "pin-project-internal"
+version = "1.1.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "2f38a4412a78282e09a2cf38d195ea5420d15ba0602cb375210efbc877243965"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "syn",
+]
+
+[[package]]
+name = "pin-project-lite"
+version = "0.2.14"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "bda66fc9667c18cb2758a2ac84d1167245054bcf85d5d1aaa6923f45801bdd02"
+
+[[package]]
+name = "pin-utils"
+version = "0.1.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184"
+
+[[package]]
+name = "pkg-config"
+version = "0.3.30"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d231b230927b5e4ad203db57bbcbee2802f6bce620b1e4a9024a07d94e2907ec"
+
+[[package]]
+name = "plotters"
+version = "0.3.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "a15b6eccb8484002195a3e44fe65a4ce8e93a625797a063735536fd59cb01cf3"
dependencies = [
"num-traits",
"plotters-backend",
@@ -806,15 +1380,15 @@
[[package]]
name = "plotters-backend"
-version = "0.3.5"
+version = "0.3.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "9e76628b4d3a7581389a35d5b6e2139607ad7c75b17aed325f210aa91f4a9609"
+checksum = "414cec62c6634ae900ea1c56128dfe87cf63e7caece0852ec76aba307cebadb7"
[[package]]
name = "plotters-svg"
-version = "0.3.5"
+version = "0.3.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "38f6d39893cca0701371e3c27294f09797214b86f1fb951b89ade8ec04e2abab"
+checksum = "81b30686a7d9c3e010b84284bdd26a29f2138574f52f5eb6f794fc0ad924e705"
dependencies = [
"plotters-backend",
]
@@ -846,81 +1420,15 @@
checksum = "5b40af805b3121feab8a3c29f04d8ad262fa8e0561883e7653e024ae4479e6de"
[[package]]
-name = "pretty_assertions"
-version = "1.4.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "af7cee1a6c8a5b9208b3cb1061f10c0cb689087b3d8ce85fb9d2dd7a29b6ba66"
-dependencies = [
- "diff",
- "yansi",
-]
-
-[[package]]
-name = "prettyplease"
-version = "0.2.20"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "5f12335488a2f3b0a83b14edad48dca9879ce89b2edd10e80237e4e852dd645e"
-dependencies = [
- "proc-macro2",
- "syn",
-]
-
-[[package]]
name = "proc-macro2"
-version = "1.0.82"
+version = "1.0.86"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "8ad3d49ab951a01fbaafe34f2ec74122942fe18a3f9814c3268f1bb72042131b"
+checksum = "5e719e8df665df0d1c8fbfd238015744736151d4445ec0836b8e628aae103b77"
dependencies = [
"unicode-ident",
]
[[package]]
-name = "proptest"
-version = "1.4.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "31b476131c3c86cb68032fdc5cb6d5a1045e3e42d96b69fa599fd77701e1f5bf"
-dependencies = [
- "bit-set",
- "bit-vec",
- "bitflags",
- "lazy_static",
- "num-traits",
- "rand",
- "rand_chacha",
- "rand_xorshift",
- "regex-syntax",
- "rusty-fork",
- "tempfile",
- "unarray",
-]
-
-[[package]]
-name = "proptest-arbitrary-interop"
-version = "0.1.0"
-source = "git+https://github.com/brson/proptest-arbitrary-interop.git?branch=incorrect-format#9ae407e9805feb109b3d49cc737166bda7e698c3"
-dependencies = [
- "arbitrary",
- "proptest",
-]
-
-[[package]]
-name = "quick-error"
-version = "1.2.3"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "a1d01941d82fa2ab50be1e79e6714289dd7cde78eba4c074bc5a4374f650dfe0"
-
-[[package]]
-name = "quickcheck"
-version = "1.0.3"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "588f6378e4dd99458b60ec275b4477add41ce4fa9f64dcba6f15adccb19b50d6"
-dependencies = [
- "env_logger",
- "log",
- "rand",
-]
-
-[[package]]
name = "quote"
version = "1.0.36"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -960,15 +1468,6 @@
]
[[package]]
-name = "rand_xorshift"
-version = "0.3.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "d25bf25ec5ae4a3f1b92f929810509a2f53d7dca2f50b794ff57e3face536c8f"
-dependencies = [
- "rand_core",
-]
-
-[[package]]
name = "rayon"
version = "1.10.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -989,10 +1488,30 @@
]
[[package]]
-name = "regex"
-version = "1.10.4"
+name = "redox_syscall"
+version = "0.5.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "c117dbdfde9c8308975b6a18d71f3f385c89461f7b3fb054288ecf2a2058ba4c"
+checksum = "2a908a6e00f1fdd0dfd9c0eb08ce85126f6d8bbda50017e74bc4a4b7d4a926a4"
+dependencies = [
+ "bitflags 2.6.0",
+]
+
+[[package]]
+name = "redox_users"
+version = "0.4.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "bd283d9651eeda4b2a83a43c1c91b266c40fd76ecd39a50a8c630ae69dc72891"
+dependencies = [
+ "getrandom",
+ "libredox",
+ "thiserror",
+]
+
+[[package]]
+name = "regex"
+version = "1.10.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "b91213439dad192326a0d7c6ee3955910425f441d7038e0d6933b0aec5c4517f"
dependencies = [
"aho-corasick",
"memchr",
@@ -1002,9 +1521,9 @@
[[package]]
name = "regex-automata"
-version = "0.4.6"
+version = "0.4.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "86b83b8b9847f9bf95ef68afb0b8e6cdb80f498442f5179a29fad448fcc1eaea"
+checksum = "38caf58cc5ef2fed281f89292ef23f6365465ed9a41b7a7754eb4e26496c92df"
dependencies = [
"aho-corasick",
"memchr",
@@ -1013,9 +1532,53 @@
[[package]]
name = "regex-syntax"
-version = "0.8.3"
+version = "0.8.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "adad44e29e4c806119491a7f06f03de4d1af22c3a680dd47f1e6e179439d1f56"
+checksum = "7a66a03ae7c801facd77a29370b4faec201768915ac14a721ba36f20bc9c209b"
+
+[[package]]
+name = "reqwest"
+version = "0.12.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "c7d6d2a27d57148378eb5e111173f4276ad26340ecc5c49a4a2152167a2d6a37"
+dependencies = [
+ "base64",
+ "bytes",
+ "encoding_rs",
+ "futures-channel",
+ "futures-core",
+ "futures-util",
+ "h2",
+ "http 1.1.0",
+ "http-body 1.0.1",
+ "http-body-util",
+ "hyper 1.4.1",
+ "hyper-rustls",
+ "hyper-tls",
+ "hyper-util",
+ "ipnet",
+ "js-sys",
+ "log",
+ "mime",
+ "native-tls",
+ "once_cell",
+ "percent-encoding",
+ "pin-project-lite",
+ "rustls-pemfile",
+ "serde",
+ "serde_json",
+ "serde_urlencoded",
+ "sync_wrapper 1.0.1",
+ "system-configuration",
+ "tokio",
+ "tokio-native-tls",
+ "tower-service",
+ "url",
+ "wasm-bindgen",
+ "wasm-bindgen-futures",
+ "web-sys",
+ "winreg",
+]
[[package]]
name = "reword"
@@ -1027,12 +1590,33 @@
]
[[package]]
+name = "ring"
+version = "0.17.8"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "c17fa4cb658e3583423e915b9f3acc01cceaee1860e33d59ebae66adc3a2dc0d"
+dependencies = [
+ "cc",
+ "cfg-if",
+ "getrandom",
+ "libc",
+ "spin",
+ "untrusted",
+ "windows-sys 0.52.0",
+]
+
+[[package]]
+name = "rustc-demangle"
+version = "0.1.24"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "719b953e2095829ee67db738b3bfa9fa368c94900df327b3f07fe6e794d2fe1f"
+
+[[package]]
name = "rustix"
version = "0.38.34"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "70dc5ec042f7a43c4a73241207cecc9873a06d45debb38b329f8541d85c2730f"
dependencies = [
- "bitflags",
+ "bitflags 2.6.0",
"errno",
"libc",
"linux-raw-sys",
@@ -1040,18 +1624,52 @@
]
[[package]]
-name = "rusty-fork"
-version = "0.3.0"
+name = "rustls"
+version = "0.23.12"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "cb3dcc6e454c328bb824492db107ab7c0ae8fcffe4ad210136ef014458c1bc4f"
+checksum = "c58f8c84392efc0a126acce10fa59ff7b3d2ac06ab451a33f2741989b806b044"
dependencies = [
- "fnv",
- "quick-error",
- "tempfile",
- "wait-timeout",
+ "once_cell",
+ "rustls-pki-types",
+ "rustls-webpki",
+ "subtle",
+ "zeroize",
]
[[package]]
+name = "rustls-pemfile"
+version = "2.1.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "29993a25686778eb88d4189742cd713c9bce943bc54251a33509dc63cbacf73d"
+dependencies = [
+ "base64",
+ "rustls-pki-types",
+]
+
+[[package]]
+name = "rustls-pki-types"
+version = "1.7.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "976295e77ce332211c0d24d92c0e83e50f5c5f046d11082cea19f3df13a3562d"
+
+[[package]]
+name = "rustls-webpki"
+version = "0.102.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "8e6b52d4fda176fd835fdc55a835d4a89b8499cad995885a21149d5ad62f852e"
+dependencies = [
+ "ring",
+ "rustls-pki-types",
+ "untrusted",
+]
+
+[[package]]
+name = "rustversion"
+version = "1.0.17"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "955d28af4278de8121b7ebeb796b6a45735dc01436d898801014aced2773a3d6"
+
+[[package]]
name = "ryu"
version = "1.0.18"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -1067,25 +1685,57 @@
]
[[package]]
+name = "schannel"
+version = "0.1.23"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "fbc91545643bcf3a0bbb6569265615222618bdf33ce4ffbbd13c4bbd4c093534"
+dependencies = [
+ "windows-sys 0.52.0",
+]
+
+[[package]]
name = "scopeguard"
version = "1.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49"
[[package]]
-name = "serde"
-version = "1.0.200"
+name = "security-framework"
+version = "2.11.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "ddc6f9cc94d67c0e21aaf7eda3a010fd3af78ebf6e096aa6e2e13c79749cce4f"
+checksum = "897b2245f0b511c87893af39b033e5ca9cce68824c4d7e7630b5a1d339658d02"
+dependencies = [
+ "bitflags 2.6.0",
+ "core-foundation",
+ "core-foundation-sys",
+ "libc",
+ "security-framework-sys",
+]
+
+[[package]]
+name = "security-framework-sys"
+version = "2.11.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "75da29fe9b9b08fe9d6b22b5b4bcbc75d8db3aa31e639aa56bb62e9d46bfceaf"
+dependencies = [
+ "core-foundation-sys",
+ "libc",
+]
+
+[[package]]
+name = "serde"
+version = "1.0.204"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "bc76f558e0cbb2a839d37354c575f1dc3fdc6546b5be373ba43d95f231bf7c12"
dependencies = [
"serde_derive",
]
[[package]]
name = "serde_derive"
-version = "1.0.200"
+version = "1.0.204"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "856f046b9400cee3c8c94ed572ecdb752444c24528c035cd35882aad6f492bcb"
+checksum = "e0cd7e117be63d3c3678776753929474f3b04a43a080c744d6b0ae2a8c28e222"
dependencies = [
"proc-macro2",
"quote",
@@ -1094,9 +1744,9 @@
[[package]]
name = "serde_json"
-version = "1.0.116"
+version = "1.0.120"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "3e17db7126d17feb94eb3fad46bf1a96b034e8aacbc2e775fe81505f8b0b2813"
+checksum = "4e0d21c9a8cae1235ad58a00c11cb40d4b1e5c784f1ef2c537876ed6ffd8b7c5"
dependencies = [
"itoa",
"ryu",
@@ -1104,16 +1754,66 @@
]
[[package]]
+name = "serde_path_to_error"
+version = "0.1.16"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "af99884400da37c88f5e9146b7f1fd0fbcae8f6eec4e9da38b67d05486f814a6"
+dependencies = [
+ "itoa",
+ "serde",
+]
+
+[[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 = "shell-escape"
version = "0.1.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "45bb67a18fa91266cc7807181f62f9178a6873bfad7dc788c42e6430db40184f"
[[package]]
-name = "spin"
-version = "0.5.2"
+name = "signal-hook-registry"
+version = "1.4.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "6e63cff320ae2c57904679ba7cb63280a3dc4613885beafb148ee7bf9aa9042d"
+checksum = "a9e9e0b4211b72e7b8b6e85c807d36c212bdb33ea8587f7569562a84df5465b1"
+dependencies = [
+ "libc",
+]
+
+[[package]]
+name = "slab"
+version = "0.4.9"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "8f92a496fb766b417c996b9c5e57daf2f7ad3b0bebe1ccfca4856390e3d3bb67"
+dependencies = [
+ "autocfg",
+]
+
+[[package]]
+name = "smallvec"
+version = "1.13.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "3c5e1a9a646d36c3599cd173a41282daf47c44583ad367b8e6837255952e5c67"
+
+[[package]]
+name = "socket2"
+version = "0.5.7"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ce305eb0b4296696835b71df73eb912e0f1ffd2556a501fcede6e0c50349191c"
+dependencies = [
+ "libc",
+ "windows-sys 0.52.0",
+]
[[package]]
name = "spin"
@@ -1131,10 +1831,16 @@
checksum = "7da8b5736845d9f2fcb837ea5d9e2628564b3b043a70948a3f0b778838c5fb4f"
[[package]]
-name = "syn"
-version = "2.0.61"
+name = "subtle"
+version = "2.6.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "c993ed8ccba56ae856363b1845da7266a7cb78e1d146c8a32d54b45a8b831fc9"
+checksum = "13c2bddecc57b384dee18652358fb23172facb8a2c51ccc10d74c157bdea3292"
+
+[[package]]
+name = "syn"
+version = "2.0.72"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "dc4b9b9bf2add8093d3f2c0204471e951b2285580335de42f9d2534f3ae7a8af"
dependencies = [
"proc-macro2",
"quote",
@@ -1142,6 +1848,39 @@
]
[[package]]
+name = "sync_wrapper"
+version = "0.1.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "2047c6ded9c721764247e62cd3b03c09ffc529b2ba5b10ec482ae507a4a70160"
+
+[[package]]
+name = "sync_wrapper"
+version = "1.0.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "a7065abeca94b6a8a577f9bd45aa0867a2238b74e8eb67cf10d492bc39351394"
+
+[[package]]
+name = "system-configuration"
+version = "0.5.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ba3a3adc5c275d719af8cb4272ea1c4a6d668a777f37e115f6d11ddbc1c8e0e7"
+dependencies = [
+ "bitflags 1.3.2",
+ "core-foundation",
+ "system-configuration-sys",
+]
+
+[[package]]
+name = "system-configuration-sys"
+version = "0.5.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "a75fb188eb626b924683e3b95e3a48e63551fcfb51949de2f06a9d91dbee93c9"
+dependencies = [
+ "core-foundation-sys",
+ "libc",
+]
+
+[[package]]
name = "tempfile"
version = "3.10.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -1155,18 +1894,18 @@
[[package]]
name = "thiserror"
-version = "1.0.60"
+version = "1.0.63"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "579e9083ca58dd9dcf91a9923bb9054071b9ebbd800b342194c9feb0ee89fc18"
+checksum = "c0342370b38b6a11b6cc11d6a805569958d54cfa061a29969c3b5ce2ea405724"
dependencies = [
"thiserror-impl",
]
[[package]]
name = "thiserror-impl"
-version = "1.0.60"
+version = "1.0.63"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "e2470041c06ec3ac1ab38d0356a6119054dedaea53e12fbefc0de730a1c08524"
+checksum = "a4558b58466b9ad7ca0f102865eccc95938dca1a74a856f2b57b6629050da261"
dependencies = [
"proc-macro2",
"quote",
@@ -1184,10 +1923,142 @@
]
[[package]]
-name = "unarray"
-version = "0.1.4"
+name = "tinyvec"
+version = "1.8.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "eaea85b334db583fe3274d12b4cd1880032beab409c0d774be044d4480ab9a94"
+checksum = "445e881f4f6d382d5f27c034e25eb92edd7c784ceab92a0937db7f2e9471b938"
+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.39.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d040ac2b29ab03b09d4129c2f5bbd012a3ac2f79d38ff506a4bf8dd34b0eac8a"
+dependencies = [
+ "backtrace",
+ "bytes",
+ "libc",
+ "mio",
+ "parking_lot",
+ "pin-project-lite",
+ "signal-hook-registry",
+ "socket2",
+ "tokio-macros",
+ "windows-sys 0.52.0",
+]
+
+[[package]]
+name = "tokio-macros"
+version = "2.4.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "693d596312e88961bc67d7f1f97af8a70227d9f90c31bba5806eec004978d752"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "syn",
+]
+
+[[package]]
+name = "tokio-native-tls"
+version = "0.3.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "bbae76ab933c85776efabc971569dd6119c580d8f5d448769dec1764bf796ef2"
+dependencies = [
+ "native-tls",
+ "tokio",
+]
+
+[[package]]
+name = "tokio-rustls"
+version = "0.26.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "0c7bc40d0e5a97695bb96e27995cd3a08538541b0a846f65bba7a359f36700d4"
+dependencies = [
+ "rustls",
+ "rustls-pki-types",
+ "tokio",
+]
+
+[[package]]
+name = "tokio-util"
+version = "0.7.11"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "9cf6b47b3771c49ac75ad09a6162f53ad4b8088b76ac60e8ec1455b31a189fe1"
+dependencies = [
+ "bytes",
+ "futures-core",
+ "futures-sink",
+ "pin-project-lite",
+ "tokio",
+]
+
+[[package]]
+name = "tower"
+version = "0.4.13"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "b8fa9be0de6cf49e536ce1851f987bd21a43b771b09473c3549a6c853db37c1c"
+dependencies = [
+ "futures-core",
+ "futures-util",
+ "pin-project",
+ "pin-project-lite",
+ "tokio",
+ "tower-layer",
+ "tower-service",
+ "tracing",
+]
+
+[[package]]
+name = "tower-layer"
+version = "0.3.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "c20c8dbed6283a09604c3e69b4b7eeb54e298b8a600d4d5ecb5ad39de609f1d0"
+
+[[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.40"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "c3523ab5a71916ccf420eebdf5521fcef02141234bbc0b8a49f2fdc4544364ef"
+dependencies = [
+ "log",
+ "pin-project-lite",
+ "tracing-core",
+]
+
+[[package]]
+name = "tracing-core"
+version = "0.1.32"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "c06d3da6113f116aaee68e4d601191614c9053067f9ab7f6edbcb161237daa54"
+dependencies = [
+ "once_cell",
+]
+
+[[package]]
+name = "try-lock"
+version = "0.2.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "e421abadd41a4225275504ea4d6566923418b7f05506fbc9c0fe86ba7396114b"
+
+[[package]]
+name = "unicode-bidi"
+version = "0.3.15"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "08f95100a766bf4f8f28f90d77e0a5461bbdb219042e7679bebe79004fed8d75"
[[package]]
name = "unicode-ident"
@@ -1196,27 +2067,50 @@
checksum = "3354b9ac3fae1ff6755cb6db53683adb661634f67557942dea4facebec0fee4b"
[[package]]
+name = "unicode-normalization"
+version = "0.1.23"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "a56d1686db2308d901306f92a263857ef59ea39678a5458e7cb17f01415101f5"
+dependencies = [
+ "tinyvec",
+]
+
+[[package]]
name = "unicode-segmentation"
version = "1.11.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d4c87d22b6e3f4a18d4d40ef354e97c90fcb14dd91d7dc0aa9d8a1172ebf7202"
[[package]]
-name = "utf8parse"
-version = "0.2.1"
+name = "untrusted"
+version = "0.9.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "711b9620af191e0cdc7468a8d14e709c3dcdb115b36f838e601583af800a370a"
+checksum = "8ecb6da28b8a351d773b68d5825ac39017e680750f980f3a1a85cd8dd28a47c1"
[[package]]
-name = "wait-timeout"
-version = "0.2.0"
+name = "url"
+version = "2.5.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "9f200f5b12eb75f8c1ed65abd4b2db8a6e1b138a20de009dacee265a2498f3f6"
+checksum = "22784dbdf76fdde8af1aeda5622b546b422b6fc585325248a2bf9f5e41e94d6c"
dependencies = [
- "libc",
+ "form_urlencoded",
+ "idna",
+ "percent-encoding",
]
[[package]]
+name = "utf8parse"
+version = "0.2.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "06abde3611657adf66d383f00b093d7faecc7fa57071cce2578660c9f1010821"
+
+[[package]]
+name = "vcpkg"
+version = "0.2.15"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "accd4ea62f7bb7a82fe23066fb0957d48ef677f6eeb8215f372f52e48bb32426"
+
+[[package]]
name = "walkdir"
version = "2.5.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -1227,6 +2121,15 @@
]
[[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"
@@ -1258,6 +2161,18 @@
]
[[package]]
+name = "wasm-bindgen-futures"
+version = "0.4.42"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "76bc14366121efc8dbb487ab05bcc9d346b3b5ec0eaa76e46594cabbe51762c0"
+dependencies = [
+ "cfg-if",
+ "js-sys",
+ "wasm-bindgen",
+ "web-sys",
+]
+
+[[package]]
name = "wasm-bindgen-macro"
version = "0.2.92"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -1333,7 +2248,22 @@
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "33ab640c8d7e35bf8ba19b884ba838ceb4fba93a4e8c65a9059d08afcfc683d9"
dependencies = [
- "windows-targets 0.52.5",
+ "windows-targets 0.52.6",
+]
+
+[[package]]
+name = "windows-sys"
+version = "0.42.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "5a3e1820f08b8513f676f7ab6c1f99ff312fb97b553d30ff4dd86f9f15728aa7"
+dependencies = [
+ "windows_aarch64_gnullvm 0.42.2",
+ "windows_aarch64_msvc 0.42.2",
+ "windows_i686_gnu 0.42.2",
+ "windows_i686_msvc 0.42.2",
+ "windows_x86_64_gnu 0.42.2",
+ "windows_x86_64_gnullvm 0.42.2",
+ "windows_x86_64_msvc 0.42.2",
]
[[package]]
@@ -1347,11 +2277,20 @@
[[package]]
name = "windows-sys"
+version = "0.48.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "677d2418bec65e3338edb076e806bc1ec15693c5d0104683f2efe857f61056a9"
+dependencies = [
+ "windows-targets 0.48.5",
+]
+
+[[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.5",
+ "windows-targets 0.52.6",
]
[[package]]
@@ -1371,18 +2310,33 @@
[[package]]
name = "windows-targets"
-version = "0.52.5"
+version = "0.48.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "6f0713a46559409d202e70e28227288446bf7841d3211583a4b53e3f6d96e7eb"
+checksum = "9a2fa6e2155d7247be68c096456083145c183cbbbc2764150dda45a87197940c"
dependencies = [
- "windows_aarch64_gnullvm 0.52.5",
- "windows_aarch64_msvc 0.52.5",
- "windows_i686_gnu 0.52.5",
+ "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.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "9b724f72796e036ab90c1021d4780d4d3d648aca59e491e6b98e725b84e99973"
+dependencies = [
+ "windows_aarch64_gnullvm 0.52.6",
+ "windows_aarch64_msvc 0.52.6",
+ "windows_i686_gnu 0.52.6",
"windows_i686_gnullvm",
- "windows_i686_msvc 0.52.5",
- "windows_x86_64_gnu 0.52.5",
- "windows_x86_64_gnullvm 0.52.5",
- "windows_x86_64_msvc 0.52.5",
+ "windows_i686_msvc 0.52.6",
+ "windows_x86_64_gnu 0.52.6",
+ "windows_x86_64_gnullvm 0.52.6",
+ "windows_x86_64_msvc 0.52.6",
]
[[package]]
@@ -1393,9 +2347,15 @@
[[package]]
name = "windows_aarch64_gnullvm"
-version = "0.52.5"
+version = "0.48.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "7088eed71e8b8dda258ecc8bac5fb1153c5cffaf2578fc8ff5d61e23578d3263"
+checksum = "2b38e32f0abccf9987a4e3079dfb67dcd799fb61361e53e2882c3cbaf0d905d8"
+
+[[package]]
+name = "windows_aarch64_gnullvm"
+version = "0.52.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "32a4622180e7a0ec044bb555404c800bc9fd9ec262ec147edd5989ccd0c02cd3"
[[package]]
name = "windows_aarch64_msvc"
@@ -1405,9 +2365,15 @@
[[package]]
name = "windows_aarch64_msvc"
-version = "0.52.5"
+version = "0.48.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "9985fd1504e250c615ca5f281c3f7a6da76213ebd5ccc9561496568a2752afb6"
+checksum = "dc35310971f3b2dbbf3f0690a219f40e2d9afcf64f9ab7cc1be722937c26b4bc"
+
+[[package]]
+name = "windows_aarch64_msvc"
+version = "0.52.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "09ec2a7bb152e2252b53fa7803150007879548bc709c039df7627cabbd05d469"
[[package]]
name = "windows_i686_gnu"
@@ -1417,15 +2383,21 @@
[[package]]
name = "windows_i686_gnu"
-version = "0.52.5"
+version = "0.48.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "88ba073cf16d5372720ec942a8ccbf61626074c6d4dd2e745299726ce8b89670"
+checksum = "a75915e7def60c94dcef72200b9a8e58e5091744960da64ec734a6c6e9b3743e"
+
+[[package]]
+name = "windows_i686_gnu"
+version = "0.52.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "8e9b5ad5ab802e97eb8e295ac6720e509ee4c243f69d781394014ebfe8bbfa0b"
[[package]]
name = "windows_i686_gnullvm"
-version = "0.52.5"
+version = "0.52.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "87f4261229030a858f36b459e748ae97545d6f1ec60e5e0d6a3d32e0dc232ee9"
+checksum = "0eee52d38c090b3caa76c563b86c3a4bd71ef1a819287c19d586d7334ae8ed66"
[[package]]
name = "windows_i686_msvc"
@@ -1435,9 +2407,15 @@
[[package]]
name = "windows_i686_msvc"
-version = "0.52.5"
+version = "0.48.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "db3c2bf3d13d5b658be73463284eaf12830ac9a26a90c717b7f771dfe97487bf"
+checksum = "8f55c233f70c4b27f66c523580f78f1004e8b5a8b659e05a4eb49d4166cca406"
+
+[[package]]
+name = "windows_i686_msvc"
+version = "0.52.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "240948bc05c5e7c6dabba28bf89d89ffce3e303022809e73deaefe4f6ec56c66"
[[package]]
name = "windows_x86_64_gnu"
@@ -1447,9 +2425,15 @@
[[package]]
name = "windows_x86_64_gnu"
-version = "0.52.5"
+version = "0.48.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "4e4246f76bdeff09eb48875a0fd3e2af6aada79d409d33011886d3e1581517d9"
+checksum = "53d40abd2583d23e4718fddf1ebec84dbff8381c07cae67ff7768bbf19c6718e"
+
+[[package]]
+name = "windows_x86_64_gnu"
+version = "0.52.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "147a5c80aabfbf0c7d901cb5895d1de30ef2907eb21fbbab29ca94c5b08b1a78"
[[package]]
name = "windows_x86_64_gnullvm"
@@ -1459,9 +2443,15 @@
[[package]]
name = "windows_x86_64_gnullvm"
-version = "0.52.5"
+version = "0.48.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "852298e482cd67c356ddd9570386e2862b5673c85bd5f88df9ab6802b334c596"
+checksum = "0b7b52767868a23d5bab768e390dc5f5c55825b6d30b86c844ff2dc7414044cc"
+
+[[package]]
+name = "windows_x86_64_gnullvm"
+version = "0.52.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "24d5b23dc417412679681396f2b49f3de8c1473deb516bd34410872eff51ed0d"
[[package]]
name = "windows_x86_64_msvc"
@@ -1471,9 +2461,25 @@
[[package]]
name = "windows_x86_64_msvc"
-version = "0.52.5"
+version = "0.48.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "bec47e5bfd1bff0eeaf6d8b485cc1074891a197ab4225d504cb7a1ab88b02bf0"
+checksum = "ed94fce61571a4006852b7389a063ab983c02eb1bb37b47f8272ce92d06d9538"
+
+[[package]]
+name = "windows_x86_64_msvc"
+version = "0.52.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec"
+
+[[package]]
+name = "winreg"
+version = "0.52.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "a277a57398d4bfa075df44f501a17cfdf8542d224f0d36095a2adc7aee4ef0a5"
+dependencies = [
+ "cfg-if",
+ "windows-sys 0.48.0",
+]
[[package]]
name = "xshell"
@@ -1491,7 +2497,7 @@
checksum = "9d422e8e38ec76e2f06ee439ccc765e9c6a9638b9e7c9f2e8255e4d41e8bd852"
[[package]]
-name = "yansi"
-version = "0.5.1"
+name = "zeroize"
+version = "1.8.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "09041cd90cf85f7f8b2df60c646f853b7f535ce68f85244eb6731cf89fa498ec"
+checksum = "ced3678a2879b30306d323f4542626697a464a97c0a07c9aebf7ebca65cd4dde"
diff --git a/common/Cargo.toml b/common/Cargo.toml
index 9e8972b..30689fc 100644
--- a/common/Cargo.toml
+++ b/common/Cargo.toml
@@ -2,11 +2,9 @@
members = [
"build_scripts",
"cmd_runner",
- "derive_fuzztest",
- "derive_fuzztest/fuzz",
- "derive_fuzztest_macro",
"handle_map",
"lock_adapter",
+ "oauth",
"pourover",
"pourover_macro",
]
@@ -32,8 +30,6 @@
[workspace.dependencies]
# local crates
cmd_runner = { path = "cmd_runner" }
-derive_fuzztest = { path = "derive_fuzztest" }
-derive_fuzztest_macro = { path = "derive_fuzztest_macro" }
lock_adapter = { path = "lock_adapter" }
handle_map = { path = "handle_map" }
pourover = { path = "pourover" }
@@ -42,7 +38,7 @@
# from crates.io
anyhow = "1.0.75"
arbitrary = "1.3.2"
-clap = { version = "4.4.11", features = ["derive"] }
+clap = { version = "4.5.13", features = ["derive"] }
criterion = { version = "0.5.1", features = ["html_reports"] }
jni = "0.21.1"
lazy_static = { version = "1.4.0", features = ["spin_no_std"] }
@@ -51,18 +47,22 @@
pretty_assertions = "1.4.0"
prettyplease = "0.2.16"
proc-macro2 = "1.0"
-proptest = "1.4.0"
-proptest-arbitrary-interop = { git = "https://github.com/brson/proptest-arbitrary-interop.git", branch = "incorrect-format" }
quickcheck = "1.0.3"
quote = "1.0"
spin = { version = "0.9.8", features = ["once", "lock_api", "rwlock"] }
syn = { version = "2.0", features = ["full"] }
xshell = "0.2.6"
+hex = "0.4.3"
+log = "0.4.22"
+rand = "0.8.5"
+reqwest = "0.12.5"
+tokio = { version = "1.39.1", features = ["full"] }
[workspace.package]
version = "0.1.0"
edition = "2021"
publish = false
+license = "Apache-2.0"
[profile.test]
# speed up test execution
diff --git a/common/build_scripts/Cargo.toml b/common/build_scripts/Cargo.toml
index 8f9d217..35efdb8 100644
--- a/common/build_scripts/Cargo.toml
+++ b/common/build_scripts/Cargo.toml
@@ -3,6 +3,7 @@
version.workspace = true
edition.workspace = true
publish.workspace = true
+license.workspace = true
rust-version = "1.71.0"
[dependencies]
diff --git a/common/cmd_runner/Cargo.toml b/common/cmd_runner/Cargo.toml
index 435b29f..189d607 100644
--- a/common/cmd_runner/Cargo.toml
+++ b/common/cmd_runner/Cargo.toml
@@ -3,15 +3,17 @@
version.workspace = true
edition.workspace = true
publish.workspace = true
+license.workspace = true
[dependencies]
anyhow = "1.0.64"
-shell-escape = "0.1.5"
-owo-colors = "3.5.0"
-xshell = "0.2.6"
-clap = { version = "4.5.4", features = ["derive"] }
-file-header = "0.1.2"
chrono = "0.4.37"
-log = "0.4.21"
+clap = { version = "4.5.13", features = ["derive"] }
+file-header = "0.1.2"
globset = "0.4.14"
-
+log = "0.4.21"
+owo-colors = "3.5.0"
+serde = { version = "1.0.203", features = ["derive"] }
+serde_json = "1.0.118"
+shell-escape = "0.1.5"
+xshell = "0.2.6"
diff --git a/common/cmd_runner/src/cargo_workspace.rs b/common/cmd_runner/src/cargo_workspace.rs
index 2b6840e..9bc3f27 100644
--- a/common/cmd_runner/src/cargo_workspace.rs
+++ b/common/cmd_runner/src/cargo_workspace.rs
@@ -40,9 +40,9 @@
#[derive(clap::Args, Debug, Clone, Default)]
pub struct CargoOptions {
#[arg(long, help = "whether to run cargo with --locked")]
- locked: bool,
+ pub locked: bool,
#[arg(long, help = "gather coverage metrics")]
- coverage: bool,
+ pub coverage: bool,
}
impl CargoOptions {
diff --git a/common/cmd_runner/src/fuzzers.rs b/common/cmd_runner/src/fuzzers.rs
new file mode 100644
index 0000000..2d0ca3b
--- /dev/null
+++ b/common/cmd_runner/src/fuzzers.rs
@@ -0,0 +1,114 @@
+// 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 std::{
+ collections::HashMap,
+ path::{self, PathBuf},
+};
+
+use serde::Deserialize;
+use xshell::{cmd, Shell};
+
+/// Partial structure for parsing the output of `cargo metadata --format-version=1`.
+///
+/// This doesn't contain all the fields in `cargo metadata`, just the ones we need.
+#[derive(Deserialize)]
+struct CargoMetadata {
+ packages: Vec<CargoMetadataPackage>,
+}
+
+#[derive(Deserialize)]
+struct CargoMetadataPackage {
+ manifest_path: String,
+ metadata: Option<HashMap<String, serde_json::Value>>,
+ targets: Vec<CargoMetadataPackageTarget>,
+}
+
+#[derive(Deserialize)]
+struct CargoMetadataPackageTarget {
+ name: String,
+ kind: Vec<String>,
+}
+
+/// A `bin` target defined for fuzzing. See [`iter_fuzz_binaries`].
+#[derive(Debug)]
+pub struct FuzzTarget {
+ pub fuzz_dir: PathBuf,
+ pub target_name: String,
+}
+
+impl FuzzTarget {
+ /// Run the fuzz target using `cargo fuzz run`.
+ pub fn run(&self, sh: &xshell::Shell) -> xshell::Result<()> {
+ let FuzzTarget {
+ fuzz_dir,
+ target_name,
+ } = self;
+ cmd!(
+ sh,
+ "cargo +nightly fuzz run --fuzz-dir {fuzz_dir} {target_name} -- -runs=1000000 -max_total_time=30"
+ )
+ .run()
+ }
+}
+
+/// Create an iterator over all of the fuzz targets defined in the workspace at `root`.
+///
+/// This iterator discovers targets using `cargo metadata`. It looks for crates with the metadata
+/// `cargo-fuzz = true` in its Cargo.toml, and looks for `[[bin]]` targets in all of those crates.
+pub fn iter_fuzz_binaries(root: &path::Path) -> anyhow::Result<impl Iterator<Item = FuzzTarget>> {
+ let sh = Shell::new()?;
+ sh.change_dir(root);
+ let cargo_metadata = cmd!(sh, "cargo metadata --no-deps --format-version=1").read()?;
+ let metadata_json: CargoMetadata = serde_json::from_str(&cargo_metadata)?;
+ let packages = metadata_json.packages.into_iter().filter_map(|package| {
+ let metadata_map = package.metadata.as_ref()?;
+ metadata_map
+ .get("cargo-fuzz")?
+ .as_bool()?
+ .then_some(package)
+ });
+ Ok(packages.flat_map(|package| {
+ let fuzz_manifest = PathBuf::from(&package.manifest_path);
+ let fuzz_dir = fuzz_manifest
+ .parent()
+ .expect("Fuzz manifest should have a parent directory")
+ .to_owned();
+ package
+ .targets
+ .into_iter()
+ .filter(|t| t.kind.contains(&String::from("bin")))
+ .map(move |target| FuzzTarget {
+ fuzz_dir: fuzz_dir.clone(),
+ target_name: target.name.to_string(),
+ })
+ }))
+}
+
+/// Runs all of the fuzz targets defined within the workspace `root`.
+pub fn run_workspace_fuzz_targets(root: &path::Path) -> anyhow::Result<()> {
+ log::info!("Running rust fuzzers");
+ let sh = Shell::new()?;
+ sh.change_dir(root);
+ let fuzz_targets: Vec<_> = iter_fuzz_binaries(root)?.collect();
+ log::info!(
+ "Fuzzing on {} targets. This may take up to {} seconds.",
+ fuzz_targets.len(),
+ 30 * fuzz_targets.len()
+ );
+ for fuzz_target in fuzz_targets {
+ fuzz_target.run(&sh)?;
+ }
+ Ok(())
+}
diff --git a/common/cmd_runner/src/lib.rs b/common/cmd_runner/src/lib.rs
index 8c45df8..24b4427 100644
--- a/common/cmd_runner/src/lib.rs
+++ b/common/cmd_runner/src/lib.rs
@@ -17,6 +17,7 @@
use std::{collections, env, ffi, io, io::BufRead, path, process, thread};
pub mod cargo_workspace;
+pub mod fuzzers;
pub mod license_checker;
pub fn run_cmd_shell(
diff --git a/common/deny.toml b/common/deny.toml
index bd3f18b..ff83d5e 100644
--- a/common/deny.toml
+++ b/common/deny.toml
@@ -71,6 +71,7 @@
allow = [
"MIT",
"Apache-2.0",
+ "BSD-3-Clause",
"Unicode-DFS-2016",
"ISC",
]
@@ -85,6 +86,10 @@
# Each entry is the crate and version constraint, and its specific allow
# list
+
+ # "Reciprocal" licensed crate pulled directly from crates.io without modifications
+ # Important: Update https://third-party-mirror.googlesource.com/option-ext/ if you update this version
+ { allow = ["MPL-2.0"], name = "option-ext", version = "0.2.0" },
]
# Some crates don't have (easily) machine readable licensing information,
@@ -103,8 +108,8 @@
# 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 }
+# Each entry is a crate relative path, and the (opaque) hash of its contents
+#{ path = "LICENSE", hash = 0xbd0eed23 }
#]
[[licenses.clarify]]
@@ -121,7 +126,7 @@
# 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
+ignore = false
# 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
diff --git a/common/derive_fuzztest/Cargo.toml b/common/derive_fuzztest/Cargo.toml
deleted file mode 100644
index 6a22ce5..0000000
--- a/common/derive_fuzztest/Cargo.toml
+++ /dev/null
@@ -1,24 +0,0 @@
-[package]
-name = "derive_fuzztest"
-version.workspace = true
-edition.workspace = true
-publish.workspace = true
-
-[dependencies]
-arbitrary.workspace = true
-derive_fuzztest_macro.workspace = true
-proptest = { workspace = true, optional = true }
-proptest-arbitrary-interop = { workspace = true, optional = true }
-quickcheck = { workspace = true, optional = true }
-
-[features]
-default = ["quickcheck"]
-quickcheck = ["dep:quickcheck", "derive_fuzztest_macro/quickcheck"]
-proptest = [
- "dep:proptest",
- "dep:proptest-arbitrary-interop",
- "derive_fuzztest_macro/proptest",
-]
-
-[lints]
-workspace = true
diff --git a/common/derive_fuzztest/fuzz/.gitignore b/common/derive_fuzztest/fuzz/.gitignore
deleted file mode 100644
index b94a8f4..0000000
--- a/common/derive_fuzztest/fuzz/.gitignore
+++ /dev/null
@@ -1 +0,0 @@
-/corpus
\ No newline at end of file
diff --git a/common/derive_fuzztest/fuzz/Cargo.toml b/common/derive_fuzztest/fuzz/Cargo.toml
deleted file mode 100644
index 1f493b6..0000000
--- a/common/derive_fuzztest/fuzz/Cargo.toml
+++ /dev/null
@@ -1,16 +0,0 @@
-[package]
-name = "derive_fuzz_example"
-version.workspace = true
-edition.workspace = true
-publish.workspace = true
-
-[package.metadata]
-cargo-fuzz = true
-
-[dependencies]
-arbitrary.workspace = true
-derive_fuzztest.workspace = true
-quickcheck.workspace = true
-
-[target.'cfg(fuzzing)'.dependencies]
-libfuzzer-sys.workspace = true
diff --git a/common/derive_fuzztest/fuzz/src/bin/arbitrary.rs b/common/derive_fuzztest/fuzz/src/bin/arbitrary.rs
deleted file mode 100644
index 8f8d77b..0000000
--- a/common/derive_fuzztest/fuzz/src/bin/arbitrary.rs
+++ /dev/null
@@ -1,32 +0,0 @@
-// Copyright 2024 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_attr(fuzzing, no_main)]
-
-use arbitrary::{Arbitrary, Unstructured};
-use derive_fuzztest::fuzztest;
-
-#[derive(Debug, Clone)]
-pub struct SmallU8(u8);
-
-impl<'a> Arbitrary<'a> for SmallU8 {
- fn arbitrary(u: &mut Unstructured<'a>) -> arbitrary::Result<Self> {
- Ok(SmallU8(u.int_in_range(0..=127)?))
- }
-}
-
-#[fuzztest]
-pub fn test(a: SmallU8, b: SmallU8) {
- let _ = a.0 + b.0; // Succeeds because our custom arbitrary impl only generates 0-127 so it never overflows
-}
diff --git a/common/derive_fuzztest/src/lib.rs b/common/derive_fuzztest/src/lib.rs
deleted file mode 100644
index c0dd11c..0000000
--- a/common/derive_fuzztest/src/lib.rs
+++ /dev/null
@@ -1,141 +0,0 @@
-// Copyright 2024 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.
-
-//! Derive macros that generates both a fuzz target for use with `cargo fuzz`, and a property test
-//! (via `quickcheck` or `proptest`) for use with `cargo test`.
-//!
-//! The reason for having both is that property testing allows for quick iteration to make sure the
-//! test works, and can be checked in presubmit CI, while fuzzing can test the input space more
-//! exhaustively and run continuously.
-//!
-//! # Example
-//!
-//! ```no_run
-//! #![cfg_attr(fuzzing, no_main)]
-//!
-//! #[derive_fuzztest::fuzztest]
-//! fn transitive_ord(a: u32, b: u32, c: u32) {
-//! if a >= b && b >= c {
-//! assert!(a >= c);
-//! }
-//! if a <= b && b <= c {
-//! assert!(a <= c);
-//! }
-//! }
-//!
-//! #[test]
-//! fn additional_test_here() {
-//! /* ... */
-//! }
-//! ```
-//!
-//! # Usage
-//!
-//!
-//! Run the generated property tests
-//! ```sh
-//! cargo test
-//! ```
-//!
-//! Run continuous fuzzing
-//! ```sh
-//! cargo +nightly fuzz run <binary name>
-//! ```
-//!
-//! # Crate structure
-//!
-//! If you use `#[fuzz]` or `#[fuzztest]`, the fuzz target imposes the following requirements:
-//!
-//! * The target must be in a separate `[[bin]]` target that only contains a single fuzz target.
-//! * The crate containing the bin target has `[package.metadata] cargo-fuzz = true`
-//! * The bin target is annotated with `#![cfg_attr(fuzzing, no_main)]`
-//!
-//! The recommended structure for your crate `foo` is to put your tests under `foo/fuzz/src/bin`:
-//!
-//! ```text
-//! foo
-//! ├── fuzz
-//! │ ├── src
-//! │ │ └── bin
-//! │ │ └── fuzz_target_1.rs
-//! │ └── Cargo.toml
-//! ├── src
-//! │ └── [project source]
-//! └── Cargo.toml
-//! ```
-//!
-//! This is different from the default structure generated by `cargo fuzz init` or `cargo fuzz add`
-//! so that we can take advantage of [target
-//! auto-discovery](https://doc.rust-lang.org/cargo/reference/cargo-targets.html#target-auto-discovery).
-//! If you prefer, the default structure generated by `cargo fuzz` can also work, but make sure you
-//! remove `test = false` from the generated target in `Cargo.toml`.
-//!
-//! You will also need to declare a dependency on the `libfuzzer-sys` crate, but only if fuzzing is
-//! requested:
-//!
-//! ```toml
-//! [target.'cfg(fuzzing)'.dependencies]
-//! libfuzzer-sys = "*"
-//! ```
-//!
-//! (The reason for this conditional dependency is that `libfuzzer-sys` injects a main function to
-//! the resulting binary, and there will be linking failures if we link that in without defining a
-//! corresponding `fuzz_target`.)
-//!
-//! # Features
-//!
-//! * `quickcheck` (default) — Enable generation of
-//! [`quickcheck`](https://docs.rs/quickcheck/latest/quickcheck/) property tests.
-//! * `proptest` — Enable generation of [`proptest`](https://docs.rs/proptest/latest/proptest/)
-//! property tests.
-//!
-//! #### See also
-//! * [Announcing Better Support for Fuzzing with Structured Inputs in
-//! Rust](https://fitzgeraldnick.com/2020/01/16/better-support-for-fuzzing-structured-inputs-in-rust.html#how-is-all-this-different-from-quickcheck-and-proptest)
-//! * [Bridging Fuzzing and Property
-//! Testing](https://blog.yoshuawuyts.com/bridging-fuzzing-and-property-testing/)
-
-pub use derive_fuzztest_macro::{fuzz, fuzztest, proptest};
-
-#[doc(hidden)]
-pub mod reexport {
- #[cfg(feature = "proptest")]
- pub use proptest;
- #[cfg(feature = "proptest")]
- pub use proptest_arbitrary_interop;
- #[cfg(feature = "quickcheck")]
- pub use quickcheck;
-}
-
-#[cfg(feature = "quickcheck")]
-#[doc(hidden)]
-pub mod arbitrary_bridge {
-
- /// Wrapper type that allows `arbitrary::Arbitrary` to be used as `quickcheck::Arbitrary`
- #[derive(Debug, Clone)]
- pub struct ArbitraryAdapter<T: for<'a> arbitrary::Arbitrary<'a>>(
- pub Result<T, arbitrary::Error>,
- );
-
- impl<T> quickcheck::Arbitrary for ArbitraryAdapter<T>
- where
- T: for<'a> arbitrary::Arbitrary<'a> + Clone + 'static,
- {
- fn arbitrary(g: &mut quickcheck::Gen) -> Self {
- let bytes = Vec::<u8>::arbitrary(g);
- let mut unstructured = arbitrary::Unstructured::new(&bytes);
- Self(T::arbitrary(&mut unstructured))
- }
- }
-}
diff --git a/common/derive_fuzztest_macro/Cargo.toml b/common/derive_fuzztest_macro/Cargo.toml
deleted file mode 100644
index 37b480a..0000000
--- a/common/derive_fuzztest_macro/Cargo.toml
+++ /dev/null
@@ -1,23 +0,0 @@
-[package]
-name = "derive_fuzztest_macro"
-version.workspace = true
-edition.workspace = true
-publish.workspace = true
-
-[dependencies]
-quote.workspace = true
-proc-macro2.workspace = true
-syn = { workspace = true, features = ["extra-traits"]}
-
-[dev-dependencies]
-derive_fuzztest.workspace = true
-pretty_assertions.workspace = true
-prettyplease.workspace = true
-
-[features]
-quickcheck = []
-proptest = []
-
-[lib]
-proc-macro = true
-doc = false
diff --git a/common/derive_fuzztest_macro/src/lib.rs b/common/derive_fuzztest_macro/src/lib.rs
deleted file mode 100644
index beb692a..0000000
--- a/common/derive_fuzztest_macro/src/lib.rs
+++ /dev/null
@@ -1,463 +0,0 @@
-// Copyright 2024 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.
-
-//! Internal crate for use by [`derive_fuzztest`](../derive_fuzztest/index.html). See the
-//! documentation there for usage information.
-
-use proc_macro::TokenStream;
-use proc_macro2::TokenStream as TokenStream2;
-use quote::quote;
-use syn::{parse::Nothing, spanned::Spanned, ItemFn, Pat, PatType, Type};
-
-/// Define a fuzz test.
-///
-/// All input parameters of the given function must implement `arbitrary::Arbitrary`.
-///
-/// This macro derives new items based on the given function.
-/// 1. A `fuzz_target!` is generated that can be used with `cargo fuzz`.
-/// 2. Property tests (`quickcheck` or `proptest`, based on which features are enabled) are
-/// generated that can be tested using `cargo test`.
-///
-/// See the crate documentation [`derive_fuzztest`](../derive_fuzztest/index.html) for details.
-#[proc_macro_attribute]
-pub fn fuzztest(attr: TokenStream, item: TokenStream) -> TokenStream {
- fuzztest_impl(attr.into(), item.into())
- .unwrap_or_else(|e| e.into_compile_error())
- .into()
-}
-
-fn fuzztest_impl(attr: TokenStream2, item: TokenStream2) -> syn::Result<TokenStream2> {
- syn::parse2::<Nothing>(attr)?;
- let func = syn::parse2::<ItemFn>(item)?;
- let fn_def = FunctionDefinition::parse(func)?;
- let original_fn = &fn_def.func;
- let fuzz_target = derive_fuzz_target(&fn_def);
- let proptest_target = proptest::derive_proptest(&fn_def);
- let quickcheck_target = quickcheck::derive_quickcheck(&fn_def);
-
- Ok(quote! {
- #[allow(unused)]
- #original_fn
- #fuzz_target
- #proptest_target
- #quickcheck_target
- })
-}
-
-/// Define a fuzz target only without corresponding test.
-///
-/// All input parameters of the given function must implement `arbitrary::Arbitrary`.
-///
-/// This macro derives a `fuzz_target!` that can be used with `cargo fuzz`. If you wish to generate
-/// property tests that can be used with `cargo test` as well, use [`fuzztest`][macro@fuzztest].
-///
-/// See the crate documentation [`derive_fuzztest`](../derive_fuzztest/index.html) for details.
-#[proc_macro_attribute]
-pub fn fuzz(attr: TokenStream, item: TokenStream) -> TokenStream {
- fuzz_impl(attr.into(), item.into())
- .unwrap_or_else(|e| e.into_compile_error())
- .into()
-}
-
-fn fuzz_impl(attr: TokenStream2, item: TokenStream2) -> syn::Result<TokenStream2> {
- syn::parse2::<Nothing>(attr)?;
- let func = syn::parse2::<ItemFn>(item)?;
- let fn_def = FunctionDefinition::parse(func)?;
- let original_fn = &fn_def.func;
- let fuzz_target = derive_fuzz_target(&fn_def);
-
- Ok(quote! {
- #[allow(unused)]
- #original_fn
- #fuzz_target
- })
-}
-
-/// Define a property test.
-///
-/// This is similar to using `quickcheck!` or `proptest::proptest!` directly.
-///
-/// All input parameters of the given function must implement `arbitrary::Arbitrary`.
-///
-/// Unlike [`fuzztest`][macro@fuzztest], this macro does not have to be placed in a `[[bin]]` target
-/// and a single file can contain multiple of these tests. The generated tests can be run with
-/// `cargo test` as usual.
-#[proc_macro_attribute]
-pub fn proptest(attr: TokenStream, item: TokenStream) -> TokenStream {
- proptest_impl(attr.into(), item.into())
- .unwrap_or_else(|e| e.into_compile_error())
- .into()
-}
-
-fn proptest_impl(attr: TokenStream2, item: TokenStream2) -> syn::Result<TokenStream2> {
- syn::parse2::<Nothing>(attr)?;
- let func = syn::parse2::<ItemFn>(item)?;
- let fn_def = FunctionDefinition::parse(func)?;
- let original_fn = &fn_def.func;
- let proptest_target = proptest::derive_proptest(&fn_def);
-
- Ok(quote! {
- #[allow(unused)]
- #original_fn
- #proptest_target
- })
-}
-
-fn derive_fuzz_target(fn_def: &FunctionDefinition) -> proc_macro2::TokenStream {
- let FunctionDefinition { func, args, types } = fn_def;
- let func_ident = &func.sig.ident;
- quote! {
- #[automatically_derived]
- #[cfg(fuzzing)]
- ::libfuzzer_sys::fuzz_target!(|args: ( #(#types),* )| {
- let ( #(#args),* ) = args; // https://github.com/rust-fuzz/libfuzzer/issues/77
- #func_ident ( #(#args),* )
- });
-
- #[cfg(not(any(fuzzing, rust_analyzer)))]
- fn main() {
- ::std::unreachable!("Run this target with `cargo fuzz` or `cargo test` instead");
- }
- }
-}
-
-#[cfg(any(feature = "quickcheck", test))]
-mod quickcheck {
- use crate::FunctionDefinition;
- use quote::quote;
-
- pub(crate) fn derive_quickcheck(fn_def: &FunctionDefinition) -> proc_macro2::TokenStream {
- let FunctionDefinition { func, args, types } = fn_def;
- let func_ident = &func.sig.ident;
- let adapted_types: Vec<_> = types
- .iter()
- .map(|ty| quote! { ArbitraryAdapter<#ty> })
- .collect();
- let arg_pattern: Vec<_> = args
- .iter()
- .map(|arg| quote! { ArbitraryAdapter(::core::result::Result::Ok(#arg)) })
- .collect();
- let test_name = quote::format_ident!("quickcheck_{func_ident}");
- quote! {
- #[automatically_derived]
- #[test]
- fn #test_name() {
- use ::derive_fuzztest::reexport::quickcheck::TestResult;
- use ::derive_fuzztest::arbitrary_bridge::ArbitraryAdapter;
-
- fn inner(args: (#(#adapted_types),*)) -> TestResult {
- let (#(#arg_pattern),*) = args else { return TestResult::discard() };
- match ::std::panic::catch_unwind(move || {
- #func_ident ( #(#args),* );
- }) {
- ::core::result::Result::Ok(()) => TestResult::passed(),
- ::core::result::Result::Err(e) => TestResult::error(::std::format!("{e:?}")),
- }
- }
-
- ::derive_fuzztest::reexport::quickcheck::QuickCheck::new().tests(1024)
- .quickcheck(inner as fn(_) -> TestResult);
- }
- }
- }
-}
-
-#[cfg(not(any(feature = "quickcheck", test)))]
-mod quickcheck {
- use crate::FunctionDefinition;
-
- pub(crate) fn derive_quickcheck(_fn_def: &FunctionDefinition) -> proc_macro2::TokenStream {
- proc_macro2::TokenStream::default()
- }
-}
-
-#[cfg(any(feature = "proptest", test))]
-mod proptest {
- use crate::FunctionDefinition;
- use quote::quote;
- use syn::{Ident, Signature};
-
- pub(crate) fn derive_proptest(fn_def: &FunctionDefinition) -> proc_macro2::TokenStream {
- let FunctionDefinition { func, args, types } = fn_def;
- let func_attrs = &func.attrs;
- let Signature {
- constness,
- asyncness,
- unsafety,
- abi,
- fn_token,
- ident,
- generics,
- paren_token: _,
- inputs: _,
- variadic: _,
- output,
- } = &func.sig;
- let proptest_ident = Ident::new(&format!("proptest_{ident}"), ident.span());
- quote! {
- #[automatically_derived]
- #[cfg(test)]
- mod #proptest_ident {
- use super::*;
- use ::derive_fuzztest::reexport::proptest;
- use ::derive_fuzztest::reexport::proptest_arbitrary_interop::arb;
-
- proptest::proptest! {
- #![proptest_config(proptest::prelude::ProptestConfig {
- cases: 1024,
- failure_persistence: Some(Box::new(proptest::test_runner::FileFailurePersistence::WithSource("regression"))),
- ..Default::default()
- })]
- #[test]
- #(#func_attrs)*
- #constness #asyncness #unsafety #abi #fn_token #proptest_ident #generics ( args in arb::<(#(#types),*)>() ) #output {
- let (#(#args),*) = args;
- #ident ( #(#args),* );
- }
- }
- }
- }
- }
-}
-
-#[cfg(not(any(feature = "proptest", test)))]
-mod proptest {
- use crate::FunctionDefinition;
-
- pub(crate) fn derive_proptest(_fn_def: &FunctionDefinition) -> proc_macro2::TokenStream {
- proc_macro2::TokenStream::default()
- }
-}
-
-/// Representation of a function definition annotated with one of the attribute macros in this
-/// crate.
-struct FunctionDefinition {
- func: ItemFn,
- args: Vec<Pat>,
- types: Vec<Type>,
-}
-
-impl FunctionDefinition {
- pub fn parse(func: ItemFn) -> syn::Result<Self> {
- let (args, types) = func
- .sig
- .inputs
- .clone()
- .into_iter()
- .map(|arg| match arg {
- syn::FnArg::Receiver(arg_receiver) => Err(syn::Error::new(
- arg_receiver.span(),
- "Receiver not supported",
- )),
- syn::FnArg::Typed(PatType {
- attrs: _,
- pat,
- colon_token: _,
- ty,
- }) => Ok((*pat, *ty)),
- })
- .try_fold((Vec::new(), Vec::new()), |(mut args, mut types), result| {
- result.map(|(arg, type_)| {
- args.push(arg);
- types.push(type_);
- (args, types)
- })
- })?;
- Ok(Self { func, args, types })
- }
-}
-
-#[cfg(test)]
-mod tests {
- use crate::{fuzz_impl, fuzztest_impl, proptest_impl};
- use quote::quote;
- use syn::parse_quote;
-
- /// Assert that a token stream for a `syn::File` is the same as expected.
- ///
- /// Usage is similar to `assert_eq!`:
- /// ```no_run
- /// assert_syn_file!(
- /// macro_impl(quote! {
- /// fn foobar() {}
- /// }),
- /// quote! {
- /// fn macro_rewritten_foobar() {}
- /// }
- /// );
- /// ```
- macro_rules! assert_syn_file {
- ($actual:expr, $expected:expr) => {
- let actual = syn::parse2::<syn::File>($actual).unwrap();
- let expected: syn::File = $expected;
- assert!(
- actual == expected,
- "{}",
- pretty_assertions::StrComparison::new(
- &prettyplease::unparse(&expected),
- &prettyplease::unparse(&actual),
- )
- )
- };
- }
-
- #[test]
- fn test_fuzztest_expansion() {
- assert_syn_file!(
- fuzztest_impl(
- quote! {},
- quote! {
- fn foobar(input: &[u8]) {
- panic!("I am just a test")
- }
- }
- )
- .unwrap(),
- parse_quote! {
- #[allow(unused)]
- fn foobar(input: &[u8]) {
- panic!("I am just a test")
- }
-
- #[automatically_derived]
- #[cfg(fuzzing)]
- ::libfuzzer_sys::fuzz_target!(|args: (&[u8])| {
- let (input) = args;
- foobar(input)
- });
-
- #[cfg(not(any(fuzzing, rust_analyzer)))]
- fn main() {
- ::std::unreachable!("Run this target with `cargo fuzz` or `cargo test` instead");
- }
-
- #[automatically_derived]
- #[cfg(test)]
- mod proptest_foobar {
- use super::*;
- use ::derive_fuzztest::reexport::proptest;
- use ::derive_fuzztest::reexport::proptest_arbitrary_interop::arb;
- proptest::proptest! {
- #![proptest_config(proptest::prelude::ProptestConfig {
- cases: 1024,
- failure_persistence: Some(Box::new(proptest::test_runner::FileFailurePersistence::WithSource("regression"))),
- ..Default::default()
- })]
- #[test]
- fn proptest_foobar(args in arb::<(&[u8])>()) {
- let (input) = args;
- foobar(input);
- }
- }
- }
-
- #[automatically_derived]
- #[test]
- fn quickcheck_foobar() {
- use ::derive_fuzztest::reexport::quickcheck::TestResult;
- use ::derive_fuzztest::arbitrary_bridge::ArbitraryAdapter;
-
- fn inner(args: (ArbitraryAdapter<&[u8]>)) -> TestResult {
- let (ArbitraryAdapter(::core::result::Result::Ok(input))) = args else {
- return TestResult::discard()
- };
- match ::std::panic::catch_unwind(move || {
- foobar(input);
- }) {
- ::core::result::Result::Ok(()) => TestResult::passed(),
- ::core::result::Result::Err(e) => TestResult::error(::std::format!("{e:?}")),
- }
- }
- ::derive_fuzztest::reexport::quickcheck::QuickCheck::new()
- .tests(1024)
- .quickcheck(inner as fn(_) -> TestResult);
- }
- }
- );
- }
-
- #[test]
- fn test_fuzz_expansion() {
- assert_syn_file!(
- fuzz_impl(
- quote! {},
- quote! {
- fn foobar(input: &[u8]) {
- panic!("I am just a test")
- }
- }
- )
- .unwrap(),
- parse_quote! {
- #[allow(unused)]
- fn foobar(input: &[u8]) {
- panic!("I am just a test")
- }
-
- #[automatically_derived]
- #[cfg(fuzzing)]
- ::libfuzzer_sys::fuzz_target!(|args: (&[u8])| {
- let (input) = args;
- foobar(input)
- });
-
- #[cfg(not(any(fuzzing, rust_analyzer)))]
- fn main() {
- ::std::unreachable!("Run this target with `cargo fuzz` or `cargo test` instead");
- }
- }
- );
- }
-
- #[test]
- fn test_proptest_expansion() {
- assert_syn_file!(
- proptest_impl(
- quote! {},
- quote! {
- fn foobar(input: &[u8]) {
- panic!("I am just a test")
- }
- }
- )
- .unwrap(),
- parse_quote! {
- #[allow(unused)]
- fn foobar(input: &[u8]) {
- panic!("I am just a test")
- }
-
- #[automatically_derived]
- #[cfg(test)]
- mod proptest_foobar {
- use super::*;
- use ::derive_fuzztest::reexport::proptest;
- use ::derive_fuzztest::reexport::proptest_arbitrary_interop::arb;
- proptest::proptest! {
- #![proptest_config(proptest::prelude::ProptestConfig {
- cases: 1024,
- failure_persistence: Some(Box::new(proptest::test_runner::FileFailurePersistence::WithSource("regression"))),
- ..Default::default()
- })]
- #[test]
- fn proptest_foobar(args in arb::<(&[u8])>()) {
- let (input) = args;
- foobar(input);
- }
- }
- }
- }
- );
- }
-}
diff --git a/common/handle_map/Cargo.toml b/common/handle_map/Cargo.toml
index 13973d4..8db74f3 100644
--- a/common/handle_map/Cargo.toml
+++ b/common/handle_map/Cargo.toml
@@ -3,16 +3,17 @@
version.workspace = true
edition.workspace = true
publish.workspace = true
+license.workspace = true
[lints]
workspace = true
[dependencies]
+lazy_static.workspace = true
lock_adapter.workspace = true
[dev-dependencies]
criterion.workspace = true
-lazy_static.workspace = true
[[bench]]
name = "benches"
diff --git a/common/handle_map/src/declare_handle_map.rs b/common/handle_map/src/declare_handle_map.rs
index 89078fa..8710275 100644
--- a/common/handle_map/src/declare_handle_map.rs
+++ b/common/handle_map/src/declare_handle_map.rs
@@ -42,9 +42,6 @@
/// 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};
///
@@ -105,7 +102,7 @@
" which references values of type `", ::core::stringify!($wrapped_type), "`."
)]
pub mod $handle_module_name {
- lazy_static! {
+ $crate::reexport::lazy_static::lazy_static! {
static ref GLOBAL_HANDLE_MAP: $crate::HandleMap<$wrapped_type> =
$crate::HandleMap::with_dimensions($map_dimension_provider);
}
diff --git a/common/handle_map/src/lib.rs b/common/handle_map/src/lib.rs
index 72bb78a..43eeb19 100644
--- a/common/handle_map/src/lib.rs
+++ b/common/handle_map/src/lib.rs
@@ -29,6 +29,11 @@
use shard::{HandleMapShard, ShardAllocationError};
+#[doc(hidden)]
+pub mod reexport {
+ pub use lazy_static;
+}
+
/// 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`.
diff --git a/common/lock_adapter/Cargo.toml b/common/lock_adapter/Cargo.toml
index 6e3174b..23234ad 100644
--- a/common/lock_adapter/Cargo.toml
+++ b/common/lock_adapter/Cargo.toml
@@ -3,6 +3,7 @@
version.workspace = true
edition.workspace = true
publish.workspace = true
+license.workspace = true
[lints]
workspace = true
diff --git a/common/pourover/Cargo.toml b/common/pourover/Cargo.toml
index eb03cdb..2ed5642 100644
--- a/common/pourover/Cargo.toml
+++ b/common/pourover/Cargo.toml
@@ -3,6 +3,7 @@
version.workspace = true
edition.workspace = true
publish.workspace = true
+license.workspace = true
[lints]
workspace = true
diff --git a/common/pourover/src/exception.rs b/common/pourover/src/exception.rs
new file mode 100644
index 0000000..24e7962
--- /dev/null
+++ b/common/pourover/src/exception.rs
@@ -0,0 +1,245 @@
+// Copyright 2024 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.
+
+//! Utilities for handling errors and propagating them into exceptions in Java.
+
+use jni::{
+ descriptors::Desc,
+ objects::{JClass, JThrowable},
+ JNIEnv,
+};
+use std::fmt::Debug;
+
+/// An error that can be thrown as a Java exception, or an error in the JNI layer.
+///
+/// This allows using Rust's error propagation mechanism (the `?` operator) to surface both JNI
+/// errors and Java throwables, and convert them to Java exceptions at the top level. At the top
+/// level of a JNI function implementation, it can use [`ThrowableJniResultExt::unwrap_or_throw`] to
+/// convert the error to Java exception, or terminate the JVM with a fatal error in the case of JNI
+/// errors.
+pub enum ThrowableJniError<'local> {
+ /// A java throwable object instance. The throwable can be received from the Java side, or
+ /// created using [`call_constructor`][crate::call_constructor].
+ JavaThrowable(JThrowable<'local>),
+ /// A [`jni::errors::Exception`], which contains the class and the message to be able to create
+ /// the exception to be thrown.
+ JavaException(jni::errors::Exception),
+ /// An error from the [`jni`] crate.
+ JniError(jni::errors::Error),
+}
+
+impl<'local> ThrowableJniError<'local> {
+ /// Throws the error as a Java exception.
+ ///
+ /// If the error is a `JavaThrowable` or a `JavaException`, it will be thrown on the given
+ /// `env`. If the error is a `JniError`, this will turn it into a [`JNIEnv::fatal_error`],
+ /// unless the error type is [`jni::errors::Error::JavaException`], in which case the error will
+ /// be ignored, allowing the already-thrown exception to remain in JVM.
+ ///
+ /// In typical usages, callers should return some default value immediately after calling this.
+ /// See the example in [`try_throwable`].
+ pub fn throw_on_jvm(self, env: &mut JNIEnv<'_>) {
+ match self {
+ ThrowableJniError::JavaThrowable(throwable) => match env.throw(throwable) {
+ Ok(()) => {}
+ Err(jni::errors::Error::JavaException) => {
+ let _ = env.exception_describe();
+ env.fatal_error("Throwing exception failed");
+ }
+ Err(_) => env.fatal_error("Throwing exception failed"),
+ },
+ ThrowableJniError::JavaException(exception) => match env.throw(exception) {
+ Ok(()) => {}
+ Err(jni::errors::Error::JavaException) => {
+ let _ = env.exception_describe();
+ env.fatal_error("Throwing exception failed");
+ }
+ Err(_) => env.fatal_error("Throwing exception failed"),
+ },
+ ThrowableJniError::JniError(err) => {
+ match err {
+ jni::errors::Error::JavaException => {
+ // Ignore the exception, it's already pending in the JVM
+ }
+ _ => env.fatal_error(format!("{err}")),
+ }
+ }
+ }
+ }
+}
+
+impl Debug for ThrowableJniError<'_> {
+ fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
+ match self {
+ Self::JavaThrowable(_) => f.debug_tuple("JavaThrowable").finish(),
+ Self::JavaException(arg0) => f
+ .debug_struct("JavaException")
+ .field("class", &arg0.class)
+ .field("msg", &arg0.msg)
+ .finish(),
+ Self::JniError(arg0) => f.debug_tuple("JniError").field(arg0).finish(),
+ }
+ }
+}
+
+impl<'local> From<JThrowable<'local>> for ThrowableJniError<'local> {
+ fn from(value: JThrowable<'local>) -> Self {
+ Self::JavaThrowable(value)
+ }
+}
+
+impl From<jni::errors::Error> for ThrowableJniError<'_> {
+ fn from(value: jni::errors::Error) -> Self {
+ Self::JniError(value)
+ }
+}
+
+impl From<jni::errors::Exception> for ThrowableJniError<'_> {
+ fn from(value: jni::errors::Exception) -> Self {
+ Self::JavaException(value)
+ }
+}
+
+/// Extension trait for `Result<T, ThrowableJniError>`.
+pub trait ThrowableJniResultExt<T> {
+ /// Returns the contained `Ok` value, or throw an exception on the JVM and return the default.
+ ///
+ /// Consumes the `self` argument then, if `Ok`, returns the contained value. Otherwise, if
+ /// `Err`, the error will be thrown on the JVM using [`ThrowableJniError::throw_on_jvm`] and a
+ /// default value will be returned. The value returned by `default` typically does not matter,
+ /// because while there is an exception pending on the JVM, the return value from your JNI
+ /// method is ignored.
+ fn unwrap_or_throw(self, env: &mut JNIEnv<'_>) -> T
+ where
+ T: Default,
+ Self: Sized,
+ {
+ self.unwrap_or_throw_with_default(env, Default::default)
+ }
+
+ /// Returns the contained `Ok` value, or throw an exception on the JVM and return some default.
+ ///
+ /// Consumes the `self` argument then, if `Ok`, returns the contained value. Otherwise, if
+ /// `Err`, the error will be thrown on the JVM using [`ThrowableJniError::throw_on_jvm`] and the
+ /// value from `default` will be returned. The value returned by `default` typically does not
+ /// matter, because while there is an exception pending on the JVM, the return value from your
+ /// JNI method is ignored.
+ fn unwrap_or_throw_with_default(self, env: &mut JNIEnv<'_>, default: impl FnOnce() -> T) -> T;
+}
+
+impl<'local, T> ThrowableJniResultExt<T> for Result<T, ThrowableJniError<'local>> {
+ fn unwrap_or_throw_with_default(self, env: &mut JNIEnv<'_>, default: impl FnOnce() -> T) -> T {
+ match self {
+ Ok(v) => v,
+ Err(e) => {
+ e.throw_on_jvm(env);
+ default()
+ }
+ }
+ }
+}
+
+/// Creates a `NullPointerException` with a default error message.
+pub fn null_pointer_exception() -> jni::errors::Exception {
+ jni::errors::Exception {
+ class: "java/lang/NullPointerException".into(),
+ msg: "Null pointer".into(),
+ }
+}
+
+/// Creates a runtime exception with the given message.
+pub fn runtime_exception(msg: impl ToString) -> jni::errors::Exception {
+ jni::errors::Exception {
+ class: "java/lang/RuntimeException".into(),
+ msg: msg.to_string(),
+ }
+}
+
+/// Runs the given `func` and turns any [`ThrowableJniError`] into Java exceptions.
+///
+/// A convenience function that takes a closure `func`, runs it immediately, and calls
+/// [`ThrowableJniResultExt::unwrap_or_throw`] on the result. This allows the code inside the
+/// closure to propagate [`ThrowableJniErrors`][ThrowableJniError] using the `?` operator.
+///
+/// # Example
+///
+/// ```
+/// use pourover::{jni_method, exception::{runtime_exception, try_throwable}};
+/// use jni::{JNIEnv, objects::{JByteArray, JClass}, sys::{jboolean, JNI_TRUE}};
+///
+/// #[jni_method(package = "com.example", class = "Foo")]
+/// extern "system" fn nativeMaybeThrowException<'local>(
+/// mut env: JNIEnv<'local>,
+/// _cls: JClass<'local>,
+/// value: JByteArray<'local>,
+/// ) -> jboolean {
+/// try_throwable(&mut env, |env| {
+/// let value = env.convert_byte_array(value)?;
+/// if value == b"Throw exception" {
+/// Err(runtime_exception("Argument was \"throw exception\""))?
+/// }
+/// Ok(JNI_TRUE)
+/// })
+/// }
+/// ```
+pub fn try_throwable<'local, R: Default>(
+ env: &mut JNIEnv<'local>,
+ func: impl FnOnce(&mut JNIEnv<'local>) -> Result<R, ThrowableJniError<'local>>,
+) -> R {
+ func(env).unwrap_or_throw(env)
+}
+
+/// Extension trait on `jni::errors::Result<T>`.
+pub trait JniResultExt<T> {
+ /// Extracts an exception of the given class from a [`jni::errors::Result`].
+ ///
+ /// If `self` is `Err(JavaException)` and the pending exception on the JVM matches the given
+ /// `desc`, the exception will be cleared on the JVM and the associated throwable will be
+ /// returned.
+ ///
+ /// # Returns
+ /// A nested result object. If `self` contains an exception matching the given `desc`, this will
+ /// return `Ok(Err(throwable))`. If `self` is `Ok`, this will return `Ok(Ok(value))`. Otherwise
+ /// if there are other errors or non-matching exceptions, the error will be propagated as
+ /// `Err(jni_error)`.
+ fn extract_exception<'local>(
+ self,
+ env: &mut JNIEnv<'local>,
+ desc: impl Desc<'local, JClass<'local>>,
+ ) -> jni::errors::Result<Result<T, JThrowable<'local>>>;
+}
+
+impl<T> JniResultExt<T> for jni::errors::Result<T> {
+ fn extract_exception<'local>(
+ self,
+ env: &mut JNIEnv<'local>,
+ desc: impl Desc<'local, JClass<'local>>,
+ ) -> jni::errors::Result<Result<T, JThrowable<'local>>> {
+ match self {
+ Ok(v) => Ok(Ok(v)),
+ Err(jni::errors::Error::JavaException) => {
+ let throwable = env.exception_occurred()?;
+ // Need to clear the exception ahead of time, otherwise `is_instance_of` will fail
+ env.exception_clear()?;
+ if env.is_instance_of(&throwable, desc)? {
+ Ok(Err(throwable))
+ } else {
+ env.throw(throwable)?;
+ Err(jni::errors::Error::JavaException)
+ }
+ }
+ Err(e) => Err(e),
+ }
+ }
+}
diff --git a/common/pourover/src/lib.rs b/common/pourover/src/lib.rs
index ed861ea..8cadc6e 100644
--- a/common/pourover/src/lib.rs
+++ b/common/pourover/src/lib.rs
@@ -12,11 +12,12 @@
// See the License for the specific language governing permissions and
// limitations under the License.
-//! Utilties for JNI interactions.
+//! Utilities for JNI interactions.
pub use pourover_macro::{call_constructor, call_method, call_static_method, jni_method};
pub mod desc;
+pub mod exception;
mod conversions;
pub use conversions::{ToSigned, ToUnsigned};
diff --git a/common/pourover/tests/common/foo_class.rs b/common/pourover/tests/common/foo_class.rs
index 3ded762..a0a59e4 100644
--- a/common/pourover/tests/common/foo_class.rs
+++ b/common/pourover/tests/common/foo_class.rs
@@ -22,8 +22,7 @@
//! [`jni::JNIEnv::define_class`].
use pourover::desc::*;
-use std::error::Error;
-use std::process::Command;
+use std::{error::Error, process::Command};
pub const CLASS_DESC: &str = "com/example/Foo";
pub static FOO: ClassDesc = ClassDesc::new(CLASS_DESC);
diff --git a/common/pourover/tests/exception_integration.rs b/common/pourover/tests/exception_integration.rs
new file mode 100644
index 0000000..129d2e6
--- /dev/null
+++ b/common/pourover/tests/exception_integration.rs
@@ -0,0 +1,176 @@
+// Copyright 2024 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(unsafe_code, clippy::unwrap_used, clippy::expect_used, clippy::panic)]
+
+use jni::{
+ descriptors::Desc,
+ objects::{JObject, JString},
+ sys::jint,
+ JNIEnv, JavaVM,
+};
+use pourover::{
+ desc::ClassDesc,
+ exception::{null_pointer_exception, runtime_exception, JniResultExt, ThrowableJniResultExt},
+};
+use std::error::Error;
+
+mod common;
+use common::foo_class::*;
+
+#[pourover::jni_method(package = "com.example", class = "Foo")]
+extern "system" fn nativeReturnsInt<'local>(
+ mut env: JNIEnv<'local>,
+ _this: JObject<'local>,
+ n: jint,
+) -> jint {
+ (|| match n {
+ -1 => Err(runtime_exception("test runtime exception"))?,
+ -2 => Err(null_pointer_exception())?,
+ _ => Ok(n),
+ })()
+ .unwrap_or_throw(&mut env)
+}
+
+#[pourover::jni_method(
+ package = "com.example",
+ class = "Foo",
+ panic_returns = JObject::null().into(),
+)]
+extern "system" fn nativeReturnsObject<'local>(
+ _env: JNIEnv<'local>,
+ _this: JObject<'local>,
+ _n: jint,
+) -> JString<'local> {
+ unimplemented!()
+}
+
+pub static RUNTIME_EXCEPTION_CLASS: ClassDesc = ClassDesc::new("java/lang/RuntimeException");
+
+#[test]
+fn can_call_native_method() -> Result<(), Box<dyn Error>> {
+ // Create the environment
+ let vm = JavaVM::new(
+ jni::InitArgsBuilder::new()
+ .version(jni::JNIVersion::V8)
+ .option("-Xcheck:jni")
+ .build()?,
+ )?;
+ let mut env = vm.attach_current_thread()?;
+
+ // Load `Foo.class`
+ {
+ let foo_class = compile_foo()?;
+ let loaded_foo = env.define_class(CLASS_DESC, &JObject::null(), &foo_class)?;
+ env.delete_local_ref(loaded_foo)?;
+ }
+
+ let obj_foo = {
+ let method_id = CONSTRUCTOR.lookup(&mut env)?;
+ let args = &[jni::sys::jvalue { i: 123 }];
+ // Safety: `args` must match the constructor arg count and types.
+ unsafe { env.new_object_unchecked(CONSTRUCTOR.cls(), method_id, args) }?
+ };
+ let obj_foo = env.auto_local(obj_foo);
+
+ // TODO: It would be better if the JVM was able to find the method on its own,
+ // but, since we are using the invocation API to create the JVM, I haven't found a way to make
+ // it visible to the JVM. Normally the methods are loaded dynamically with
+ // `System.loadLibrary`, but in this case the symbols already exist in the executable, and I'm
+ // unsure why the JVM cannot find them. I have tried compiling with
+ // `-Zexport-executable-symbols` but that wasn't working for me.
+ env.register_native_methods(
+ &FOO,
+ &[
+ jni::NativeMethod {
+ name: "nativeReturnsInt".into(),
+ sig: "(I)I".into(),
+ fn_ptr: crate::nativeReturnsInt as *mut std::ffi::c_void,
+ },
+ jni::NativeMethod {
+ name: "nativeReturnsObject".into(),
+ sig: "(I)Ljava/lang/String;".into(),
+ fn_ptr: crate::nativeReturnsObject as *mut std::ffi::c_void,
+ },
+ ],
+ )?;
+
+ // Sub-test 1: Call nativeReturnsInt(99) returns the argument without exceptions
+ {
+ let result =
+ pourover::call_method!(&mut env, FOO, "nativeReturnsInt", "(I)I", &obj_foo, 99);
+ assert_eq!(99, result.unwrap());
+ }
+
+ // Sub-test 2: Call nativeReturnsInt(99) returns the argument without exceptions, so
+ // extract_exception just returns Ok
+ {
+ let result =
+ pourover::call_method!(&mut env, FOO, "nativeReturnsInt", "(I)I", &obj_foo, 99);
+ let extracted_result = result.extract_exception(&mut env, "java/lang/Throwable");
+ let Ok(Ok(99)) = extracted_result else {
+ panic!("extracted_result should be Ok(Ok(99))")
+ };
+ }
+
+ // Sub-test 3: Call nativeReturnsInt(-1) throws RuntimeException("test runtime exception")
+ {
+ let result =
+ pourover::call_method!(&mut env, FOO, "nativeReturnsInt", "(I)I", &obj_foo, -1);
+ let throwable = result
+ .extract_exception(&mut env, "java/lang/RuntimeException")
+ .unwrap()
+ .unwrap_err();
+ let message = pourover::call_method!(
+ &mut env,
+ RUNTIME_EXCEPTION_CLASS,
+ "getMessage",
+ "()Ljava/lang/String;",
+ &throwable
+ )
+ .unwrap();
+ let message = env.get_string(&message).map(String::from).unwrap();
+ assert_eq!("test runtime exception", message);
+ }
+
+ // Sub-test 4: Call nativeReturnsInt(-2) throws NullPointerException()
+ {
+ let result =
+ pourover::call_method!(&mut env, FOO, "nativeReturnsInt", "(I)I", &obj_foo, -2);
+ let _ = result
+ .extract_exception(&mut env, "java/lang/NullPointerException")
+ .unwrap()
+ .unwrap_err();
+ }
+
+ // Sub-test 5: Call nativeReturnsInt(-1) throws RuntimeException(). Calling
+ // extract_exception with NullPointerException should return Err(JavaException).
+ {
+ let result =
+ pourover::call_method!(&mut env, FOO, "nativeReturnsInt", "(I)I", &obj_foo, -1);
+
+ let Err(jni::errors::Error::JavaException) =
+ result.extract_exception(&mut env, "java/lang/NullPointerException")
+ else {
+ panic!(concat!(
+ "Extracting NullPointerException from a result that contains RuntimeException ",
+ "should return Err(JavaException)"
+ ))
+ };
+ // The exception should still be pending on the JVM
+ assert!(env.exception_check().unwrap());
+ }
+
+ Ok(())
+}
diff --git a/common/pourover/tests/jni_method_integration.rs b/common/pourover/tests/jni_method_integration.rs
index 4bd6547..87cb80d 100644
--- a/common/pourover/tests/jni_method_integration.rs
+++ b/common/pourover/tests/jni_method_integration.rs
@@ -21,8 +21,10 @@
sys::jint,
JNIEnv, JavaVM,
};
-use std::error::Error;
-use std::sync::atomic::{AtomicBool, Ordering};
+use std::{
+ error::Error,
+ sync::atomic::{AtomicBool, Ordering},
+};
mod common;
use common::foo_class::*;
diff --git a/common/pourover_macro/Cargo.toml b/common/pourover_macro/Cargo.toml
index 0473dd4..13dc189 100644
--- a/common/pourover_macro/Cargo.toml
+++ b/common/pourover_macro/Cargo.toml
@@ -3,6 +3,7 @@
version.workspace = true
edition.workspace = true
publish.workspace = true
+license.workspace = true
[lints]
workspace = true
diff --git a/common/pourover_macro/src/lib.rs b/common/pourover_macro/src/lib.rs
index f46b7b1..9aeca11 100644
--- a/common/pourover_macro/src/lib.rs
+++ b/common/pourover_macro/src/lib.rs
@@ -15,27 +15,29 @@
//! Proc macros for `pourover`. These macros are reexported by the `pourover` crate, so this crate
//! is an implementation detail.
+#[cfg(proc_macro)]
use proc_macro::TokenStream;
mod call_method;
mod jni_method;
mod type_parser;
-/// Export a function as a JNI native method. This will attach a `#[export_name = "..."]` attribute that
-/// is formatted with the given parameters. The provided `package`, `class`, and `method_name` will
-/// be combined and formatted in according to the [JNI method name resolution rules][JNI naming].
+/// Export a function as a JNI native method. This will attach a `#[export_name = "..."]` attribute
+/// that is formatted with the given parameters. The provided `package`, `class`, and `method_name`
+/// will be combined and formatted in according to the [JNI method name resolution rules][JNI
+/// naming].
///
-/// [JNI naming]: https://docs.oracle.com/javase/8/docs/technotes/guides/jni/spec/design.html#resolving_native_method_names
+/// [JNI naming]:
+/// https://docs.oracle.com/javase/8/docs/technotes/guides/jni/spec/design.html#resolving_native_method_names
///
/// # Parameters
/// - `package` (LitStr): the Java package for the class being implemented
-/// - `class` (LitStr): the Java class being implemented. Use `Foo.Inner` syntax for inner
-/// classes.
+/// - `class` (LitStr): the Java class being implemented. Use `Foo.Inner` syntax for inner classes.
/// - `method_name` (*optional* LitStr): the method's name in Java. The Rust function name will be
-/// used if this parameter is not set.
+/// used if this parameter is not set.
/// - `panic_returns` (*optional* Expr): the value to return when a panic is encountered. This can
-/// not access local variables. This may only be used with `panic=unwind` and will produce a
-/// compile error otherwise.
+/// not access local variables. This may only be used with `panic=unwind` and will produce a
+/// compile error otherwise.
///
/// When using `panic_returns` function arguments must be [`std::panic::UnwindSafe`]. See
/// [`std::panic::catch_unwind`] for details. In practice this will not cause issues as JNI
@@ -57,6 +59,7 @@
/// ```
///
/// This function will be exported with `#[export_name = "Java_my_package_Foo_getFoo"]`.
+#[cfg(proc_macro)]
#[proc_macro_attribute]
pub fn jni_method(meta: TokenStream, item: TokenStream) -> TokenStream {
use quote::ToTokens;
@@ -75,14 +78,13 @@
/// - `cls` (Expr: `&'static ClassDesc`): The class containing the method.
/// - `name` (Expr: `&'static str`): The name of the method.
/// - `sig` (LitStr): The JNI type signature of the method. This needs to be a literal so that it
-/// can be parsed by the macro to type-check args and return a correctly-typed value.
+/// can be parsed by the macro to type-check args and return a correctly-typed value.
/// - `this` (Expr: `&JObject`): The Java object receiving the method call.
/// - `args` (Expr ...): A variable number of arguments to be passed to the method.
///
/// # Caching
-/// Each macro callsite will generate a `static` `MethodDesc` to cache the
-/// method id. Due to this, **this macro call should be wrapped in function** instead of being called
-/// multiple times.
+/// Each macro callsite will generate a `static` `MethodDesc` to cache the method id. Due to this,
+/// **this macro call should be wrapped in function** instead of being called multiple times.
///
/// # Type-Safety
/// The given type signature will be parsed and arguments will be type checked against it. The
@@ -94,8 +96,8 @@
/// Similarly, the return type will be one of the types above.
///
/// # Returns
-/// The macro will evaluate to `jni::errors::Result<R>` where `R` is the return type parsed from
-/// the type signature.
+/// The macro will evaluate to `jni::errors::Result<R>` where `R` is the return type parsed from the
+/// type signature.
///
/// # Example
/// Let's call `sayHello` from the following class.
@@ -119,6 +121,7 @@
/// call_method!(env, &MY_CLASS, "sayHello", "(Ljava/lang/String;)I", my_obj, name)
/// }
/// ```
+#[cfg(proc_macro)]
#[proc_macro]
pub fn call_method(args: TokenStream) -> TokenStream {
call_method::call_method(args.into())
@@ -134,13 +137,12 @@
/// - `cls` (Expr: `&'static ClassDesc`): The class containing the method.
/// - `name` (Expr: `&'static str`): The name of the method.
/// - `sig` (LitStr): The JNI type signature of the method. This needs to be a literal so that it
-/// can be parsed by the macro to type-check args and return a correctly-typed value.
+/// can be parsed by the macro to type-check args and return a correctly-typed value.
/// - `args` (Expr ...): A variable number of arguments to be passed to the method.
///
/// # Caching
-/// Each macro callsite will generate a `static` `StaticMethodDesc` to cache the
-/// method id. Due to this, **this macro call should be wrapped in function** instead of being called
-/// multiple times.
+/// Each macro callsite will generate a `static` `StaticMethodDesc` to cache the method id. Due to
+/// this, **this macro call should be wrapped in function** instead of being called multiple times.
///
/// # Type-Safety
/// The given type signature will be parsed and arguments will be type checked against it. The
@@ -152,8 +154,8 @@
/// Similarly, the return type will be one of the types above.
///
/// # Returns
-/// The macro will evaluate to `jni::errors::Result<R>` where `R` is the return type parsed from
-/// the type signature.
+/// The macro will evaluate to `jni::errors::Result<R>` where `R` is the return type parsed from the
+/// type signature.
///
/// # Example
/// Let's call `sayHello` from the following class.
@@ -176,6 +178,7 @@
/// call_static_method!(env, &MY_CLASS, "sayHello", "(Ljava/lang/String;)I", name)
/// }
/// ```
+#[cfg(proc_macro)]
#[proc_macro]
pub fn call_static_method(args: TokenStream) -> TokenStream {
call_method::call_static_method(args.into())
@@ -189,14 +192,13 @@
/// `call_constructor!($env, $cls, $sig, $($args),*)`
/// - `env` (Expr: `&mut jni::JNIEnv`): The JNI environment.
/// - `cls` (Expr: `&'static ClassDesc`): The class to be constructed.
-/// - `sig` (LitStr): The JNI type signature of the constructor. This needs to be a literal so that it
-/// can be parsed by the macro to type-check args and return a correctly-typed value.
+/// - `sig` (LitStr): The JNI type signature of the constructor. This needs to be a literal so that
+/// it can be parsed by the macro to type-check args and return a correctly-typed value.
/// - `args` (Expr ...): A variable number of arguments to be passed to the constructor.
///
/// # Caching
-/// Each macro callsite will generate a `static` `MethodDesc` to cache the
-/// method id. Due to this, **this macro call should be wrapped in function** instead of being called
-/// multiple times.
+/// Each macro callsite will generate a `static` `MethodDesc` to cache the method id. Due to this,
+/// **this macro call should be wrapped in function** instead of being called multiple times.
///
/// # Type-Safety
/// The given type signature will be parsed and arguments will be type checked against it. The
@@ -229,6 +231,7 @@
/// call_constructor!(env, &MY_CLASS, "(Ljava/lang/String;)V", name)
/// }
/// ```
+#[cfg(proc_macro)]
#[proc_macro]
pub fn call_constructor(args: TokenStream) -> TokenStream {
call_method::call_constructor(args.into())
diff --git a/common/pourover_macro/src/type_parser.rs b/common/pourover_macro/src/type_parser.rs
index e6834e2..9a120d2 100644
--- a/common/pourover_macro/src/type_parser.rs
+++ b/common/pourover_macro/src/type_parser.rs
@@ -90,7 +90,7 @@
}
}
-#[cfg(jni)]
+#[cfg(feature = "jni")]
impl<'a> From<JavaType<'a>> for jni::signature::ReturnType {
fn from(ty: JavaType<'a>) -> Self {
match ty {
@@ -221,7 +221,7 @@
}
}
-#[cfg(jni)]
+#[cfg(feature = "jni")]
impl From<Primitive> for jni::signature::Primitive {
fn from(p: Primitive) -> Self {
match p {
@@ -275,7 +275,7 @@
}
}
-#[cfg(jni)]
+#[cfg(feature = "jni")]
impl<'a> From<ReturnType<'a>> for jni::signature::ReturnType {
fn from(ty: ReturnType<'a>) -> Self {
match ty {
diff --git a/nearby/Cargo.lock b/nearby/Cargo.lock
index 6eb2999..982aed1 100644
--- a/nearby/Cargo.lock
+++ b/nearby/Cargo.lock
@@ -91,9 +91,9 @@
[[package]]
name = "anstream"
-version = "0.6.14"
+version = "0.6.15"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "418c75fa768af9c03be99d17643f93f79bbba589895012a80e3452a19ddda15b"
+checksum = "64e15c1ab1f89faffbf04a634d5e1962e9074f2741eef6d97f3c4e322426d526"
dependencies = [
"anstyle",
"anstyle-parse",
@@ -106,33 +106,33 @@
[[package]]
name = "anstyle"
-version = "1.0.7"
+version = "1.0.8"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "038dfcf04a5feb68e9c60b21c9625a54c2c0616e79b72b0fd87075a056ae1d1b"
+checksum = "1bec1de6f59aedf83baf9ff929c98f2ad654b97c9510f4e70cf6f661d49fd5b1"
[[package]]
name = "anstyle-parse"
-version = "0.2.4"
+version = "0.2.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "c03a11a9034d92058ceb6ee011ce58af4a9bf61491aa7e1e59ecd24bd40d22d4"
+checksum = "eb47de1e80c2b463c735db5b217a0ddc39d612e7ac9e2e96a5aed1f57616c1cb"
dependencies = [
"utf8parse",
]
[[package]]
name = "anstyle-query"
-version = "1.0.3"
+version = "1.1.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "a64c907d4e79225ac72e2a354c9ce84d50ebb4586dee56c82b3ee73004f537f5"
+checksum = "6d36fc52c7f6c869915e99412912f22093507da8d9e942ceaf66fe4b7c14422a"
dependencies = [
"windows-sys 0.52.0",
]
[[package]]
name = "anstyle-wincon"
-version = "3.0.3"
+version = "3.0.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "61a38449feb7068f52bb06c12759005cf459ee52bb4adc1d5a7c4322d716fb19"
+checksum = "5bf74e1b6e971609db8ca7a9ce79fd5768ab6ae46441c572e46cf596f59e57f8"
dependencies = [
"anstyle",
"windows-sys 0.52.0",
@@ -140,9 +140,9 @@
[[package]]
name = "anyhow"
-version = "1.0.83"
+version = "1.0.86"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "25bdb32cbbdce2b519a9cd7df3a678443100e265d5e25ca763b7572a5104f5f3"
+checksum = "b3d1d046238990b9cf5bcde22a3fb3584ee5cf65fb2765f454ed428c7a0063da"
[[package]]
name = "arbitrary"
@@ -204,9 +204,9 @@
[[package]]
name = "bitflags"
-version = "2.5.0"
+version = "2.6.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "cf4b9d6a944f767f8e5e0db018570623c85f3d925ac718db4e06d0187adb21c1"
+checksum = "b048fb63fd8b5923fc5aa7b340d8e156aec7ec02f0c78fa8a6ddc2613f6f71de"
[[package]]
name = "blake2"
@@ -248,21 +248,21 @@
[[package]]
name = "bstr"
-version = "1.9.1"
+version = "1.10.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "05efc5cfd9110c8416e471df0e96702d58690178e206e61b7173706673c93706"
+checksum = "40723b8fb387abc38f4f4a37c09073622e41dd12327033091ef8950659e6dc0c"
dependencies = [
"memchr",
"serde",
]
[[package]]
-name = "build-scripts"
+name = "build_scripts"
version = "0.1.0"
dependencies = [
"anyhow",
"chrono",
- "clap 4.5.4",
+ "clap 4.5.13",
"cmd_runner",
"crossbeam",
"env_logger 0.10.2",
@@ -295,9 +295,9 @@
[[package]]
name = "bytes"
-version = "1.6.0"
+version = "1.7.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "514de17de45fdb8dc022b1a7975556c53c86f9f0aa5f534b98977b171857c2c9"
+checksum = "8318a53db07bb3f8dca91a600466bdb3f2eaadeedfdbcf02e1accbad9271ba50"
[[package]]
name = "cast"
@@ -335,13 +335,12 @@
[[package]]
name = "cc"
-version = "1.0.97"
+version = "1.1.8"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "099a5357d84c4c61eb35fc8eafa9a79a902c2f76911e5747ced4e032edd8d9b4"
+checksum = "504bdec147f2cc13c8b57ed9401fd8a147cc66b67ad5cb241394244f2c947549"
dependencies = [
"jobserver",
"libc",
- "once_cell",
]
[[package]]
@@ -367,7 +366,7 @@
"js-sys",
"num-traits",
"wasm-bindgen",
- "windows-targets 0.52.5",
+ "windows-targets 0.52.6",
]
[[package]]
@@ -424,9 +423,9 @@
[[package]]
name = "clap"
-version = "4.5.4"
+version = "4.5.13"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "90bc066a67923782aa8515dbaea16946c5bcc5addbd668bb80af688e53e548a0"
+checksum = "0fbb260a053428790f3de475e304ff84cdbc4face759ea7a3e64c1edd938a7fc"
dependencies = [
"clap_builder",
"clap_derive",
@@ -434,26 +433,26 @@
[[package]]
name = "clap_builder"
-version = "4.5.2"
+version = "4.5.13"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "ae129e2e766ae0ec03484e609954119f123cc1fe650337e155d03b022f24f7b4"
+checksum = "64b17d7ea74e9f833c7dbf2cbe4fb12ff26783eda4782a8975b72f895c9b4d99"
dependencies = [
"anstream",
"anstyle",
- "clap_lex 0.7.0",
+ "clap_lex 0.7.2",
"strsim 0.11.1",
]
[[package]]
name = "clap_derive"
-version = "4.5.4"
+version = "4.5.13"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "528131438037fd55894f62d6e9f068b8f45ac57ffa77517819645d10aed04f64"
+checksum = "501d359d5f3dcaf6ecdeee48833ae73ec6e42723a1e52419c79abf9507eec0a0"
dependencies = [
"heck 0.5.0",
"proc-macro2",
"quote",
- "syn 2.0.61",
+ "syn 2.0.72",
]
[[package]]
@@ -467,9 +466,9 @@
[[package]]
name = "clap_lex"
-version = "0.7.0"
+version = "0.7.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "98cc8fbded0c607b7ba9dd60cd98df59af97e84d24e49c8557331cfc26d301ce"
+checksum = "1462739cb27611015575c0c11df5df7601141071f07518d56fcc1be504cbec97"
[[package]]
name = "cmd_runner"
@@ -477,20 +476,22 @@
dependencies = [
"anyhow",
"chrono",
- "clap 4.5.4",
+ "clap 4.5.13",
"file-header",
"globset",
"log",
"owo-colors",
+ "serde",
+ "serde_json",
"shell-escape",
"xshell",
]
[[package]]
name = "colorchoice"
-version = "1.0.1"
+version = "1.0.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "0b6a852b24ab71dffc585bcb46eaf7959d175cb865a7152e35b348d1b2960422"
+checksum = "d3fd119d74b830634cea2a0f58bbd0d54540518a14397557951e79340abc28c0"
[[package]]
name = "combine"
@@ -525,9 +526,9 @@
[[package]]
name = "crc32fast"
-version = "1.4.0"
+version = "1.4.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "b3855a8a784b474f333699ef2bbca9db2c4a1f6d9088a90a2d25b1eb53111eaa"
+checksum = "a97769d94ddab943e4510d138150169a2758b5ef3eb191a9ee688de3e23ef7b3"
dependencies = [
"cfg-if",
]
@@ -541,7 +542,7 @@
"anes",
"cast",
"ciborium",
- "clap 4.5.4",
+ "clap 4.5.13",
"criterion-plot",
"is-terminal",
"itertools 0.10.5",
@@ -583,9 +584,9 @@
[[package]]
name = "crossbeam-channel"
-version = "0.5.12"
+version = "0.5.13"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "ab3db02a9c5b5121e1e42fbdb1aeb65f5e02624cc58c43f2884c6ccac0b82f95"
+checksum = "33480d6946193aa8033910124896ca395333cae7e2d1113d1fef6c3272217df2"
dependencies = [
"crossbeam-utils",
]
@@ -620,9 +621,9 @@
[[package]]
name = "crossbeam-utils"
-version = "0.8.19"
+version = "0.8.20"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "248e3bacc7dc6baa3b21e405ee045c3047101a49145e7e9eca583ab4c2ca5345"
+checksum = "22ec99545bb0ed0ea7bb9b8e1e9122ea386ff8a48c0922e43f36d45ab09e0e80"
[[package]]
name = "crunchy"
@@ -754,16 +755,15 @@
[[package]]
name = "curve25519-dalek"
-version = "4.1.2"
+version = "4.1.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "0a677b8922c94e01bdbb12126b0bc852f00447528dee1782229af9c720c3f348"
+checksum = "97fb8b7c4503de7d6ae7b42ab72a5a59857b4c937ec27a3d4539dba95b5ab2be"
dependencies = [
"cfg-if",
"cpufeatures",
"curve25519-dalek-derive",
"digest",
"fiat-crypto",
- "platforms",
"rustc_version",
"subtle",
"zeroize",
@@ -777,7 +777,7 @@
dependencies = [
"proc-macro2",
"quote",
- "syn 2.0.61",
+ "syn 2.0.72",
]
[[package]]
@@ -798,25 +798,30 @@
dependencies = [
"proc-macro2",
"quote",
- "syn 2.0.61",
+ "syn 2.0.72",
]
[[package]]
name = "derive_fuzztest"
-version = "0.1.0"
+version = "0.1.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "98a03c381eaef21fd453d46a2c9be9f7527e588f113fdc63c3c4a11d7da8b473"
dependencies = [
"arbitrary",
"derive_fuzztest_macro",
+ "libfuzzer-sys",
"quickcheck",
]
[[package]]
name = "derive_fuzztest_macro"
-version = "0.1.0"
+version = "0.1.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "001301b8ebda15d520d9916f226711c0fa30be0bc8f055dd2379523bde1b4a32"
dependencies = [
"proc-macro2",
"quote",
- "syn 2.0.61",
+ "syn 2.0.72",
]
[[package]]
@@ -863,9 +868,9 @@
[[package]]
name = "either"
-version = "1.11.0"
+version = "1.13.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "a47c1c47d2f5964e29c61246e81db715514cd532db6b5116a25ea3c03d6780a2"
+checksum = "60b1af1c220855b6ceac025d3f6ecdd2b7c4894bfe9cd9bda4fbb4bc7c0d4cf0"
[[package]]
name = "elliptic-curve"
@@ -911,9 +916,9 @@
[[package]]
name = "errno"
-version = "0.3.8"
+version = "0.3.9"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "a258e46cdc063eb8519c00b9fc845fc47bcfca4130e2f08e88665ceda8474245"
+checksum = "534c5cf6194dfab3db3242765c03bbe257cf92f22b38f6bc0c58d59108a820ba"
dependencies = [
"libc",
"windows-sys 0.52.0",
@@ -937,9 +942,9 @@
[[package]]
name = "fiat-crypto"
-version = "0.2.8"
+version = "0.2.9"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "38793c55593b33412e3ae40c2c9781ffaa6f438f6f8c10f24e71846fbd7ae01e"
+checksum = "28dea519a9695b9977216879a3ebfddf92f1c08c05d984f8996aecd6ecdc811d"
[[package]]
name = "file-header"
@@ -956,9 +961,9 @@
[[package]]
name = "flate2"
-version = "1.0.30"
+version = "1.0.31"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "5f54427cfd1c7829e2a139fcefea601bf088ebca651d2bf53ebc600eac295dae"
+checksum = "7f211bbe8e69bbd0cfdea405084f128ae8b4aaa6b0b522fc8f2b009084797920"
dependencies = [
"crc32fast",
"miniz_oxide",
@@ -1040,6 +1045,7 @@
name = "handle_map"
version = "0.1.0"
dependencies = [
+ "lazy_static",
"lock_adapter",
]
@@ -1191,9 +1197,9 @@
[[package]]
name = "is_terminal_polyfill"
-version = "1.70.0"
+version = "1.70.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "f8478577c03552c21db0e2724ffb8986a5ce7af88107e6be5d2ee6e158c12800"
+checksum = "7943c866cc5cd64cbc25b2e01621d07fa8eb2a1a23160ee81ce38704e97b8ecf"
[[package]]
name = "itertools"
@@ -1206,9 +1212,9 @@
[[package]]
name = "itertools"
-version = "0.12.1"
+version = "0.13.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "ba291022dbbd398a455acf126c1e341954079855bc60dfdda641363bd6922569"
+checksum = "413ee7dfc52ee1a4949ceeb7dbc8a33f2d6c088194d9f922fb8318faf1f01186"
dependencies = [
"either",
]
@@ -1243,9 +1249,9 @@
[[package]]
name = "jobserver"
-version = "0.1.31"
+version = "0.1.32"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "d2b099aaa34a9751c5bf0878add70444e1ed2dd73f347be99003d4577277de6e"
+checksum = "48d1dbcbbeb6a7fec7e059840aa538bd62aaccf972c7346c4d9d2059312853d0"
dependencies = [
"libc",
]
@@ -1261,11 +1267,11 @@
[[package]]
name = "lazy_static"
-version = "1.4.0"
+version = "1.5.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646"
+checksum = "bbd2bcb4c963f2ddae06a2efc7e9f3591312473c50c6685e1f298068316e66fe"
dependencies = [
- "spin 0.5.2",
+ "spin",
]
[[package]]
@@ -1276,7 +1282,7 @@
"anyhow",
"base64",
"blake2",
- "clap 4.5.4",
+ "clap 4.5.13",
"criterion",
"crypto_provider",
"crypto_provider_default",
@@ -1357,7 +1363,7 @@
"ldt_np_adv",
"np_hkdf",
"rand",
- "spin 0.9.8",
+ "spin",
]
[[package]]
@@ -1382,9 +1388,9 @@
[[package]]
name = "libc"
-version = "0.2.154"
+version = "0.2.155"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "ae743338b92ff9146ce83992f766a31066a91a8c84a45e0e9f21e7cf6de6d346"
+checksum = "97b3888a4aecf77e811145cadf6eef5901f4782c53886191b2f693f24761847c"
[[package]]
name = "libfuzzer-sys"
@@ -1399,9 +1405,9 @@
[[package]]
name = "license"
-version = "3.3.1"
+version = "3.4.0+3.24.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "3bba2f02ee1d13cd4bea565658939cd851d70e391f34f7c27b45b2077df3a2e4"
+checksum = "a7da1e0d845faf299a9fe5f201a918a0dc0d5fc22c7b9580a6a23fed3a912b37"
dependencies = [
"reword",
"serde",
@@ -1410,15 +1416,15 @@
[[package]]
name = "linux-raw-sys"
-version = "0.4.13"
+version = "0.4.14"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "01cda141df6706de531b6c46c3a33ecca755538219bd484262fa09410c13539c"
+checksum = "78b3ae25bc7c8c38cec158d1f2757ee79e9b3740fbc7ccf0e59e4b08d793fa89"
[[package]]
name = "lock_adapter"
version = "0.1.0"
dependencies = [
- "spin 0.9.8",
+ "spin",
]
[[package]]
@@ -1433,15 +1439,15 @@
[[package]]
name = "log"
-version = "0.4.21"
+version = "0.4.22"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "90ed8c1e510134f979dbc4f070f87d4313098b704861a105fe34231c70a3901c"
+checksum = "a7a70ba024b9dc04c27ea2f0c0548feb474ec5c54bba33a7f72f873a39d07b24"
[[package]]
name = "memchr"
-version = "2.7.2"
+version = "2.7.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "6c8640c5d730cb13ebd907d8d04b52f55ac9a2eec55b440c8892f40d56c76c1d"
+checksum = "78ca9ab1a0babb1e7d5695e3530886289c18cf2f87ec19a575a0abdce112e3a3"
[[package]]
name = "minimal-lexical"
@@ -1451,9 +1457,9 @@
[[package]]
name = "miniz_oxide"
-version = "0.7.2"
+version = "0.7.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "9d811f3e15f28568be3407c8e7fdb6514c1cda3cb30683f15b6a1a1dc4ea14a7"
+checksum = "b8a240ddb74feaf34a79a7add65a741f3167852fba007066dcac1ca548d89c08"
dependencies = [
"adler",
]
@@ -1478,6 +1484,7 @@
"crypto_provider",
"crypto_provider_default",
"hex",
+ "itertools 0.13.0",
"lazy_static",
"ldt",
"ldt_np_adv",
@@ -1509,6 +1516,18 @@
]
[[package]]
+name = "np_adv_fuzz"
+version = "0.1.0"
+dependencies = [
+ "arbitrary",
+ "crypto_provider",
+ "crypto_provider_default",
+ "derive_fuzztest",
+ "libfuzzer-sys",
+ "np_adv",
+]
+
+[[package]]
name = "np_c_ffi"
version = "0.1.0"
dependencies = [
@@ -1567,6 +1586,7 @@
name = "np_java_ffi"
version = "0.1.0"
dependencies = [
+ "array_view",
"crypto_provider_default",
"handle_map",
"jni",
@@ -1578,9 +1598,9 @@
[[package]]
name = "num-bigint"
-version = "0.4.5"
+version = "0.4.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "c165a9ab64cf766f73521c0dd2cfdff64f488b8f0b3e621face3462d3db536d7"
+checksum = "a5e44f723f1133c9deac646763579fdb3ac745e418f2a7af9cd0c431da1f20b9"
dependencies = [
"num-integer",
"num-traits",
@@ -1612,9 +1632,9 @@
[[package]]
name = "oorandom"
-version = "11.1.3"
+version = "11.1.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "0ab1bc2a289d34bd04a330323ac98a1b4bc82c9d9fcb1e66b63caa84da26b575"
+checksum = "b410bbe7e14ab526a0e86877eb47c6996a2bd7746f027ba551028c925390e4e9"
[[package]]
name = "opaque-debug"
@@ -1655,16 +1675,10 @@
]
[[package]]
-name = "platforms"
-version = "3.4.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "db23d408679286588f4d4644f965003d056e3dd5abcaaa938116871d7ce2fee7"
-
-[[package]]
name = "plotters"
-version = "0.3.5"
+version = "0.3.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "d2c224ba00d7cadd4d5c660deaf2098e5e80e07846537c51f9cfa4be50c1fd45"
+checksum = "a15b6eccb8484002195a3e44fe65a4ce8e93a625797a063735536fd59cb01cf3"
dependencies = [
"num-traits",
"plotters-backend",
@@ -1675,15 +1689,15 @@
[[package]]
name = "plotters-backend"
-version = "0.3.5"
+version = "0.3.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "9e76628b4d3a7581389a35d5b6e2139607ad7c75b17aed325f210aa91f4a9609"
+checksum = "414cec62c6634ae900ea1c56128dfe87cf63e7caece0852ec76aba307cebadb7"
[[package]]
name = "plotters-svg"
-version = "0.3.5"
+version = "0.3.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "38f6d39893cca0701371e3c27294f09797214b86f1fb951b89ade8ec04e2abab"
+checksum = "81b30686a7d9c3e010b84284bdd26a29f2138574f52f5eb6f794fc0ad924e705"
dependencies = [
"plotters-backend",
]
@@ -1715,14 +1729,17 @@
"nom",
"proc-macro2",
"quote",
- "syn 2.0.61",
+ "syn 2.0.72",
]
[[package]]
name = "ppv-lite86"
-version = "0.2.17"
+version = "0.2.20"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "5b40af805b3121feab8a3c29f04d8ad262fa8e0561883e7653e024ae4479e6de"
+checksum = "77957b295656769bb8ad2b6a6b09d897d94f05c41b069aede1fcdaa675eaea04"
+dependencies = [
+ "zerocopy",
+]
[[package]]
name = "primeorder"
@@ -1735,9 +1752,9 @@
[[package]]
name = "proc-macro2"
-version = "1.0.82"
+version = "1.0.86"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "8ad3d49ab951a01fbaafe34f2ec74122942fe18a3f9814c3268f1bb72042131b"
+checksum = "5e719e8df665df0d1c8fbfd238015744736151d4445ec0836b8e628aae103b77"
dependencies = [
"unicode-ident",
]
@@ -1884,9 +1901,9 @@
[[package]]
name = "regex"
-version = "1.10.4"
+version = "1.10.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "c117dbdfde9c8308975b6a18d71f3f385c89461f7b3fb054288ecf2a2058ba4c"
+checksum = "4219d74c6b67a3654a9fbebc4b419e22126d13d2f3c4a07ee0cb61ff79a79619"
dependencies = [
"aho-corasick",
"memchr",
@@ -1896,9 +1913,9 @@
[[package]]
name = "regex-automata"
-version = "0.4.6"
+version = "0.4.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "86b83b8b9847f9bf95ef68afb0b8e6cdb80f498442f5179a29fad448fcc1eaea"
+checksum = "38caf58cc5ef2fed281f89292ef23f6365465ed9a41b7a7754eb4e26496c92df"
dependencies = [
"aho-corasick",
"memchr",
@@ -1907,9 +1924,9 @@
[[package]]
name = "regex-syntax"
-version = "0.8.3"
+version = "0.8.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "adad44e29e4c806119491a7f06f03de4d1af22c3a680dd47f1e6e179439d1f56"
+checksum = "7a66a03ae7c801facd77a29370b4faec201768915ac14a721ba36f20bc9c209b"
[[package]]
name = "relative-path"
@@ -1949,7 +1966,7 @@
"regex",
"relative-path",
"rustc_version",
- "syn 2.0.61",
+ "syn 2.0.72",
"unicode-ident",
]
@@ -1962,7 +1979,7 @@
"quote",
"rand",
"rustc_version",
- "syn 2.0.61",
+ "syn 2.0.72",
]
[[package]]
@@ -1980,7 +1997,7 @@
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "70dc5ec042f7a43c4a73241207cecc9873a06d45debb38b329f8541d85c2730f"
dependencies = [
- "bitflags 2.5.0",
+ "bitflags 2.6.0",
"errno",
"libc",
"linux-raw-sys",
@@ -1989,9 +2006,9 @@
[[package]]
name = "rustversion"
-version = "1.0.16"
+version = "1.0.17"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "092474d1a01ea8278f69e6a358998405fae5b8b963ddaeb2b0b04a128bf1dfb0"
+checksum = "955d28af4278de8121b7ebeb796b6a45735dc01436d898801014aced2773a3d6"
[[package]]
name = "ryu"
@@ -2035,31 +2052,32 @@
[[package]]
name = "serde"
-version = "1.0.200"
+version = "1.0.204"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "ddc6f9cc94d67c0e21aaf7eda3a010fd3af78ebf6e096aa6e2e13c79749cce4f"
+checksum = "bc76f558e0cbb2a839d37354c575f1dc3fdc6546b5be373ba43d95f231bf7c12"
dependencies = [
"serde_derive",
]
[[package]]
name = "serde_derive"
-version = "1.0.200"
+version = "1.0.204"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "856f046b9400cee3c8c94ed572ecdb752444c24528c035cd35882aad6f492bcb"
+checksum = "e0cd7e117be63d3c3678776753929474f3b04a43a080c744d6b0ae2a8c28e222"
dependencies = [
"proc-macro2",
"quote",
- "syn 2.0.61",
+ "syn 2.0.72",
]
[[package]]
name = "serde_json"
-version = "1.0.116"
+version = "1.0.122"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "3e17db7126d17feb94eb3fad46bf1a96b034e8aacbc2e775fe81505f8b0b2813"
+checksum = "784b6203951c57ff748476b126ccb5e8e2959a5c19e5c617ab1956be3dbc68da"
dependencies = [
"itoa",
+ "memchr",
"ryu",
"serde",
]
@@ -2099,12 +2117,6 @@
[[package]]
name = "spin"
-version = "0.5.2"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "6e63cff320ae2c57904679ba7cb63280a3dc4613885beafb148ee7bf9aa9042d"
-
-[[package]]
-name = "spin"
version = "0.9.8"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6980e8d7511241f8acf4aebddbb1ff938df5eebe98691418c4468d0b72a96a67"
@@ -2150,14 +2162,14 @@
"proc-macro2",
"quote",
"rustversion",
- "syn 2.0.61",
+ "syn 2.0.72",
]
[[package]]
name = "subtle"
-version = "2.5.0"
+version = "2.6.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "81cdd64d312baedb58e21336b31bc043b77e01cc99033ce76ef539f78e965ebc"
+checksum = "13c2bddecc57b384dee18652358fb23172facb8a2c51ccc10d74c157bdea3292"
[[package]]
name = "syn"
@@ -2172,9 +2184,9 @@
[[package]]
name = "syn"
-version = "2.0.61"
+version = "2.0.72"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "c993ed8ccba56ae856363b1845da7266a7cb78e1d146c8a32d54b45a8b831fc9"
+checksum = "dc4b9b9bf2add8093d3f2c0204471e951b2285580335de42f9d2534f3ae7a8af"
dependencies = [
"proc-macro2",
"quote",
@@ -2183,14 +2195,15 @@
[[package]]
name = "tempfile"
-version = "3.10.1"
+version = "3.12.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "85b77fafb263dd9d05cbeac119526425676db3784113aa9295c88498cbf8bff1"
+checksum = "04cbcdd0c794ebb0d4cf35e88edd2f7d2c4c3e9a5a6dab322839b321c6a87a64"
dependencies = [
"cfg-if",
"fastrand",
+ "once_cell",
"rustix",
- "windows-sys 0.52.0",
+ "windows-sys 0.59.0",
]
[[package]]
@@ -2207,7 +2220,7 @@
version = "0.1.0"
dependencies = [
"hex",
- "itertools 0.12.1",
+ "itertools 0.13.0",
"serde_json",
]
@@ -2227,22 +2240,22 @@
[[package]]
name = "thiserror"
-version = "1.0.60"
+version = "1.0.63"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "579e9083ca58dd9dcf91a9923bb9054071b9ebbd800b342194c9feb0ee89fc18"
+checksum = "c0342370b38b6a11b6cc11d6a805569958d54cfa061a29969c3b5ce2ea405724"
dependencies = [
"thiserror-impl",
]
[[package]]
name = "thiserror-impl"
-version = "1.0.60"
+version = "1.0.63"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "e2470041c06ec3ac1ab38d0356a6119054dedaea53e12fbefc0de730a1c08524"
+checksum = "a4558b58466b9ad7ca0f102865eccc95938dca1a74a856f2b57b6629050da261"
dependencies = [
"proc-macro2",
"quote",
- "syn 2.0.61",
+ "syn 2.0.72",
]
[[package]]
@@ -2257,9 +2270,9 @@
[[package]]
name = "tinyvec"
-version = "1.6.0"
+version = "1.8.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "87cc5ceb3875bb20c2890005a4e226a4651264a5c75edb2421b52861a0a0cb50"
+checksum = "445e881f4f6d382d5f27c034e25eb92edd7c784ceab92a0937db7f2e9471b938"
[[package]]
name = "toml"
@@ -2359,7 +2372,7 @@
name = "ukey2_shell"
version = "0.1.0"
dependencies = [
- "clap 4.5.4",
+ "clap 4.5.13",
"crypto_provider_rustcrypto",
"ukey2_connections",
]
@@ -2388,15 +2401,15 @@
[[package]]
name = "utf8parse"
-version = "0.2.1"
+version = "0.2.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "711b9620af191e0cdc7468a8d14e709c3dcdb115b36f838e601583af800a370a"
+checksum = "06abde3611657adf66d383f00b093d7faecc7fa57071cce2578660c9f1010821"
[[package]]
name = "version_check"
-version = "0.9.4"
+version = "0.9.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f"
+checksum = "0b928f33d975fc6ad9f86c8f283853ad26bdd5b10b7f1542aa2fa15e2289105a"
[[package]]
name = "walkdir"
@@ -2435,7 +2448,7 @@
"once_cell",
"proc-macro2",
"quote",
- "syn 2.0.61",
+ "syn 2.0.72",
"wasm-bindgen-shared",
]
@@ -2457,7 +2470,7 @@
dependencies = [
"proc-macro2",
"quote",
- "syn 2.0.61",
+ "syn 2.0.72",
"wasm-bindgen-backend",
"wasm-bindgen-shared",
]
@@ -2508,11 +2521,11 @@
[[package]]
name = "winapi-util"
-version = "0.1.8"
+version = "0.1.9"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "4d4cc384e1e73b93bafa6fb4f1df8c41695c8a91cf9c4c64358067d15a7b6c6b"
+checksum = "cf221c93e13a30d793f7645a0e7762c55d169dbb0a49671918a2319d289b10bb"
dependencies = [
- "windows-sys 0.52.0",
+ "windows-sys 0.59.0",
]
[[package]]
@@ -2527,7 +2540,7 @@
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "33ab640c8d7e35bf8ba19b884ba838ceb4fba93a4e8c65a9059d08afcfc683d9"
dependencies = [
- "windows-targets 0.52.5",
+ "windows-targets 0.52.6",
]
[[package]]
@@ -2545,7 +2558,16 @@
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "282be5f36a8ce781fad8c8ae18fa3f9beff57ec1b52cb3de0789201425d9a33d"
dependencies = [
- "windows-targets 0.52.5",
+ "windows-targets 0.52.6",
+]
+
+[[package]]
+name = "windows-sys"
+version = "0.59.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "1e38bc4d79ed67fd075bcc251a1c39b32a1776bbe92e5bef1f0bf1f8c531853b"
+dependencies = [
+ "windows-targets 0.52.6",
]
[[package]]
@@ -2565,18 +2587,18 @@
[[package]]
name = "windows-targets"
-version = "0.52.5"
+version = "0.52.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "6f0713a46559409d202e70e28227288446bf7841d3211583a4b53e3f6d96e7eb"
+checksum = "9b724f72796e036ab90c1021d4780d4d3d648aca59e491e6b98e725b84e99973"
dependencies = [
- "windows_aarch64_gnullvm 0.52.5",
- "windows_aarch64_msvc 0.52.5",
- "windows_i686_gnu 0.52.5",
+ "windows_aarch64_gnullvm 0.52.6",
+ "windows_aarch64_msvc 0.52.6",
+ "windows_i686_gnu 0.52.6",
"windows_i686_gnullvm",
- "windows_i686_msvc 0.52.5",
- "windows_x86_64_gnu 0.52.5",
- "windows_x86_64_gnullvm 0.52.5",
- "windows_x86_64_msvc 0.52.5",
+ "windows_i686_msvc 0.52.6",
+ "windows_x86_64_gnu 0.52.6",
+ "windows_x86_64_gnullvm 0.52.6",
+ "windows_x86_64_msvc 0.52.6",
]
[[package]]
@@ -2587,9 +2609,9 @@
[[package]]
name = "windows_aarch64_gnullvm"
-version = "0.52.5"
+version = "0.52.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "7088eed71e8b8dda258ecc8bac5fb1153c5cffaf2578fc8ff5d61e23578d3263"
+checksum = "32a4622180e7a0ec044bb555404c800bc9fd9ec262ec147edd5989ccd0c02cd3"
[[package]]
name = "windows_aarch64_msvc"
@@ -2599,9 +2621,9 @@
[[package]]
name = "windows_aarch64_msvc"
-version = "0.52.5"
+version = "0.52.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "9985fd1504e250c615ca5f281c3f7a6da76213ebd5ccc9561496568a2752afb6"
+checksum = "09ec2a7bb152e2252b53fa7803150007879548bc709c039df7627cabbd05d469"
[[package]]
name = "windows_i686_gnu"
@@ -2611,15 +2633,15 @@
[[package]]
name = "windows_i686_gnu"
-version = "0.52.5"
+version = "0.52.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "88ba073cf16d5372720ec942a8ccbf61626074c6d4dd2e745299726ce8b89670"
+checksum = "8e9b5ad5ab802e97eb8e295ac6720e509ee4c243f69d781394014ebfe8bbfa0b"
[[package]]
name = "windows_i686_gnullvm"
-version = "0.52.5"
+version = "0.52.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "87f4261229030a858f36b459e748ae97545d6f1ec60e5e0d6a3d32e0dc232ee9"
+checksum = "0eee52d38c090b3caa76c563b86c3a4bd71ef1a819287c19d586d7334ae8ed66"
[[package]]
name = "windows_i686_msvc"
@@ -2629,9 +2651,9 @@
[[package]]
name = "windows_i686_msvc"
-version = "0.52.5"
+version = "0.52.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "db3c2bf3d13d5b658be73463284eaf12830ac9a26a90c717b7f771dfe97487bf"
+checksum = "240948bc05c5e7c6dabba28bf89d89ffce3e303022809e73deaefe4f6ec56c66"
[[package]]
name = "windows_x86_64_gnu"
@@ -2641,9 +2663,9 @@
[[package]]
name = "windows_x86_64_gnu"
-version = "0.52.5"
+version = "0.52.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "4e4246f76bdeff09eb48875a0fd3e2af6aada79d409d33011886d3e1581517d9"
+checksum = "147a5c80aabfbf0c7d901cb5895d1de30ef2907eb21fbbab29ca94c5b08b1a78"
[[package]]
name = "windows_x86_64_gnullvm"
@@ -2653,9 +2675,9 @@
[[package]]
name = "windows_x86_64_gnullvm"
-version = "0.52.5"
+version = "0.52.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "852298e482cd67c356ddd9570386e2862b5673c85bd5f88df9ab6802b334c596"
+checksum = "24d5b23dc417412679681396f2b49f3de8c1473deb516bd34410872eff51ed0d"
[[package]]
name = "windows_x86_64_msvc"
@@ -2665,9 +2687,9 @@
[[package]]
name = "windows_x86_64_msvc"
-version = "0.52.5"
+version = "0.52.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "bec47e5bfd1bff0eeaf6d8b485cc1074891a197ab4225d504cb7a1ab88b02bf0"
+checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec"
[[package]]
name = "wycheproof"
@@ -2751,7 +2773,28 @@
]
[[package]]
-name = "zeroize"
-version = "1.7.0"
+name = "zerocopy"
+version = "0.7.35"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "525b4ec142c6b68a2d10f01f7bbf6755599ca3f81ea53b8431b7dd348f5fdb2d"
+checksum = "1b9b4fd18abc82b8136838da5d50bae7bdea537c574d8dc1a34ed098d6c166f0"
+dependencies = [
+ "byteorder",
+ "zerocopy-derive",
+]
+
+[[package]]
+name = "zerocopy-derive"
+version = "0.7.35"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "fa4f8080344d4671fb4e831a13ad1e68092748387dfc4f55e356242fae12ce3e"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "syn 2.0.72",
+]
+
+[[package]]
+name = "zeroize"
+version = "1.8.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ced3678a2879b30306d323f4542626697a464a97c0a07c9aebf7ebca65cd4dde"
diff --git a/nearby/Cargo.toml b/nearby/Cargo.toml
index f69bcf7..11aecd8 100644
--- a/nearby/Cargo.toml
+++ b/nearby/Cargo.toml
@@ -1,5 +1,6 @@
[workspace]
members = [
+ "build_scripts",
"connections/ukey2/ukey2",
"connections/ukey2/ukey2_connections",
"connections/ukey2/ukey2_connections/fuzz",
@@ -22,6 +23,7 @@
"presence/ldt_np_jni",
"presence/ldt_tbc",
"presence/np_adv",
+ "presence/np_adv/fuzz",
"presence/np_adv_dynamic",
"presence/np_c_ffi",
"presence/np_ed25519",
@@ -35,11 +37,13 @@
"presence/xts_aes",
"presence/xts_aes/fuzz",
]
+default-members = ["build_scripts"]
# TODO: remove boringssl once we figure out a better plan for integrating the build system
exclude = [
"crypto/crypto_provider_boringssl",
]
+resolver = "2"
[workspace.lints.rust]
missing_docs = "deny"
@@ -50,6 +54,7 @@
unused_extern_crates = "deny"
unused_import_braces = "deny"
unused_results = "deny"
+unexpected_cfgs = { level = "warn", check-cfg = ['cfg(fuzzing)', 'cfg(blaze)'] }
[workspace.lints.clippy]
expect_used = "deny"
@@ -80,14 +85,12 @@
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", default-features=false }
+np_ffi_core = { path = "presence/np_ffi_core", default-features = false }
np_java_ffi = { path = "presence/np_java_ffi" }
sink = { path = "presence/sink" }
test_vector_hkdf = { path = "presence/test_vector_hkdf" }
# from utils workspace
-derive_fuzztest = { path = "../common/derive_fuzztest" }
-derive_fuzztest_macro = { path = "../common/derive_fuzztest_macro" }
handle_map = { path = "../common/handle_map" }
lock_adapter = { path = "../common/lock_adapter" }
pourover = { path = "../common/pourover" }
@@ -129,7 +132,7 @@
log = "0.4.20"
env_logger = "0.10.1"
criterion = { version = "0.5.1", features = ["html_reports"] }
-clap = { version = "4.4.11", features = ["derive"] }
+clap = { version = "4.5.13", features = ["derive"] }
lazy_static = { version = "1.4.0", features = ["spin_no_std"] }
hex-literal = "0.4.1"
cfg-if = "1.0.0"
@@ -154,16 +157,19 @@
syn = { version = "2.0", features = ["full"] }
proc-macro2 = "1.0"
quote = "1.0"
-itertools = "0.12.1"
+itertools = { version = "0.13.0", default-features = false }
quickcheck = "1.0.3"
proptest = "1.4.0"
proptest-arbitrary-interop = { git = "https://github.com/brson/proptest-arbitrary-interop.git", branch = "incorrect-format" }
libfuzzer-sys = "0.4.7"
+derive_fuzztest = "0.1.1"
+derive_fuzztest_macro = "0.1.1"
[workspace.package]
version = "0.1.0"
edition = "2021"
publish = false
+license = "Apache-2.0"
[profile.test]
# speed up test execution
@@ -183,33 +189,3 @@
# z optimizes for size
opt-level = "z"
strip = true
-
-[package]
-name = "build-scripts"
-version.workspace = true
-edition.workspace = true
-publish.workspace = true
-rust-version = "1.71.0"
-
-[dependencies]
-clap.workspace = true
-cmd_runner = { path = "../common/cmd_runner" }
-anyhow.workspace = true
-shell-escape = "0.1.5"
-owo-colors.workspace = true
-semver = "1.0.17"
-walkdir = "2.3.3"
-globset = "0.4.10"
-glob = "0.3.1"
-crossbeam = "0.8.2"
-chrono.workspace = true
-thiserror.workspace = true
-log.workspace = true
-env_logger.workspace = true
-file-header = "0.1.2"
-serde_json = { workspace = true, features = ["std"] }
-regex = "1.10.2"
-xshell = "0.2.6"
-
-[dev-dependencies]
-tempfile.workspace = true
diff --git a/nearby/build_scripts/Cargo.toml b/nearby/build_scripts/Cargo.toml
new file mode 100644
index 0000000..5349e9c
--- /dev/null
+++ b/nearby/build_scripts/Cargo.toml
@@ -0,0 +1,30 @@
+[package]
+name = "build_scripts"
+version.workspace = true
+edition.workspace = true
+publish.workspace = true
+license.workspace = true
+rust-version = "1.71.0"
+
+[dependencies]
+clap.workspace = true
+cmd_runner = { path = "../../common/cmd_runner" }
+anyhow.workspace = true
+shell-escape = "0.1.5"
+owo-colors.workspace = true
+semver = "1.0.17"
+walkdir = "2.3.3"
+globset = "0.4.10"
+glob = "0.3.1"
+crossbeam = "0.8.2"
+chrono.workspace = true
+thiserror.workspace = true
+log.workspace = true
+env_logger.workspace = true
+file-header = "0.1.2"
+serde_json = { workspace = true, features = ["std"] }
+regex = "1.10.2"
+xshell = "0.2.6"
+
+[dev-dependencies]
+tempfile.workspace = true
diff --git a/nearby/src/coverage.rs b/nearby/build_scripts/src/coverage.rs
similarity index 100%
rename from nearby/src/coverage.rs
rename to nearby/build_scripts/src/coverage.rs
diff --git a/nearby/src/crypto_ffi.rs b/nearby/build_scripts/src/crypto_ffi.rs
similarity index 100%
rename from nearby/src/crypto_ffi.rs
rename to nearby/build_scripts/src/crypto_ffi.rs
diff --git a/nearby/src/ffi.rs b/nearby/build_scripts/src/ffi.rs
similarity index 100%
rename from nearby/src/ffi.rs
rename to nearby/build_scripts/src/ffi.rs
diff --git a/nearby/build_scripts/src/fuzzers.rs b/nearby/build_scripts/src/fuzzers.rs
new file mode 100644
index 0000000..c2caf29
--- /dev/null
+++ b/nearby/build_scripts/src/fuzzers.rs
@@ -0,0 +1,56 @@
+// Copyright 2023 Google LLC
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+use cmd_runner::{run_cmd_shell, run_cmd_shell_with_color, YellowStderr};
+use std::{fs, path};
+
+pub(crate) fn run_rust_fuzzers(root: &path::Path) -> anyhow::Result<()> {
+ cmd_runner::fuzzers::run_workspace_fuzz_targets(root)?;
+ run_cmd_shell_with_color::<YellowStderr>(
+ &root.join("crypto/crypto_provider_test"),
+ concat!(
+ "cargo +nightly fuzz run fuzz_p256 --features=boringssl --no-default-features ",
+ "-- -runs=1000000 -max_total_time=30"
+ ),
+ )?;
+
+ Ok(())
+}
+
+// Runs the fuzztest fuzzers as short lived unit tests, compatible with gtest
+pub(crate) fn build_fuzztest_unit_tests(root: &path::Path) -> anyhow::Result<()> {
+ log::info!("Checking fuzztest targets in unit test mode");
+ run_cmd_shell(root, "cargo build -p np_c_ffi --release")?;
+ run_cmd_shell(root, "cargo build -p ldt_np_adv_ffi --release")?;
+ let build_dir = root.join("cmake-build");
+ fs::create_dir_all(&build_dir)?;
+ run_cmd_shell_with_color::<YellowStderr>(&build_dir, "cmake -G Ninja -DENABLE_FUZZ=true ..")?;
+
+ for target in ["deserialization_fuzzer", "ldt_fuzzer"] {
+ run_cmd_shell_with_color::<YellowStderr>(
+ &build_dir,
+ format!("cmake --build . --target {}", target),
+ )?;
+ }
+
+ run_cmd_shell_with_color::<YellowStderr>(
+ &build_dir.join("presence/np_cpp_ffi/fuzz/"),
+ "ctest",
+ )?;
+ run_cmd_shell_with_color::<YellowStderr>(
+ &build_dir.join("presence/ldt_np_adv_ffi/c/fuzz/"),
+ "ctest",
+ )?;
+ Ok(())
+}
diff --git a/nearby/src/jni.rs b/nearby/build_scripts/src/jni.rs
similarity index 91%
rename from nearby/src/jni.rs
rename to nearby/build_scripts/src/jni.rs
index 4ef3590..ddea7d2 100644
--- a/nearby/src/jni.rs
+++ b/nearby/build_scripts/src/jni.rs
@@ -30,7 +30,10 @@
}
pub fn run_np_java_ffi_tests(root: &path::Path) -> anyhow::Result<()> {
- run_cmd_shell(root, "cargo build -p np_java_ffi -F crypto_provider_default/rustcrypto")?;
+ run_cmd_shell(
+ root,
+ "cargo build -p np_java_ffi -F crypto_provider_default/rustcrypto -F testing",
+ )?;
let np_java_path = root.to_path_buf().join("presence/np_java_ffi");
run_cmd_shell(&np_java_path, "./gradlew :test --info --rerun")?;
Ok(())
diff --git a/nearby/src/license.rs b/nearby/build_scripts/src/license.rs
similarity index 100%
rename from nearby/src/license.rs
rename to nearby/build_scripts/src/license.rs
diff --git a/nearby/src/main.rs b/nearby/build_scripts/src/main.rs
similarity index 91%
rename from nearby/src/main.rs
rename to nearby/build_scripts/src/main.rs
index 6fe4263..63ab0e9 100644
--- a/nearby/src/main.rs
+++ b/nearby/build_scripts/src/main.rs
@@ -19,6 +19,7 @@
use cmd_runner::{license_checker::LicenseSubcommand, run_cmd, run_cmd_shell, YellowStderr};
use env_logger::Env;
use license::LICENSE_CHECKER;
+use log::info;
use std::{env, ffi::OsString, path};
mod crypto_ffi;
@@ -31,13 +32,15 @@
env_logger::Builder::from_env(Env::default().default_filter_or("info")).init();
let cli: Cli = Cli::parse();
- let root_dir = path::PathBuf::from(
+ let build_script_dir = path::PathBuf::from(
env::var("CARGO_MANIFEST_DIR").expect("Must be run via Cargo to establish root directory"),
);
+ let root_dir =
+ build_script_dir.parent().expect("build_scripts crate directory should have a parent");
match cli.subcommand {
Subcommand::RunDefaultChecks(ref check_options) => {
- run_default_checks(&root_dir, check_options)?;
+ run_default_checks(root_dir, check_options)?;
print!(concat!(
"Congratulations, the default checks passed. Since you like quality, here are\n",
"some more checks you may like:\n",
@@ -45,33 +48,31 @@
" cargo run -- check-stack-usage\n",
));
}
- Subcommand::VerifyCi { ref check_options } => verify_ci(&root_dir, check_options)?,
- Subcommand::RunAllChecks { ref check_options } => run_all_checks(&root_dir, check_options)?,
- Subcommand::CleanEverything => clean_everything(&root_dir)?,
- Subcommand::CheckFormat(ref options) => check_format(&root_dir, options)?,
- Subcommand::CheckWorkspace(ref options) => check_workspace(&root_dir, options)?,
- Subcommand::CheckAllFfi(ref options) => ffi::check_all_ffi(&root_dir, options, false)?,
- Subcommand::BazelBuild => bazel_build(&root_dir)?,
- Subcommand::BuildBoringssl => crypto_ffi::build_boringssl(&root_dir)?,
- Subcommand::CheckBoringssl(ref options) => crypto_ffi::check_boringssl(&root_dir, options)?,
+ Subcommand::VerifyCi { ref check_options } => verify_ci(root_dir, check_options)?,
+ Subcommand::RunAllChecks { ref check_options } => run_all_checks(root_dir, check_options)?,
+ Subcommand::CleanEverything => clean_everything(root_dir)?,
+ Subcommand::CheckFormat(ref options) => check_format(root_dir, options)?,
+ Subcommand::CheckWorkspace(ref options) => check_workspace(root_dir, options)?,
+ Subcommand::CheckAllFfi(ref options) => ffi::check_all_ffi(root_dir, options, false)?,
+ Subcommand::BazelBuild => bazel_build(root_dir)?,
+ Subcommand::BuildBoringssl => crypto_ffi::build_boringssl(root_dir)?,
+ Subcommand::CheckBoringssl(ref options) => crypto_ffi::check_boringssl(root_dir, options)?,
Subcommand::CheckBoringsslAtLatest(ref options) => {
- crypto_ffi::check_boringssl_at_head(&root_dir, options)?
+ crypto_ffi::check_boringssl_at_head(root_dir, options)?
}
- Subcommand::RunRustFuzzers => fuzzers::run_rust_fuzzers(&root_dir)?,
- Subcommand::CheckFuzztest => fuzzers::build_fuzztest_unit_tests(&root_dir)?,
+ Subcommand::RunRustFuzzers => fuzzers::run_rust_fuzzers(root_dir)?,
+ Subcommand::CheckFuzztest => fuzzers::build_fuzztest_unit_tests(root_dir)?,
Subcommand::License(license_subcommand) => {
- license_subcommand.run(&LICENSE_CHECKER, &root_dir)?
+ license_subcommand.run(&LICENSE_CHECKER, root_dir)?
}
- Subcommand::CheckUkey2Ffi(ref options) => {
- ffi::check_ukey2_cmake(&root_dir, options, false)?
- }
- Subcommand::RunUkey2JniTests => jni::run_ukey2_jni_tests(&root_dir)?,
- Subcommand::RunNpJavaFfiTests => jni::run_np_java_ffi_tests(&root_dir)?,
- Subcommand::CheckLdtCmake(ref options) => ffi::check_ldt_cmake(&root_dir, options, false)?,
+ Subcommand::CheckUkey2Ffi(ref options) => ffi::check_ukey2_cmake(root_dir, options, false)?,
+ Subcommand::RunUkey2JniTests => jni::run_ukey2_jni_tests(root_dir)?,
+ Subcommand::RunNpJavaFfiTests => jni::run_np_java_ffi_tests(root_dir)?,
+ Subcommand::CheckLdtCmake(ref options) => ffi::check_ldt_cmake(root_dir, options, false)?,
Subcommand::CheckNpFfiCmake(ref options) => {
- ffi::check_np_ffi_cmake(&root_dir, options, false)?
+ ffi::check_np_ffi_cmake(root_dir, options, false)?
}
- Subcommand::RunLdtKotlinTests => jni::run_ldt_kotlin_tests(&root_dir)?,
+ Subcommand::RunLdtKotlinTests => jni::run_ldt_kotlin_tests(root_dir)?,
}
Ok(())
@@ -133,7 +134,7 @@
}
pub fn check_workspace(root: &path::Path, options: &CheckOptions) -> anyhow::Result<()> {
- log::info!("Running cargo checks on workspace");
+ info!("Running cargo checks on workspace");
// ensure formatting is correct (Check for it first because it is fast compared to running tests)
check_format(root, &options.formatter_options)?;
diff --git a/common/derive_fuzztest/fuzz/src/bin/integer_add.rs b/nearby/build_scripts/src/no_std.rs
similarity index 70%
copy from common/derive_fuzztest/fuzz/src/bin/integer_add.rs
copy to nearby/build_scripts/src/no_std.rs
index 38b8172..6258260 100644
--- a/common/derive_fuzztest/fuzz/src/bin/integer_add.rs
+++ b/nearby/build_scripts/src/no_std.rs
@@ -1,4 +1,4 @@
-// Copyright 2024 Google LLC
+// 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.
@@ -12,12 +12,3 @@
// See the License for the specific language governing permissions and
// limitations under the License.
-#![cfg_attr(fuzzing, no_main)]
-
-use derive_fuzztest::fuzztest;
-
-#[fuzztest]
-pub fn test(a: u8, b: u8) {
- let _ = a.checked_add(b);
- // a + b; // This fails because a + b can overflow.
-}
diff --git a/common/derive_fuzztest/fuzz/src/bin/integer_add.rs b/nearby/build_scripts/src/tools.rs
similarity index 70%
copy from common/derive_fuzztest/fuzz/src/bin/integer_add.rs
copy to nearby/build_scripts/src/tools.rs
index 38b8172..6258260 100644
--- a/common/derive_fuzztest/fuzz/src/bin/integer_add.rs
+++ b/nearby/build_scripts/src/tools.rs
@@ -1,4 +1,4 @@
-// Copyright 2024 Google LLC
+// 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.
@@ -12,12 +12,3 @@
// See the License for the specific language governing permissions and
// limitations under the License.
-#![cfg_attr(fuzzing, no_main)]
-
-use derive_fuzztest::fuzztest;
-
-#[fuzztest]
-pub fn test(a: u8, b: u8) {
- let _ = a.checked_add(b);
- // a + b; // This fails because a + b can overflow.
-}
diff --git a/nearby/connections/.clang-format b/nearby/connections/.clang-format
new file mode 100644
index 0000000..8217234
--- /dev/null
+++ b/nearby/connections/.clang-format
@@ -0,0 +1,17 @@
+# Copyright 2024 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.
+
+Language: Cpp
+BasedOnStyle: Google
+QualifierAlignment: Left
\ No newline at end of file
diff --git a/nearby/connections/ukey2/ukey2/Cargo.toml b/nearby/connections/ukey2/ukey2/Cargo.toml
index 35de144..900a5a0 100644
--- a/nearby/connections/ukey2/ukey2/Cargo.toml
+++ b/nearby/connections/ukey2/ukey2/Cargo.toml
@@ -3,6 +3,7 @@
version.workspace = true
edition.workspace = true
publish.workspace = true
+license.workspace = true
[lints]
workspace = true
diff --git a/nearby/connections/ukey2/ukey2_c_ffi/Cargo.toml b/nearby/connections/ukey2/ukey2_c_ffi/Cargo.toml
index 1034359..328400b 100644
--- a/nearby/connections/ukey2/ukey2_c_ffi/Cargo.toml
+++ b/nearby/connections/ukey2/ukey2_c_ffi/Cargo.toml
@@ -3,6 +3,7 @@
version.workspace = true
edition.workspace = true
publish.workspace = true
+license.workspace = true
[dependencies]
ukey2_connections = { path = "../ukey2_connections" }
@@ -25,4 +26,4 @@
[lib]
# Static lib is a bit large, resulting in quite a large test executable.
# This will be also shipped as a dynamic lib in most environments (I think) so good to replicate those conditions.
-crate_type = ["cdylib"]
+crate-type = ["cdylib"]
diff --git a/nearby/connections/ukey2/ukey2_connections/Cargo.toml b/nearby/connections/ukey2/ukey2_connections/Cargo.toml
index 5c9622b..ff2ee44 100644
--- a/nearby/connections/ukey2/ukey2_connections/Cargo.toml
+++ b/nearby/connections/ukey2/ukey2_connections/Cargo.toml
@@ -3,6 +3,7 @@
version.workspace = true
edition.workspace = true
publish.workspace = true
+license.workspace = true
[lints]
workspace = true
@@ -15,11 +16,11 @@
[dependencies]
ukey2_rs = { path = "../ukey2" }
-crypto_provider.workspace = true
+crypto_provider = { workspace = true, features = ["alloc"] }
rand = { workspace = true, features = ["std", "std_rng"] }
ukey2_proto.workspace = true
nom = { version = "7.1.3", features = ["alloc"] }
-bytes = "1.5.0"
+bytes = "1.7.1"
criterion.workspace = true
[dev-dependencies]
diff --git a/nearby/connections/ukey2/ukey2_connections/fuzz/Cargo.toml b/nearby/connections/ukey2/ukey2_connections/fuzz/Cargo.toml
index 2c94ebb..6ba66f5 100644
--- a/nearby/connections/ukey2/ukey2_connections/fuzz/Cargo.toml
+++ b/nearby/connections/ukey2/ukey2_connections/fuzz/Cargo.toml
@@ -2,6 +2,7 @@
name = "ukey2_connections-fuzz"
version.workspace = true
publish.workspace = true
+license.workspace = true
edition.workspace = true
[package.metadata]
@@ -18,6 +19,9 @@
[target.'cfg(fuzzing)'.dependencies]
libfuzzer-sys.workspace = true
+[lints.rust]
+unexpected_cfgs = { level = "warn", check-cfg = ['cfg(fuzzing)'] }
+
[[bin]]
name = "fuzz_connection"
path = "fuzz_targets/fuzz_connection.rs"
diff --git a/nearby/connections/ukey2/ukey2_jni/Cargo.toml b/nearby/connections/ukey2/ukey2_jni/Cargo.toml
index bd615f5..f66ddb7 100644
--- a/nearby/connections/ukey2/ukey2_jni/Cargo.toml
+++ b/nearby/connections/ukey2/ukey2_jni/Cargo.toml
@@ -3,6 +3,7 @@
version.workspace = true
edition.workspace = true
publish.workspace = true
+license.workspace = true
[lints]
workspace = true
@@ -28,4 +29,4 @@
std = ["lock_adapter/std"]
[lib]
-crate_type = ["cdylib"]
\ No newline at end of file
+crate-type = ["cdylib"]
diff --git a/nearby/connections/ukey2/ukey2_proto/Cargo.toml b/nearby/connections/ukey2/ukey2_proto/Cargo.toml
index c94d5b8..e7292da 100644
--- a/nearby/connections/ukey2/ukey2_proto/Cargo.toml
+++ b/nearby/connections/ukey2/ukey2_proto/Cargo.toml
@@ -3,6 +3,7 @@
version.workspace = true
edition.workspace = true
publish.workspace = true
+license.workspace = true
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[dependencies]
diff --git a/nearby/connections/ukey2/ukey2_shell/Cargo.toml b/nearby/connections/ukey2/ukey2_shell/Cargo.toml
index c5c84ea..e81c5dd 100644
--- a/nearby/connections/ukey2/ukey2_shell/Cargo.toml
+++ b/nearby/connections/ukey2/ukey2_shell/Cargo.toml
@@ -3,6 +3,7 @@
version.workspace = true
edition.workspace = true
publish.workspace = true
+license.workspace = true
[lints]
workspace = true
diff --git a/nearby/crypto/crypto_provider/Cargo.toml b/nearby/crypto/crypto_provider/Cargo.toml
index bafdfcc..3862cfd 100644
--- a/nearby/crypto/crypto_provider/Cargo.toml
+++ b/nearby/crypto/crypto_provider/Cargo.toml
@@ -3,6 +3,7 @@
version.workspace = true
edition.workspace = true
publish.workspace = true
+license.workspace = true
[lints]
workspace = true
diff --git a/nearby/crypto/crypto_provider_boringssl/Cargo.lock b/nearby/crypto/crypto_provider_boringssl/Cargo.lock
index 356a65d..cf82847 100644
--- a/nearby/crypto/crypto_provider_boringssl/Cargo.lock
+++ b/nearby/crypto/crypto_provider_boringssl/Cargo.lock
@@ -102,9 +102,9 @@
[[package]]
name = "itertools"
-version = "0.12.1"
+version = "0.13.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "ba291022dbbd398a455acf126c1e341954079855bc60dfdda641363bd6922569"
+checksum = "413ee7dfc52ee1a4949ceeb7dbc8a33f2d6c088194d9f922fb8318faf1f01186"
dependencies = [
"either",
]
diff --git a/nearby/crypto/crypto_provider_default/Cargo.toml b/nearby/crypto/crypto_provider_default/Cargo.toml
index d79739b..c67bad2 100644
--- a/nearby/crypto/crypto_provider_default/Cargo.toml
+++ b/nearby/crypto/crypto_provider_default/Cargo.toml
@@ -3,6 +3,7 @@
version.workspace = true
edition.workspace = true
publish.workspace = true
+license.workspace = true
[lints]
workspace = true
diff --git a/nearby/crypto/crypto_provider_rustcrypto/Cargo.toml b/nearby/crypto/crypto_provider_rustcrypto/Cargo.toml
index 14e8ad9..c26c7db 100644
--- a/nearby/crypto/crypto_provider_rustcrypto/Cargo.toml
+++ b/nearby/crypto/crypto_provider_rustcrypto/Cargo.toml
@@ -3,6 +3,7 @@
version.workspace = true
edition.workspace = true
publish.workspace = true
+license.workspace = true
[lints]
workspace = true
diff --git a/nearby/crypto/crypto_provider_stubs/Cargo.toml b/nearby/crypto/crypto_provider_stubs/Cargo.toml
index 4e8bdec..5808d6a 100644
--- a/nearby/crypto/crypto_provider_stubs/Cargo.toml
+++ b/nearby/crypto/crypto_provider_stubs/Cargo.toml
@@ -3,6 +3,7 @@
version.workspace = true
edition.workspace = true
publish.workspace = true
+license.workspace = true
[dependencies]
crypto_provider = {workspace = true, features = ["std", "alloc"] }
diff --git a/nearby/crypto/crypto_provider_test/Cargo.toml b/nearby/crypto/crypto_provider_test/Cargo.toml
index 4064330..1191aae 100644
--- a/nearby/crypto/crypto_provider_test/Cargo.toml
+++ b/nearby/crypto/crypto_provider_test/Cargo.toml
@@ -3,6 +3,7 @@
version.workspace = true
edition.workspace = true
publish.workspace = true
+license.workspace = true
[dependencies]
crypto_provider = { workspace = true, features = ["raw_private_key_permit", "test_vectors", "alloc"] }
diff --git a/nearby/crypto/crypto_provider_test/fuzz/Cargo.toml b/nearby/crypto/crypto_provider_test/fuzz/Cargo.toml
index 10c3e5c..d8dbd6f 100644
--- a/nearby/crypto/crypto_provider_test/fuzz/Cargo.toml
+++ b/nearby/crypto/crypto_provider_test/fuzz/Cargo.toml
@@ -3,6 +3,7 @@
version = "0.0.0"
publish = false
edition = "2021"
+license = "Apache-2.0"
[package.metadata]
cargo-fuzz = true
@@ -15,6 +16,9 @@
[target.'cfg(fuzzing)'.dependencies]
libfuzzer-sys.workspace = true
+[lints.rust]
+unexpected_cfgs = { level = "warn", check-cfg = ['cfg(fuzzing)'] }
+
[features]
default = ["crypto_provider_default/default"]
boringssl = ["crypto_provider_default/boringssl"]
diff --git a/nearby/deny.toml b/nearby/deny.toml
index aaf8eea..bc7f4c5 100644
--- a/nearby/deny.toml
+++ b/nearby/deny.toml
@@ -24,6 +24,9 @@
ignore = [
# comment explaining why we have to ignore it
# "RUSTSEC-FOO",
+
+ # Need a new release of cbindgen: https://github.com/mozilla/cbindgen/issues/983
+ "RUSTSEC-2021-0145",
]
# Threshold for security vulnerabilities, any vulnerability with a CVSS score
# lower than the range specified will be ignored. Note that ignored advisories
@@ -120,7 +123,7 @@
# 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
+ignore = false
# 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
diff --git a/nearby/presence/array_ref/Cargo.toml b/nearby/presence/array_ref/Cargo.toml
index b74168b..c981ecf 100644
--- a/nearby/presence/array_ref/Cargo.toml
+++ b/nearby/presence/array_ref/Cargo.toml
@@ -3,6 +3,7 @@
version.workspace = true
edition.workspace = true
publish.workspace = true
+license.workspace = true
[lints]
workspace = true
diff --git a/nearby/presence/array_view/Cargo.toml b/nearby/presence/array_view/Cargo.toml
index 8ac53d2..94a37d7 100644
--- a/nearby/presence/array_view/Cargo.toml
+++ b/nearby/presence/array_view/Cargo.toml
@@ -3,6 +3,7 @@
version.workspace = true
edition.workspace = true
publish.workspace = true
+license.workspace = true
[lints]
workspace = true
diff --git a/nearby/presence/ldt/Cargo.toml b/nearby/presence/ldt/Cargo.toml
index 337dd0d..bb7c81c 100644
--- a/nearby/presence/ldt/Cargo.toml
+++ b/nearby/presence/ldt/Cargo.toml
@@ -3,6 +3,7 @@
version.workspace = true
edition.workspace = true
publish.workspace = true
+license.workspace = true
[lints]
workspace = true
diff --git a/nearby/presence/ldt/fuzz/Cargo.toml b/nearby/presence/ldt/fuzz/Cargo.toml
index c192792..14659e8 100644
--- a/nearby/presence/ldt/fuzz/Cargo.toml
+++ b/nearby/presence/ldt/fuzz/Cargo.toml
@@ -2,6 +2,7 @@
name = "ldt-fuzz"
version.workspace = true
publish.workspace = true
+license.workspace = true
edition.workspace = true
[package.metadata]
@@ -17,6 +18,9 @@
[target.'cfg(fuzzing)'.dependencies]
libfuzzer-sys.workspace = true
+[lints.rust]
+unexpected_cfgs = { level = "warn", check-cfg = ['cfg(fuzzing)'] }
+
[[bin]]
name = "ldt_roundtrip"
path = "src/bin/ldt_roundtrip.rs"
diff --git a/nearby/presence/ldt_np_adv/Cargo.toml b/nearby/presence/ldt_np_adv/Cargo.toml
index 5d18a28..c846439 100644
--- a/nearby/presence/ldt_np_adv/Cargo.toml
+++ b/nearby/presence/ldt_np_adv/Cargo.toml
@@ -3,6 +3,7 @@
version.workspace = true
edition.workspace = true
publish.workspace = true
+license.workspace = true
[lints]
workspace = true
diff --git a/nearby/presence/ldt_np_adv/fuzz/Cargo.toml b/nearby/presence/ldt_np_adv/fuzz/Cargo.toml
index e4f497c..544b257 100644
--- a/nearby/presence/ldt_np_adv/fuzz/Cargo.toml
+++ b/nearby/presence/ldt_np_adv/fuzz/Cargo.toml
@@ -2,6 +2,7 @@
name = "ldt-np-adv-fuzz"
version.workspace = true
publish.workspace = true
+license.workspace = true
edition.workspace = true
[package.metadata]
@@ -19,6 +20,9 @@
[target.'cfg(fuzzing)'.dependencies]
libfuzzer-sys.workspace = true
+[lints.rust]
+unexpected_cfgs = { level = "warn", check-cfg = ['cfg(fuzzing)'] }
+
[[bin]]
name = "ldt_np_roundtrip"
path = "src/bin/ldt_np_roundtrip.rs"
diff --git a/nearby/presence/ldt_np_adv_ffi/Cargo.toml b/nearby/presence/ldt_np_adv_ffi/Cargo.toml
index fdc5501..37176fe 100644
--- a/nearby/presence/ldt_np_adv_ffi/Cargo.toml
+++ b/nearby/presence/ldt_np_adv_ffi/Cargo.toml
@@ -3,6 +3,7 @@
version = "0.1.0"
edition = "2021"
publish = false
+license = "Apache-2.0"
[dependencies]
crypto_provider_default.workspace = true
diff --git a/nearby/presence/ldt_np_adv_ffi/c/tests/ldt_ffi_tests.cc b/nearby/presence/ldt_np_adv_ffi/c/tests/ldt_ffi_tests.cc
index 65e0f68..674547a 100644
--- a/nearby/presence/ldt_np_adv_ffi/c/tests/ldt_ffi_tests.cc
+++ b/nearby/presence/ldt_np_adv_ffi/c/tests/ldt_ffi_tests.cc
@@ -279,12 +279,12 @@
}
TEST(LdtFfiTests, MultiThreadedTests) {
- int i, num_threads = 100;
+ constexpr int num_threads = 100;
pthread_t tid[num_threads];
memset(tid, 0, num_threads * sizeof(pthread_t));
// Create the threads
- for (i = 0; i < num_threads; i++)
+ for (int i = 0; i < num_threads; i++)
ASSERT_EQ(pthread_create(&tid[i], nullptr, worker_thread, (void *)&tid[i]),
0);
@@ -295,6 +295,8 @@
pthread_cond_broadcast(&cond);
// Wait for them all to finish and check the status
- for (i = 0; i < num_threads; i++) ASSERT_EQ(pthread_join(tid[i], nullptr), 0);
+ for (int i = 0; i < num_threads; i++) {
+ ASSERT_EQ(pthread_join(tid[i], nullptr), 0);
+ }
}
#endif
diff --git a/nearby/presence/ldt_np_jni/Cargo.toml b/nearby/presence/ldt_np_jni/Cargo.toml
index cf7c2db..698f022 100644
--- a/nearby/presence/ldt_np_jni/Cargo.toml
+++ b/nearby/presence/ldt_np_jni/Cargo.toml
@@ -3,6 +3,7 @@
version.workspace = true
edition.workspace = true
publish.workspace = true
+license.workspace = true
[lints]
workspace = true
diff --git a/nearby/presence/ldt_tbc/Cargo.toml b/nearby/presence/ldt_tbc/Cargo.toml
index 8941051..bf7d363 100644
--- a/nearby/presence/ldt_tbc/Cargo.toml
+++ b/nearby/presence/ldt_tbc/Cargo.toml
@@ -3,6 +3,7 @@
version.workspace = true
edition.workspace = true
publish.workspace = true
+license.workspace = true
[lints]
workspace = true
diff --git a/nearby/presence/np_adv/Cargo.toml b/nearby/presence/np_adv/Cargo.toml
index 6670192..4b79c97 100644
--- a/nearby/presence/np_adv/Cargo.toml
+++ b/nearby/presence/np_adv/Cargo.toml
@@ -3,12 +3,13 @@
version.workspace = true
edition.workspace = true
publish.workspace = true
+license.workspace = true
[lints]
workspace = true
[dependencies]
-array_view = { path = "../array_view" }
+array_view.workspace = true
ldt_np_adv.workspace = true
ldt.workspace = true
np_hkdf.workspace = true
@@ -21,12 +22,15 @@
lazy_static.workspace = true
sink.workspace = true
tinyvec.workspace = true
+itertools = { workspace = true, default-features = false }
[features]
default = ["alloc"]
devtools = []
testing = []
alloc = ["crypto_provider/alloc"]
+# adds std::error::Error impls
+std = ["alloc"]
[dev-dependencies]
hex.workspace = true
diff --git a/nearby/presence/np_adv/fuzz/.gitignore b/nearby/presence/np_adv/fuzz/.gitignore
new file mode 100644
index 0000000..1a45eee
--- /dev/null
+++ b/nearby/presence/np_adv/fuzz/.gitignore
@@ -0,0 +1,4 @@
+target
+corpus
+artifacts
+coverage
diff --git a/nearby/presence/np_adv/fuzz/Cargo.toml b/nearby/presence/np_adv/fuzz/Cargo.toml
new file mode 100644
index 0000000..d65d5b6
--- /dev/null
+++ b/nearby/presence/np_adv/fuzz/Cargo.toml
@@ -0,0 +1,32 @@
+[package]
+name = "np_adv_fuzz"
+version.workspace = true
+publish = false
+edition.workspace = true
+license.workspace = true
+
+[package.metadata]
+cargo-fuzz = true
+
+[dependencies]
+arbitrary = { workspace = true, features = ["derive"] }
+derive_fuzztest.workspace = true
+np_adv = { workspace = true, features = ["testing"] }
+crypto_provider.workspace = true
+crypto_provider_default = { workspace = true, features = ["std", "rustcrypto"] }
+
+[target.'cfg(fuzzing)'.dependencies]
+libfuzzer-sys.workspace = true
+
+[[bin]]
+name = "actions_de_deser"
+doc = false
+
+[[bin]]
+name = "actions_de_encoder"
+doc = false
+
+[[bin]]
+name = "actions_de_roundtrip"
+doc = false
+
diff --git a/nearby/presence/np_adv/fuzz/src/bin/actions_de_deser.rs b/nearby/presence/np_adv/fuzz/src/bin/actions_de_deser.rs
new file mode 100644
index 0000000..de06305
--- /dev/null
+++ b/nearby/presence/np_adv/fuzz/src/bin/actions_de_deser.rs
@@ -0,0 +1,41 @@
+// Copyright 2024 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.
+
+//! Fuzz test for actions data element parsing logic
+
+#![cfg_attr(fuzzing, no_main)]
+
+use arbitrary::Unstructured;
+use np_adv::extended::data_elements::DeserializedActionsDE;
+use np_adv::extended::deserialize::data_element::DataElement;
+
+#[derive(arbitrary::Arbitrary, Clone, Debug)]
+struct ActionsDeserFuzzInput {
+ data: [u8; 127],
+ #[arbitrary(with = arbitrary_de_len)]
+ de_len: usize,
+}
+
+fn arbitrary_de_len(u: &mut Unstructured) -> arbitrary::Result<usize> {
+ u.int_in_range(0..=127).map(|val| usize::try_from(val).expect("range is valid for a usize"))
+}
+
+#[derive_fuzztest::fuzztest]
+fn deserialize_actions_de(input: ActionsDeserFuzzInput) {
+ let de = DataElement::new_for_testing(0.into(), 6_u32.into(), &input.data[..input.de_len]);
+ let actions_de = DeserializedActionsDE::try_from(&de).map(|actions| {
+ // collect actions to trigger iterator parsing logic
+ let action_ids = actions.collect_action_ids();
+ });
+}
diff --git a/common/derive_fuzztest/fuzz/src/bin/integer_add.rs b/nearby/presence/np_adv/fuzz/src/bin/actions_de_encoder.rs
similarity index 60%
copy from common/derive_fuzztest/fuzz/src/bin/integer_add.rs
copy to nearby/presence/np_adv/fuzz/src/bin/actions_de_encoder.rs
index 38b8172..9108169 100644
--- a/common/derive_fuzztest/fuzz/src/bin/integer_add.rs
+++ b/nearby/presence/np_adv/fuzz/src/bin/actions_de_encoder.rs
@@ -12,12 +12,17 @@
// See the License for the specific language governing permissions and
// limitations under the License.
+//! Fuzz test for actions data element encoding logic
+
#![cfg_attr(fuzzing, no_main)]
-use derive_fuzztest::fuzztest;
+use np_adv::extended::data_elements::ActionsDataElement;
+use np_adv::ArrayVec;
+use np_adv_fuzz::FuzzInput;
-#[fuzztest]
-pub fn test(a: u8, b: u8) {
- let _ = a.checked_add(b);
- // a + b; // This fails because a + b can overflow.
+#[derive_fuzztest::fuzztest]
+fn deserialize_actions_de(input: FuzzInput) {
+ let mut actions = ArrayVec::new();
+ actions.extend_from_slice(&input.data[..input.count]);
+ let actions_de = ActionsDataElement::try_from_actions(actions);
}
diff --git a/nearby/presence/np_adv/fuzz/src/bin/actions_de_roundtrip.rs b/nearby/presence/np_adv/fuzz/src/bin/actions_de_roundtrip.rs
new file mode 100644
index 0000000..7976c5f
--- /dev/null
+++ b/nearby/presence/np_adv/fuzz/src/bin/actions_de_roundtrip.rs
@@ -0,0 +1,76 @@
+// Copyright 2024 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.
+
+//! Fuzz test for actions data element encoding and decoding round trip
+
+#![cfg_attr(fuzzing, no_main)]
+
+use crypto_provider_default::CryptoProviderImpl;
+use np_adv::credential::book::CredentialBookBuilder;
+use np_adv::credential::matched::EmptyMatchedCredential;
+use np_adv::extended::data_elements::{ActionsDataElement, DeserializedActionsDE};
+use np_adv::extended::deserialize::{Section, V1DeserializedSection};
+use np_adv::extended::serialize::{
+ AdvBuilder, AdvertisementType, SingleTypeDataElement, UnencryptedSectionEncoder,
+};
+use np_adv::{deserialization_arena, deserialize_advertisement, ArrayVec};
+use np_adv_fuzz::FuzzInput;
+
+#[derive_fuzztest::fuzztest]
+fn deserialize_actions_de(input: FuzzInput) {
+ let mut actions = ArrayVec::new();
+ actions.extend_from_slice(&input.data[..input.count]);
+ let actions_de = match ActionsDataElement::try_from_actions(actions) {
+ Ok(de) => de,
+ Err(_) => return,
+ };
+ let mut adv_builder = AdvBuilder::new(AdvertisementType::Plaintext);
+ let mut section_builder = adv_builder.section_builder(UnencryptedSectionEncoder).unwrap();
+ section_builder.add_de(|_salt| actions_de).unwrap();
+ section_builder.add_to_advertisement::<CryptoProviderImpl>();
+ let arena = deserialization_arena!();
+ let adv = adv_builder.into_advertisement();
+ let cred_book = CredentialBookBuilder::<EmptyMatchedCredential>::build_cached_slice_book::<
+ 0,
+ 0,
+ CryptoProviderImpl,
+ >(&[], &[]);
+ let contents =
+ 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 section = match §ions[0] {
+ V1DeserializedSection::Plaintext(s) => s,
+ _ => panic!("this is a plaintext adv"),
+ };
+ let data_elements = section.iter_data_elements().collect::<Result<Vec<_>, _>>().unwrap();
+ assert_eq!(1, data_elements.len());
+ let de = &data_elements[0];
+ assert_eq!(ActionsDataElement::DE_TYPE, de.de_type());
+ let actions_de =
+ DeserializedActionsDE::try_from(de).expect("Should succeed since this de is an actions de");
+ let decoded_actions = actions_de
+ .collect_action_ids()
+ .iter()
+ .map(|res| res.expect("valid action ids"))
+ .collect::<Vec<_>>();
+ let mut expected = actions.to_vec();
+ expected.sort();
+ expected.dedup();
+ assert_eq!(expected.as_slice(), decoded_actions.as_slice());
+}
diff --git a/nearby/presence/np_adv/fuzz/src/lib.rs b/nearby/presence/np_adv/fuzz/src/lib.rs
new file mode 100644
index 0000000..b10929c
--- /dev/null
+++ b/nearby/presence/np_adv/fuzz/src/lib.rs
@@ -0,0 +1,38 @@
+// Copyright 2024 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 arbitrary::Unstructured;
+use np_adv::extended::data_elements::ActionId;
+
+#[derive(arbitrary::Arbitrary, Clone, Debug)]
+pub struct FuzzInput {
+ #[arbitrary(with = arbitrary_action_ids)]
+ pub data: [ActionId; 64],
+ #[arbitrary(with = arbitrary_actions_count)]
+ pub count: usize,
+}
+
+fn arbitrary_actions_count(u: &mut Unstructured) -> arbitrary::Result<usize> {
+ u.int_in_range(0..=64).map(|val| usize::try_from(val).unwrap())
+}
+
+fn arbitrary_action_ids(u: &mut Unstructured) -> arbitrary::Result<[ActionId; 64]> {
+ Ok(std::array::from_fn(|_| {
+ let next = u16::try_from(
+ u.int_in_range(0..=2047).expect("fuzzer should generate enough data for a u16"),
+ )
+ .expect("the range will always be a valid u16");
+ ActionId::try_from(next).expect("rang is valid for action_ids")
+ }))
+}
diff --git a/nearby/presence/np_adv/src/array_vec.rs b/nearby/presence/np_adv/src/array_vec.rs
index bcc387e..3298ace 100644
--- a/nearby/presence/np_adv/src/array_vec.rs
+++ b/nearby/presence/np_adv/src/array_vec.rs
@@ -77,6 +77,13 @@
self.0.push(Some(value))
}
+ /// Tries to place an element onto the end of the vec.
+ /// Returns back the element if the capacity is exhausted,
+ /// otherwise returns None.
+ pub fn try_push(&mut self, value: A) -> Option<A> {
+ self.0.try_push(Some(value)).unwrap_or_else(|| None)
+ }
+
/// 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())
diff --git a/nearby/presence/np_adv/src/extended/data_elements/actions.rs b/nearby/presence/np_adv/src/extended/data_elements/actions.rs
new file mode 100644
index 0000000..3679555
--- /dev/null
+++ b/nearby/presence/np_adv/src/extended/data_elements/actions.rs
@@ -0,0 +1,557 @@
+// Copyright 2024 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")]
+use alloc::vec::Vec;
+
+use itertools::Itertools;
+use nom::error::ErrorKind;
+use nom::Err::Error;
+use nom::{bytes, combinator, multi};
+use sink::Sink;
+use tinyvec::ArrayVec;
+
+use crate::array_vec::ArrayVecOption;
+use crate::extended::de_type::DeType;
+use crate::extended::deserialize::data_element::DataElement;
+use crate::extended::serialize::{DeHeader, SingleTypeDataElement, WriteDataElement};
+use crate::extended::MAX_DE_LEN;
+
+#[cfg(test)]
+mod tests;
+
+pub const MAX_ACTION_ID_VALUE: u16 = 2047;
+
+pub const MAX_ACTIONS_CONTAINER_LENGTH: usize = 64;
+
+// The minimum length of a single container is 2 (one header byte + 1 byte payload) so an actions DE
+/// can fit at most 127/2 = 63 total actions containers
+pub const MAX_NUM_ACTIONS_CONTAINERS: usize = 63;
+
+/// Represents a valid action ID which can be encoded in an Actions DE
+#[derive(Default, Debug, PartialEq, Eq, Clone, Copy, Ord, PartialOrd)]
+pub struct ActionId(u16);
+
+impl ActionId {
+ /// Gets the u16 value of the action id
+ pub fn as_u16(&self) -> u16 {
+ self.0
+ }
+}
+
+// The provided value was not in the range of valid action ids [0, 2047]
+#[derive(Debug, PartialEq, Eq, Clone, Copy)]
+pub struct ActionIdOutOfRange;
+
+impl TryFrom<u16> for ActionId {
+ type Error = ActionIdOutOfRange;
+
+ fn try_from(value: u16) -> Result<Self, Self::Error> {
+ if value <= MAX_ACTION_ID_VALUE {
+ Ok(Self(value))
+ } else {
+ Err(ActionIdOutOfRange)
+ }
+ }
+}
+
+impl From<ActionId> for u16 {
+ fn from(value: ActionId) -> Self {
+ value.0
+ }
+}
+
+#[derive(PartialEq, Eq, Debug, Clone, Copy)]
+enum ContainerType {
+ DeltaEncoded,
+ DeltaEncodedWithOffset,
+ BitVectorOffset,
+}
+
+#[derive(Debug)]
+struct UnknownContainerTypeValue;
+
+impl TryFrom<u8> for ContainerType {
+ type Error = UnknownContainerTypeValue;
+
+ fn try_from(value: u8) -> Result<Self, Self::Error> {
+ match value {
+ 0b00 => Ok(ContainerType::DeltaEncoded),
+ 0b01 => Ok(ContainerType::DeltaEncodedWithOffset),
+ 0b10 => Ok(ContainerType::BitVectorOffset),
+ _ => Err(UnknownContainerTypeValue),
+ }
+ }
+}
+
+impl From<ContainerType> for u8 {
+ fn from(value: ContainerType) -> Self {
+ match value {
+ ContainerType::DeltaEncoded => 0b00,
+ ContainerType::DeltaEncodedWithOffset => 0b01,
+ ContainerType::BitVectorOffset => 0b10,
+ }
+ }
+}
+
+/// Actions data element parsed from a slice of bytes, this type references the original slice of
+/// bytes which was parsed
+#[derive(Debug)]
+pub struct DeserializedActionsDE<'adv> {
+ containers: ArrayVecOption<DeserializedActionsContainer<'adv>, MAX_NUM_ACTIONS_CONTAINERS>,
+}
+
+impl<'adv> SingleTypeDataElement for DeserializedActionsDE<'adv> {
+ const DE_TYPE: DeType = DeType::const_from(0x06);
+}
+
+#[derive(Debug, Eq, PartialEq)]
+pub enum ActionsDeserializationError {
+ InvalidTypeCode,
+ InvalidContainerLength,
+ InvalidContainerType,
+ GenericDeserializationError,
+}
+
+/// Convert a deserialized DE into one you can serialize
+impl<'a> TryFrom<&'a DataElement<'a>> for DeserializedActionsDE<'a> {
+ type Error = ActionsDeserializationError;
+
+ fn try_from(value: &'a DataElement<'a>) -> Result<Self, Self::Error> {
+ if value.de_type() != Self::DE_TYPE {
+ return Err(ActionsDeserializationError::InvalidTypeCode);
+ }
+ DeserializedActionsDE::deserialize(value.contents()).map_err(|e| match e {
+ Error(e) => match e.code {
+ ErrorKind::Eof | ErrorKind::Verify => {
+ ActionsDeserializationError::InvalidContainerLength
+ }
+ ErrorKind::MapOpt => ActionsDeserializationError::InvalidContainerType,
+ _ => ActionsDeserializationError::GenericDeserializationError,
+ },
+ _ => ActionsDeserializationError::GenericDeserializationError,
+ })
+ }
+}
+
+impl<'adv> DeserializedActionsDE<'adv> {
+ /// Returns a collection over of all action ids contained within the data element
+ #[cfg(feature = "alloc")]
+ pub fn collect_action_ids(&self) -> Vec<Result<ActionId, ActionIdOutOfRange>> {
+ let mut result = Vec::new();
+ self.containers.iter().for_each(|container| {
+ container.iter_action_ids().for_each(|action| result.push(action))
+ });
+ result
+ }
+
+ /// Parses the raw bytes of an Actions DE into an intermediate format, that is the contents
+ /// are separated into containers with their corresponding container type, but no further decoding
+ /// is done on the contents of the containers, leaving the bytes in their compact encoded format.
+ fn deserialize(
+ de_contents: &'adv [u8],
+ ) -> Result<DeserializedActionsDE, nom::Err<nom::error::Error<&[u8]>>> {
+ combinator::all_consuming(multi::fold_many_m_n(
+ 1,
+ MAX_NUM_ACTIONS_CONTAINERS,
+ DeserializedActionsContainer::parse,
+ Self::new_empty,
+ |mut acc, item| {
+ acc.add_container(item);
+ acc
+ },
+ ))(de_contents)
+ .map(|(rem, actions)| {
+ debug_assert!(rem.is_empty());
+ actions
+ })
+ }
+
+ /// Initializes an actions de with empty contents
+ fn new_empty() -> Self {
+ let internal = ArrayVecOption::default();
+ Self { containers: internal }
+ }
+
+ /// Appends a container to the DE, panicking in the case where the max length is exceeded
+ fn add_container(&mut self, container: DeserializedActionsContainer<'adv>) {
+ self.containers.push(container);
+ }
+}
+
+#[derive(Debug)]
+struct DeserializedActionsContainer<'adv> {
+ container_type: ContainerType,
+ offset: u16,
+ payload: &'adv [u8],
+}
+
+impl<'adv> DeserializedActionsContainer<'adv> {
+ fn parse(bytes: &'adv [u8]) -> nom::IResult<&[u8], Self> {
+ let (input, (container_type, container_encoded_len)) =
+ combinator::map_opt(nom::number::complete::u8, |b| {
+ // right shift by 6 to obtain the upper 2 type bits TTLLLLLL
+ let type_value = b >> 6;
+ let encoded_len = b & 0b00111111;
+ ContainerType::try_from(type_value)
+ .map(|container_type| (container_type, encoded_len))
+ .ok()
+ })(bytes)?;
+
+ let (input, payload_bytes) = bytes::complete::take(container_encoded_len + 1)(input)?;
+
+ let (payload, offset) = match container_type {
+ ContainerType::DeltaEncoded => (payload_bytes, 0u16),
+ // A container which only contains the sub header offset and no
+ // subsequent data is disallowed by the spec
+ ContainerType::DeltaEncodedWithOffset | ContainerType::BitVectorOffset => {
+ combinator::verify(nom::number::complete::u8, |_| payload_bytes.len() > 1)(
+ payload_bytes,
+ )
+ .map(|(remaining, byte)| (remaining, u16::from(byte) * 8))?
+ }
+ };
+ Ok((input, DeserializedActionsContainer { container_type, offset, payload }))
+ }
+
+ fn iter_action_ids(&self) -> ActionsContainerIterator {
+ ActionsContainerIterator::new(self)
+ }
+}
+
+/// Iterates all the action ids in a container
+enum ActionsContainerIterator<'c> {
+ DeltaEncoded { first: bool, delta: u16, bytes: &'c [u8] },
+ BitVector { current_offset: u16, position_in_byte: u8, current_byte: u8, bytes: &'c [u8] },
+}
+
+impl<'c> ActionsContainerIterator<'c> {
+ fn new(container: &'c DeserializedActionsContainer) -> Self {
+ match container.container_type {
+ ContainerType::DeltaEncoded | ContainerType::DeltaEncodedWithOffset => {
+ Self::DeltaEncoded {
+ delta: container.offset,
+ bytes: container.payload,
+ first: true,
+ }
+ }
+ ContainerType::BitVectorOffset => {
+ let (remaining, byte) =
+ nom::number::complete::u8::<&[u8], nom::error::Error<_>>(container.payload)
+ .expect(
+ "we verified bytes has at least one byte when parsing original container",
+ );
+ Self::BitVector {
+ current_offset: container.offset,
+ position_in_byte: 0,
+ current_byte: byte,
+ bytes: remaining,
+ }
+ }
+ }
+ }
+}
+
+impl<'c> Iterator for ActionsContainerIterator<'c> {
+ type Item = Result<ActionId, ActionIdOutOfRange>;
+
+ fn next(&mut self) -> Option<Self::Item> {
+ match self {
+ ActionsContainerIterator::DeltaEncoded { delta, bytes, first } => {
+ nom::number::complete::u8::<&[u8], nom::error::Error<_>>(bytes)
+ .map(|(rem, b)| {
+ let mut result = u16::from(b) + *delta;
+ if *first {
+ *first = false;
+ } else {
+ result += 1;
+ }
+ // save the result to be used as the next delta
+ *delta = result;
+ // update the slice to no longer include the current byte since we just used it
+ *bytes = rem;
+ result.try_into()
+ })
+ .ok()
+ }
+ ActionsContainerIterator::BitVector {
+ current_offset,
+ position_in_byte,
+ current_byte,
+ bytes,
+ } => {
+ while *position_in_byte <= 7 || !bytes.is_empty() {
+ // take another byte and update position and offset
+ if *position_in_byte > 7 && !bytes.is_empty() {
+ let (remaining, byte) =
+ nom::number::complete::u8::<&[u8], nom::error::Error<_>>(bytes)
+ .expect("we verified bytes has at least one byte");
+ *current_byte = byte;
+ *bytes = remaining;
+ *current_offset += 8;
+ *position_in_byte = 0
+ }
+ if ((0b10000000 >> *position_in_byte) & *current_byte) != 0 {
+ let result: Option<Result<ActionId, _>> =
+ Some((*current_offset + u16::from(*position_in_byte)).try_into());
+ *position_in_byte += 1;
+ return result;
+ }
+ *position_in_byte += 1;
+ }
+ None
+ }
+ }
+ }
+}
+/// Actions data element consists of one or more containers
+#[derive(Debug)]
+pub struct ActionsDataElement {
+ containers: ArrayVecOption<ActionsContainer, MAX_NUM_ACTIONS_CONTAINERS>,
+ len: usize,
+}
+
+impl ActionsDataElement {
+ /// Creates an Actions DE storing the provided action ids in memory using the default encoding
+ /// scheme of simple delta encoding. This limits the amount of encodable actions to 64, and the
+ /// action id range to what can fit in a byte after being delta encoded.
+ // TODO: provide more flexibility over which encoding scheme is used
+ pub fn try_from_actions(
+ actions: ArrayVec<[ActionId; 64]>,
+ ) -> Result<Self, ActionsDataElementError> {
+ DeltaEncodedContainer::try_from_actions(actions).map(|c| {
+ let mut actions_de = ActionsDataElement::new_empty();
+ // This will always succeed since we are only adding one container which cannot exceed
+ // the 127 max DE length
+ let res = actions_de.try_add_container(c.into());
+ debug_assert!(res.is_none());
+ actions_de
+ })
+ }
+
+ /// Initializes an actions de with empty contents
+ fn new_empty() -> Self {
+ let internal = ArrayVecOption::default();
+ Self { containers: internal, len: 0 }
+ }
+
+ /// Appends a container to the DE, returning back the container in the event that max length of
+ /// a DE would be exceeded or returning `None` in the case that it has been successfully added
+ fn try_add_container(&mut self, container: ActionsContainer) -> Option<ActionsContainer> {
+ if self.len + container.payload.len() + 1 > MAX_DE_LEN {
+ Some(container)
+ } else {
+ self.len = self.len + container.payload.len() + 1;
+ self.containers.push(container);
+ None
+ }
+ }
+}
+
+/// The actions container type saved in the DE which can represent any of the 3 existing container
+/// types as specified in its container_type field. This can be converted into from any of the 3
+/// more specific actions container types and is what is stored in `ActionsDataElement`
+#[derive(Debug, Copy, Clone)]
+struct ActionsContainer {
+ container_type: ContainerType,
+ payload: ArrayVec<[u8; MAX_ACTIONS_CONTAINER_LENGTH]>,
+}
+
+trait ContainerEncoder {
+ const CONTAINER_TYPE: ContainerType;
+ /// encodes the bytes of the container after the initial container header
+ fn encoded_payload(&self) -> ArrayVec<[u8; MAX_ACTIONS_CONTAINER_LENGTH]>;
+}
+
+impl<C: ContainerEncoder> From<C> for ActionsContainer {
+ fn from(value: C) -> Self {
+ Self { container_type: C::CONTAINER_TYPE, payload: value.encoded_payload() }
+ }
+}
+
+#[derive(Debug)]
+struct DeltaEncodedContainer {
+ payload: ArrayVec<[u8; MAX_ACTIONS_CONTAINER_LENGTH]>,
+}
+
+impl DeltaEncodedContainer {
+ fn try_from_actions(
+ actions: ArrayVec<[ActionId; MAX_ACTIONS_CONTAINER_LENGTH]>,
+ ) -> Result<Self, ActionsDataElementError> {
+ let sorted = sort(actions);
+ let mut payload = ArrayVec::<[u8; MAX_ACTIONS_CONTAINER_LENGTH]>::default();
+ let first =
+ u8::try_from(sorted.first().ok_or(ActionsDataElementError::EmptyActions)?.as_u16())
+ .map_err(|_| ActionsDataElementError::ActionIdDeltaOverflow)?;
+ payload.push(first);
+ let remaining = delta_encoding(sorted)?;
+ payload.extend_from_slice(remaining.as_slice());
+ Ok(Self { payload })
+ }
+}
+
+impl ContainerEncoder for DeltaEncodedContainer {
+ const CONTAINER_TYPE: ContainerType = ContainerType::DeltaEncoded;
+ fn encoded_payload(&self) -> ArrayVec<[u8; MAX_ACTIONS_CONTAINER_LENGTH]> {
+ self.payload
+ }
+}
+
+fn sort<const N: usize>(actions: ArrayVec<[ActionId; N]>) -> ArrayVec<[ActionId; N]> {
+ let mut sorted = actions;
+ sorted.sort_unstable_by_key(|x| *x);
+ sorted
+}
+
+/// Calculates the delta encoded for the given sorted collection offset by 1, so an encoding of
+/// 0 represents a delta of 1, and an encoding of 0xFF represents a delta of 256
+fn delta_encoding<const N: usize>(
+ sorted: ArrayVec<[ActionId; N]>,
+) -> Result<ArrayVec<[u8; N]>, ActionsDataElementError> {
+ sorted
+ .iter()
+ .tuple_windows()
+ .filter(|(a, b)| *a != *b)
+ .map(|(a, b)| u8::try_from(b.as_u16() - (a.as_u16() + 1)))
+ .collect::<Result<ArrayVec<[u8; N]>, _>>()
+ .map_err(|_| ActionsDataElementError::ActionIdDeltaOverflow)
+}
+
+#[derive(Debug)]
+struct DeltaEncodedOffsetContainer {
+ payload: ArrayVec<[u8; MAX_ACTIONS_CONTAINER_LENGTH]>,
+}
+
+impl DeltaEncodedOffsetContainer {
+ #[allow(unused)]
+ fn try_from_actions(
+ actions: ArrayVec<[ActionId; MAX_ACTIONS_CONTAINER_LENGTH - 1]>,
+ ) -> Result<Self, ActionsDataElementError> {
+ let sorted = sort(actions);
+ let first_action = sorted.first().ok_or(ActionsDataElementError::EmptyActions)?.as_u16();
+ let mut payload = ArrayVec::<[u8; MAX_ACTIONS_CONTAINER_LENGTH]>::default();
+ let offset = u8::try_from(first_action / 8)
+ .expect("Max action id 2047 divided by 8 is always within range of a valid u8");
+ payload.push(offset);
+ let first_action_encoding = u8::try_from(first_action - u16::from(offset) * 8)
+ .expect("This will always fit into a u8 because of the offset subtraction");
+ payload.push(first_action_encoding);
+
+ let remaining = delta_encoding(sorted)?;
+ payload.extend_from_slice(remaining.as_slice());
+ Ok(Self { payload })
+ }
+}
+
+impl ContainerEncoder for DeltaEncodedOffsetContainer {
+ const CONTAINER_TYPE: ContainerType = ContainerType::DeltaEncodedWithOffset;
+ fn encoded_payload(&self) -> ArrayVec<[u8; MAX_ACTIONS_CONTAINER_LENGTH]> {
+ self.payload
+ }
+}
+
+/// The maximum amount of expressible action_ids in a single bit vector container.
+/// Each byte can hold 8 unique actions with a max of 64 bytes per container.
+const MAX_BIT_VECTOR_ACTIONS: usize = MAX_ACTIONS_CONTAINER_LENGTH * 8;
+
+#[derive(Debug)]
+struct BitVectorOffsetContainer {
+ payload: ArrayVec<[u8; MAX_ACTIONS_CONTAINER_LENGTH]>,
+}
+
+impl BitVectorOffsetContainer {
+ #[allow(unused)]
+ fn try_from_actions(actions: &mut [ActionId]) -> Result<Self, ActionsDataElementError> {
+ if actions.len() > MAX_BIT_VECTOR_ACTIONS {
+ return Err(ActionsDataElementError::TooManyActions);
+ }
+ actions.sort_unstable_by_key(|x| *x);
+
+ let mut payload = ArrayVec::<[u8; MAX_ACTIONS_CONTAINER_LENGTH]>::new();
+ let first = actions.first().ok_or(ActionsDataElementError::EmptyActions)?.as_u16();
+ let offset = u8::try_from(first / 8)
+ .expect("Max action id 2047 divided by 8 is always within range of a valid u8");
+ payload.push(offset);
+
+ let max_value = actions.last().expect("we have verified above that sorted is not empty");
+ let bytes_required = (max_value.as_u16() / 8) + 1 - u16::from(offset);
+ for _ in 0..bytes_required {
+ if payload.try_push(0).is_some() {
+ return Err(ActionsDataElementError::ActionIdOutOfRange);
+ }
+ }
+ actions.iter().for_each(|a| {
+ // which byte this action id belongs in
+ let index = usize::from(a.as_u16() / 8 + 1 - u16::from(offset));
+ // how far the action is shifted from the most significant bit in the byte
+ let shift = a.as_u16() % 8;
+ payload[index] |= 0b1000_0000 >> shift;
+ });
+
+ Ok(Self { payload })
+ }
+}
+
+impl ContainerEncoder for BitVectorOffsetContainer {
+ const CONTAINER_TYPE: ContainerType = ContainerType::BitVectorOffset;
+ fn encoded_payload(&self) -> ArrayVec<[u8; MAX_ACTIONS_CONTAINER_LENGTH]> {
+ self.payload
+ }
+}
+
+/// Errors that can occur constructing an [ActionsDataElement].
+#[derive(Debug, PartialEq, Eq)]
+#[allow(unused)]
+pub enum ActionsDataElementError {
+ /// Must specify at least one action id to encode
+ EmptyActions,
+ /// The provided ActionIds cannot be delta encoded into u8 values
+ ActionIdDeltaOverflow,
+ /// The provided range of action ids cannot be encoded into a single container
+ ActionIdOutOfRange,
+ /// Too many actions provided than what can be encoded into a single container
+ TooManyActions,
+}
+
+impl SingleTypeDataElement for ActionsDataElement {
+ const DE_TYPE: DeType = DeType::const_from(0x06);
+}
+
+impl WriteDataElement for ActionsDataElement {
+ fn de_header(&self) -> DeHeader {
+ DeHeader::new(
+ Self::DE_TYPE,
+ // each containers length is the header byte + the length of its encoded contents
+ self.containers
+ .iter()
+ .map(|a| 1 + a.payload.len())
+ .sum::<usize>()
+ .try_into()
+ .expect("An actions DE will always be <= 127, this is enforced upon creation"),
+ )
+ }
+
+ fn write_de_contents<S: Sink<u8>>(&self, sink: &mut S) -> Option<()> {
+ // write actions container header and bytes for each container in the de
+ let mut encoded_actions_de_bytes = ArrayVec::<[u8; MAX_DE_LEN]>::new();
+ self.containers.iter().for_each(|a| {
+ let header_byte = (u8::from(a.container_type) << 6) | ((a.payload.len() as u8) - 1);
+ // This will not panic because the length of the actions DE is checked during creation
+ // to not exceed the max limit of 127
+ encoded_actions_de_bytes.extend_from_slice(&[header_byte]);
+ encoded_actions_de_bytes.extend_from_slice(a.payload.as_slice());
+ });
+ sink.try_extend_from_slice(encoded_actions_de_bytes.as_slice())
+ }
+}
diff --git a/nearby/presence/np_adv/src/extended/data_elements/actions/tests.rs b/nearby/presence/np_adv/src/extended/data_elements/actions/tests.rs
new file mode 100644
index 0000000..3abdbd1
--- /dev/null
+++ b/nearby/presence/np_adv/src/extended/data_elements/actions/tests.rs
@@ -0,0 +1,636 @@
+// Copyright 2024 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 alloc::collections::VecDeque;
+use alloc::vec;
+use alloc::vec::Vec;
+
+extern crate std;
+
+use crate::credential::book::{CachedSliceCredentialBook, CredentialBook, CredentialBookBuilder};
+use crate::credential::matched::EmptyMatchedCredential;
+use crate::deserialization_arena::DeserializationArena;
+use crate::extended::data_elements::actions::{
+ ActionsContainer, ActionsDataElement, ActionsDataElementError, ActionsDeserializationError,
+ BitVectorOffsetContainer, ContainerEncoder, ContainerType, DeltaEncodedContainer,
+ DeltaEncodedOffsetContainer, DeserializedActionsDE, MAX_ACTIONS_CONTAINER_LENGTH,
+};
+use crate::extended::data_elements::ActionId;
+use crate::extended::de_type::DeType;
+use crate::extended::deserialize::data_element::DataElement;
+use crate::extended::deserialize::{Section, V1AdvertisementContents, V1DeserializedSection};
+use crate::extended::serialize::{
+ AdvBuilder, AdvertisementType, EncodedAdvertisement, SingleTypeDataElement,
+ UnencryptedSectionEncoder,
+};
+use crate::{deserialization_arena, deserialize_advertisement};
+use crypto_provider_default::CryptoProviderImpl;
+use nom::error::ErrorKind;
+use nom::Err::Error;
+use np_hkdf::v1_salt::DataElementOffset;
+use rand::distributions::uniform::SampleRange;
+use rand::distributions::Standard;
+use rand::prelude::Distribution;
+use rand::rngs::StdRng;
+use rand::{Rng, SeedableRng};
+use tinyvec::{array_vec, ArrayVec};
+
+#[test]
+fn actions_de_round_trip() {
+ let mut actions = ArrayVec::<[ActionId; 64]>::new();
+ actions.extend_from_slice([100u16, 500, 300, 600].map(|v| v.try_into().unwrap()).as_slice());
+ let actions_de = ActionsDataElement::try_from_actions(actions).expect("should succeed");
+ let adv = create_adv_with_de(actions_de);
+ let arena = deserialization_arena!();
+ let cred_book = create_empty_cred_book();
+ let contents = deser_into_v1_contents(arena, adv.as_slice(), &cred_book);
+ assert_eq!(0, contents.invalid_sections_count());
+ let sections = contents.sections().collect::<Vec<_>>();
+ assert_eq!(1, sections.len());
+ let section = match §ions[0] {
+ V1DeserializedSection::Plaintext(s) => s,
+ _ => panic!("this is a plaintext adv"),
+ };
+ let data_elements = section.iter_data_elements().collect::<Result<Vec<_>, _>>().unwrap();
+ assert_eq!(1, data_elements.len());
+ let de = &data_elements[0];
+ assert_eq!(ActionsDataElement::DE_TYPE, de.de_type());
+ let actions_de =
+ DeserializedActionsDE::try_from(de).expect("Should succeed since this de is an actions de");
+ assert_eq!(
+ actions_de
+ .collect_action_ids()
+ .iter()
+ .map(|res| res.expect("valid action ids").as_u16())
+ .collect::<Vec<_>>(),
+ vec![100, 300, 500, 600]
+ )
+}
+
+#[test]
+fn randomized_containers_roundtrip_tests() {
+ let mut rng = StdRng::from_entropy();
+ for _ in 0..10_000 {
+ let mut actions_de = ActionsDataElement::new_empty();
+ let mut expected = VecDeque::new();
+ for _num_containers in 0..rng.gen_range(1..MAX_ACTIONS_CONTAINER_LENGTH) {
+ let (container, actions) = gen_random_container(&mut rng);
+ if actions_de.try_add_container(container).is_none() {
+ expected.push_back(actions);
+ }
+ }
+ let adv = create_adv_with_de(actions_de);
+ let arena = deserialization_arena!();
+ let cred_book = create_empty_cred_book();
+ let contents = deser_into_v1_contents(arena, adv.as_slice(), &cred_book);
+ assert_eq!(0, contents.invalid_sections_count());
+ let sections = contents.sections().collect::<Vec<_>>();
+ assert_eq!(1, sections.len());
+ let section = match §ions[0] {
+ V1DeserializedSection::Plaintext(s) => s,
+ _ => panic!("this is a plaintext adv"),
+ };
+ let data_elements = section.iter_data_elements().collect::<Result<Vec<_>, _>>().unwrap();
+ assert_eq!(1, data_elements.len());
+ let de = &data_elements[0];
+ assert_eq!(ActionsDataElement::DE_TYPE, de.de_type());
+ let actions_de = DeserializedActionsDE::try_from(de)
+ .expect("Should succeed since this de is an actions de");
+ assert_eq!(actions_de.containers.len(), expected.len());
+ let mut expected_action_ids =
+ expected.iter().flatten().map(|action| action.as_u16()).collect::<Vec<_>>();
+ expected_action_ids.sort();
+ expected_action_ids.dedup();
+ let mut decoded_action_ids =
+ actions_de.collect_action_ids().iter().map(|x| x.unwrap().as_u16()).collect::<Vec<_>>();
+ decoded_action_ids.sort();
+ decoded_action_ids.dedup();
+ assert_eq!(expected_action_ids, decoded_action_ids);
+ }
+}
+
+#[test]
+fn roundtrip_edge_case_actions_ids_in_last_bit_of_byte() {
+ let mut actions =
+ [1260u16, 1268, 1269, 1273, 1285, 1288, 1302, 1303, 1306, 1308, 1310, 1312, 1320, 1330]
+ .map(|v| ActionId::try_from(v).unwrap());
+ let container = BitVectorOffsetContainer::try_from_actions(actions.as_mut_slice()).unwrap();
+ let mut de = ActionsDataElement::new_empty();
+ assert!(de.try_add_container(container.into()).is_none());
+ let adv = create_adv_with_de(de);
+ let arena = deserialization_arena!();
+ let cred_book = create_empty_cred_book();
+ let contents = deser_into_v1_contents(arena, adv.as_slice(), &cred_book);
+ assert_eq!(0, contents.invalid_sections_count());
+ let sections = contents.sections().collect::<Vec<_>>();
+ assert_eq!(1, sections.len());
+ let section = match §ions[0] {
+ V1DeserializedSection::Plaintext(s) => s,
+ _ => panic!("this is a plaintext adv"),
+ };
+ let data_elements = section.iter_data_elements().collect::<Result<Vec<_>, _>>().unwrap();
+ assert_eq!(1, data_elements.len());
+ let de = &data_elements[0];
+ assert_eq!(ActionsDataElement::DE_TYPE, de.de_type());
+ let actions_de =
+ DeserializedActionsDE::try_from(de).expect("Should succeed since this de is an actions de");
+ let decoded_actions =
+ actions_de.collect_action_ids().iter().map(|c| c.expect("")).collect::<Vec<ActionId>>();
+ assert_eq!(actions.as_slice(), decoded_actions.as_slice())
+}
+
+fn create_adv_with_de(actions_de: ActionsDataElement) -> EncodedAdvertisement {
+ let mut adv_builder = AdvBuilder::new(AdvertisementType::Plaintext);
+ let mut section_builder = adv_builder.section_builder(UnencryptedSectionEncoder).unwrap();
+ section_builder.add_de(|_salt| actions_de).unwrap();
+ section_builder.add_to_advertisement::<CryptoProviderImpl>();
+ adv_builder.into_advertisement()
+}
+
+fn create_empty_cred_book<'a>() -> CachedSliceCredentialBook<'a, EmptyMatchedCredential, 0, 0> {
+ CredentialBookBuilder::<EmptyMatchedCredential>::build_cached_slice_book::<
+ 0,
+ 0,
+ CryptoProviderImpl,
+ >(&[], &[])
+}
+
+pub fn deser_into_v1_contents<'adv, 'cred, B>(
+ arena: DeserializationArena<'adv>,
+ adv: &'adv [u8],
+ cred_book: &'cred B,
+) -> V1AdvertisementContents<'adv, B::Matched>
+where
+ B: CredentialBook<'cred>,
+{
+ deserialize_advertisement::<_, CryptoProviderImpl>(arena, adv, cred_book)
+ .expect("Should be a valid advertisement")
+ .into_v1()
+ .expect("Should be V1")
+}
+
+impl Distribution<ContainerType> for Standard {
+ fn sample<R: Rng + ?Sized>(&self, rng: &mut R) -> ContainerType {
+ match rng.gen_range(0..=2) {
+ 0 => ContainerType::DeltaEncoded,
+ 1 => ContainerType::DeltaEncodedWithOffset,
+ _ => ContainerType::BitVectorOffset,
+ }
+ }
+}
+
+fn gen_actions_in_rng<const N: usize, R: SampleRange<u16> + Clone>(
+ rng: &mut StdRng,
+ gen_range: R,
+) -> ArrayVec<[ActionId; N]> {
+ let mut actions = ArrayVec::new();
+ for _ in 0..rng.gen_range(1..N) {
+ let action_id: u16 = rng.gen_range(gen_range.clone());
+ actions.push(ActionId::try_from(action_id).expect("range is a valid action id"))
+ }
+ actions
+}
+
+fn gen_delta_actions<const N: usize, R: SampleRange<u16> + Clone>(
+ rng: &mut StdRng,
+ gen_range: R,
+) -> ArrayVec<[ActionId; N]> {
+ let mut actions = ArrayVec::new();
+ let mut previous = rng.gen_range(gen_range.clone());
+ actions.push(ActionId::try_from(previous).unwrap());
+ for _ in 0..rng.gen_range(1..N - 1) {
+ // make sure generated actions are within a valid delta range of other actions
+ let lower_bound = previous.saturating_sub(256);
+ let upper_bound = if previous + 256 > 2047 { 2047 } else { previous + 256 };
+ let action_id = rng.gen_range(lower_bound..=upper_bound);
+ actions.push(ActionId::try_from(action_id).unwrap());
+ previous = action_id
+ }
+ actions
+}
+
+fn gen_random_container(rng: &mut StdRng) -> (ActionsContainer, Vec<ActionId>) {
+ let container_type: ContainerType = rng.gen();
+ match container_type {
+ ContainerType::DeltaEncoded => {
+ let actions = gen_delta_actions(rng, 0..=255);
+ (DeltaEncodedContainer::try_from_actions(actions).unwrap().into(), actions.to_vec())
+ }
+ ContainerType::DeltaEncodedWithOffset => {
+ let actions = gen_delta_actions(rng, 0..=2047);
+ (
+ DeltaEncodedOffsetContainer::try_from_actions(actions).unwrap().into(),
+ actions.to_vec(),
+ )
+ }
+ ContainerType::BitVectorOffset => {
+ let range_lower_bound = rng.gen_range(0u16..2047 - 495);
+ let range_upper_bound = range_lower_bound + 495;
+ let mut actions =
+ gen_actions_in_rng::<512, _>(rng, range_lower_bound..=range_upper_bound);
+ (
+ BitVectorOffsetContainer::try_from_actions(actions.as_mut_slice()).unwrap().into(),
+ actions.to_vec(),
+ )
+ }
+ }
+}
+
+#[test]
+fn parse_single_container_delta_encoded() {
+ let bytes = [0x01, 0x64, 0xC7];
+ let actions_de =
+ DeserializedActionsDE::deserialize(&bytes).expect("bytes parse into valid actions DE");
+ let action_containers = &actions_de.containers;
+ assert_eq!(action_containers.len(), 1);
+ let container = action_containers.first().unwrap();
+ assert_eq!(container.container_type, ContainerType::DeltaEncoded);
+ assert_eq!(container.payload, &[0x64, 0xC7]);
+
+ let action_ids: Vec<_> = container
+ .iter_action_ids()
+ .map(|id| id.expect("test data contains action ids within valid range").as_u16())
+ .collect();
+ assert_eq!(action_ids, vec![100u16, 300]);
+ assert_eq!(
+ actions_de.collect_action_ids(),
+ vec![Ok(100u16.try_into().unwrap()), Ok(300.try_into().unwrap())]
+ );
+}
+
+#[test]
+fn encode_single_container_delta_encoded() {
+ let actions = array_vec!([ActionId; 64] => ActionId::try_from(100).unwrap(), ActionId::try_from(300).unwrap());
+ let container = DeltaEncodedContainer::try_from_actions(actions).expect("Should succeed");
+ assert_eq!(container.encoded_payload().as_slice(), &[0x64, 0xC7])
+}
+
+#[test]
+fn encode_single_container_delta_encoded_delta_1() {
+ let actions = array_vec!([ActionId; 64] => ActionId::try_from(100).unwrap(), ActionId::try_from(101).unwrap());
+ let container = DeltaEncodedContainer::try_from_actions(actions).expect("Should succeed");
+ assert_eq!(container.encoded_payload().as_slice(), &[0x64, 0x00])
+}
+
+#[test]
+fn encode_single_container_delta_encoded_max_delta() {
+ let actions = array_vec!([ActionId; 64] => ActionId::try_from(100).unwrap(), ActionId::try_from(356).unwrap());
+ let container = DeltaEncodedContainer::try_from_actions(actions).expect("Should succeed");
+ assert_eq!(container.encoded_payload().as_slice(), &[0x64, 0xFF])
+}
+
+#[test]
+fn parse_single_container_delta_encoded_with_offset() {
+ let bytes = [0x42, 0x06, 0x02, 0x66];
+ let actions_de =
+ DeserializedActionsDE::deserialize(&bytes).expect("provided bytes are valid actions de");
+ assert_eq!(actions_de.containers.len(), 1);
+ let container = actions_de.containers.first().unwrap();
+ assert_eq!(container.container_type, ContainerType::DeltaEncodedWithOffset);
+ assert_eq!(container.offset, 48);
+ let actions: Vec<u16> = container
+ .iter_action_ids()
+ .map(|action| action.expect("Encoded id is in valid range").as_u16())
+ .collect();
+ assert_eq!(actions, vec![50, 153]);
+ assert_eq!(
+ actions_de.collect_action_ids(),
+ vec![Ok(50.try_into().unwrap()), Ok(153.try_into().unwrap())]
+ );
+}
+
+#[test]
+fn encode_single_container_delta_encoded_with_offset() {
+ let actions = array_vec!([ActionId; 63] => ActionId::try_from(50).unwrap(), ActionId::try_from(153).unwrap());
+ let container = DeltaEncodedOffsetContainer::try_from_actions(actions).expect("Should succeed");
+ assert_eq!(container.encoded_payload().as_slice(), &[0x06, 0x02, 0x66])
+}
+
+#[test]
+fn encode_single_container_delta_encoded_with_offset_delta_of_1() {
+ let actions = array_vec!([ActionId; 63] => ActionId::try_from(50).unwrap(), ActionId::try_from(51).unwrap());
+ let container = DeltaEncodedOffsetContainer::try_from_actions(actions).expect("Should succeed");
+ assert_eq!(container.encoded_payload().as_slice(), &[0x06, 0x02, 0x00])
+}
+
+#[test]
+fn encode_single_container_delta_encoded_with_offset_max_delta() {
+ let actions = array_vec!([ActionId; 63] => ActionId::try_from(50).unwrap(), ActionId::try_from(306).unwrap());
+ let container = DeltaEncodedOffsetContainer::try_from_actions(actions).expect("Should succeed");
+ assert_eq!(container.encoded_payload().as_slice(), &[0x06, 0x02, 0xFF])
+}
+
+#[test]
+fn encode_single_container_delta_encoded_with_offset_delta_of_0() {
+ let actions = array_vec!([ActionId; 63] => ActionId::try_from(48).unwrap(), ActionId::try_from(304).unwrap());
+ let container = DeltaEncodedOffsetContainer::try_from_actions(actions).expect("Should succeed");
+ assert_eq!(container.encoded_payload().as_slice(), &[0x06, 0x00, 0xFF])
+}
+
+#[test]
+fn parse_single_container_bit_vector_offset() {
+ let bytes = [0x82, 0x01, 0x5C, 0x08];
+ let actions_de =
+ DeserializedActionsDE::deserialize(&bytes).expect("provided bytes are valid actions de");
+ assert_eq!(actions_de.containers.len(), 1);
+ let container = actions_de.containers.first().unwrap();
+ assert_eq!(container.container_type, ContainerType::BitVectorOffset);
+ assert_eq!(container.offset, 8);
+ let actions: Vec<u16> = container
+ .iter_action_ids()
+ .map(|action| action.expect("Encoded id is in valid range").as_u16())
+ .collect();
+ assert_eq!(actions, vec![9, 11, 12, 13, 20]);
+}
+
+#[test]
+fn encode_single_container_bit_vector_offset() {
+ let mut actions = [9, 11, 12, 13, 20].map(|x| ActionId::try_from(x).unwrap());
+ let container =
+ BitVectorOffsetContainer::try_from_actions(actions.as_mut_slice()).expect("Should succeed");
+ assert_eq!(container.encoded_payload().as_slice(), &[0x01, 0x5C, 0x08])
+}
+
+#[test]
+fn encode_single_container_bit_vector_offset_offset_of_0() {
+ let mut actions = [0, 3, 5, 7].map(|x| ActionId::try_from(x).unwrap());
+ let container =
+ BitVectorOffsetContainer::try_from_actions(actions.as_mut_slice()).expect("Should succeed");
+ assert_eq!(container.encoded_payload().as_slice(), &[0x00, 0b1001_0101])
+}
+
+#[test]
+fn encode_single_container_bit_vector_offset_max_offset() {
+ let mut actions = [2046, 2047].map(|x| ActionId::try_from(x).unwrap());
+ let container =
+ BitVectorOffsetContainer::try_from_actions(actions.as_mut_slice()).expect("Should succeed");
+ assert_eq!(container.encoded_payload().as_slice(), &[0xFF, 0b0000_0011])
+}
+
+#[test]
+fn encode_single_container_bit_vector_max_range() {
+ // A container is at most 64 bytes. One byte is always used for the offset so that leaves 63 total bytes of
+ // bit vector encoded action ids where each bit represents an action. Since the first byte represent 0-7,
+ // byte 63 represents actions 496-503, making 503 the maximum possible action id which can be encoded
+ // in this container
+ let mut actions = [0, 503].map(|x| ActionId::try_from(x).unwrap());
+ let container =
+ BitVectorOffsetContainer::try_from_actions(actions.as_mut_slice()).expect("Should succeed");
+ let mut expected = [0; 64];
+ expected[1] = 0b1000_0000;
+ expected[63] = 0b0000_0001;
+ assert_eq!(container.encoded_payload().as_slice(), &expected);
+}
+
+#[test]
+fn encode_single_container_bit_vector_invalid_range() {
+ let mut actions = [1, 504].map(|x| ActionId::try_from(x).unwrap());
+ let err = BitVectorOffsetContainer::try_from_actions(actions.as_mut_slice()).unwrap_err();
+ assert_eq!(err, ActionsDataElementError::ActionIdOutOfRange)
+}
+
+#[test]
+fn encode_single_container_bit_vector_max_range_with_offset() {
+ // now 559 should still be in range since this is offset by 7 bytes
+ let mut actions = [56, 559].map(|x| ActionId::try_from(x).unwrap());
+ let container =
+ BitVectorOffsetContainer::try_from_actions(actions.as_mut_slice()).expect("Should succeed");
+ let mut expected = [0; 64];
+ expected[0] = 7;
+ expected[1] = 0b1000_0000;
+ expected[63] = 0b0000_0001;
+ assert_eq!(container.encoded_payload().as_slice(), &expected);
+}
+
+#[test]
+fn encode_single_container_bit_vector_out_of_range_with_offset() {
+ let mut actions = [56, 560].map(|x| ActionId::try_from(x).unwrap());
+ let err = BitVectorOffsetContainer::try_from_actions(actions.as_mut_slice()).unwrap_err();
+ assert_eq!(err, ActionsDataElementError::ActionIdOutOfRange);
+}
+
+#[test]
+fn encode_bit_vector_max_amount_of_ids() {
+ let mut actions_ids = [0xFF; 512].map(|x| ActionId::try_from(x).unwrap());
+ assert!(BitVectorOffsetContainer::try_from_actions(actions_ids.as_mut_slice()).is_ok())
+}
+
+#[test]
+fn encode_bit_vector_too_many_actionids() {
+ let mut actions_ids = [0xFF; 513].map(|x| ActionId::try_from(x).unwrap());
+ assert_eq!(
+ BitVectorOffsetContainer::try_from_actions(actions_ids.as_mut_slice()).unwrap_err(),
+ ActionsDataElementError::TooManyActions
+ );
+}
+
+#[test]
+fn parse_multiple_containers() {
+ let bytes = [
+ vec![0x01, 0x64, 0xC7], // delta encoded container
+ vec![0x42, 0x06, 0x02, 0x66], // delta encoded with offset
+ vec![0x82, 0x01, 0x5C, 0x08], // bit vector offset
+ ]
+ .concat();
+ let de = DeserializedActionsDE::deserialize(&bytes).expect("bytes are valid de");
+ assert_eq!(de.containers.len(), 3);
+ assert_eq!(de.containers[0].container_type, ContainerType::DeltaEncoded);
+ assert_eq!(de.containers[1].container_type, ContainerType::DeltaEncodedWithOffset);
+ assert_eq!(de.containers[2].container_type, ContainerType::BitVectorOffset);
+
+ let action_ids = de.collect_action_ids();
+ assert_eq!(
+ action_ids
+ .iter()
+ .map(|a| { a.expect("action_ids encoded are in valid range").as_u16() })
+ .collect::<Vec<_>>(),
+ vec![100, 300, 50, 153, 9, 11, 12, 13, 20]
+ );
+}
+
+#[test]
+fn try_from_invalid_type_code() {
+ let data = [];
+ let de = DataElement::new(DataElementOffset::from(0), DeType::const_from(5), &data);
+ assert_eq!(
+ DeserializedActionsDE::try_from(&de).unwrap_err(),
+ ActionsDeserializationError::InvalidTypeCode
+ );
+}
+
+#[test]
+fn try_from_invalid_container_length_empty_after_subheader() {
+ let data = [0x40, 0x64];
+ let de = DataElement::new(DataElementOffset::from(0), DeType::const_from(6), &data);
+ assert_eq!(
+ DeserializedActionsDE::try_from(&de).unwrap_err(),
+ ActionsDeserializationError::InvalidContainerLength
+ );
+}
+
+#[test]
+fn try_from_invalid_container_length_not_enough_data() {
+ let data = [0x02, 0x64, 0xC8];
+ let de = DataElement::new(DataElementOffset::from(0), DeType::const_from(6), &data);
+ assert_eq!(
+ DeserializedActionsDE::try_from(&de).unwrap_err(),
+ ActionsDeserializationError::InvalidContainerLength
+ );
+}
+
+#[test]
+fn try_from_invalid_container_type() {
+ let data = [0xC0, 0x64];
+ let de = DataElement::new(DataElementOffset::from(0), DeType::const_from(6), &data);
+ assert_eq!(
+ DeserializedActionsDE::try_from(&de).unwrap_err(),
+ ActionsDeserializationError::InvalidContainerType
+ );
+}
+
+#[test]
+fn parse_single_byte_delta_encoding() {
+ let bytes = [0x00, 0x64];
+ let actions_de = DeserializedActionsDE::deserialize(&bytes).unwrap();
+ let action_containers = &actions_de.containers;
+ assert_eq!(action_containers.len(), 1);
+ let container = action_containers.first().unwrap();
+ assert_eq!(container.container_type, ContainerType::DeltaEncoded);
+ assert_eq!(container.payload, &[0x64])
+}
+
+#[test]
+fn parse_single_byte_delta_encoding_offset_should_fail() {
+ let bytes = [0x40, 0x64];
+ let err = DeserializedActionsDE::deserialize(&bytes).unwrap_err();
+ assert_eq!(err, Error(nom::error::Error { input: &[100][..], code: ErrorKind::Verify }));
+}
+
+#[test]
+fn parse_single_container_max_length() {
+ let mut bytes = vec![0x3F];
+ bytes.extend_from_slice(&[0x01; 64]);
+
+ let actions_de =
+ DeserializedActionsDE::deserialize(&bytes).expect("bytes parse into valid actions DE");
+ let action_containers = &actions_de.containers;
+ assert_eq!(action_containers.len(), 1);
+ let container = action_containers.first().unwrap();
+ assert_eq!(container.container_type, ContainerType::DeltaEncoded);
+ assert_eq!(container.payload, &[0x01; 64]);
+}
+
+#[test]
+fn parse_single_byte_bit_vector_offset_should_fail() {
+ let bytes = [0x80, 0x64];
+ let err = DeserializedActionsDE::deserialize(&bytes).unwrap_err();
+ assert_eq!(err, Error(nom::error::Error { input: &[100][..], code: ErrorKind::Verify }));
+}
+
+#[test]
+fn parse_actions_de_invalid_length_bytes() {
+ let bytes = [0x02, 0x64, 0xC8];
+ let err = DeserializedActionsDE::deserialize(&bytes).unwrap_err();
+ assert_eq!(err, Error(nom::error::Error { input: &[100u8, 200][..], code: ErrorKind::Eof }))
+}
+
+#[test]
+fn parse_actions_de_extra_bytes() {
+ let bytes = [0x01, 0x64, 0xC8, 0xFF];
+ let err = DeserializedActionsDE::deserialize(&bytes).unwrap_err();
+ assert_eq!(err, Error(nom::error::Error { input: &[255u8][..], code: ErrorKind::Eof }))
+}
+
+#[test]
+fn parse_actions_de_invalid_container_type() {
+ let bytes = [0xC2, 0x64, 0xC8];
+ let err = DeserializedActionsDE::deserialize(&bytes).unwrap_err();
+ assert_eq!(
+ err,
+ Error(nom::error::Error { input: &[0xC2, 0x64, 0xC8][..], code: ErrorKind::MapOpt })
+ )
+}
+
+#[test]
+fn encode_empty_actions() {
+ let actions = ArrayVec::new();
+ let err = ActionsDataElement::try_from_actions(actions).unwrap_err();
+ assert_eq!(err, ActionsDataElementError::EmptyActions);
+}
+
+#[test]
+fn encode_empty_delta_container() {
+ let actions = ArrayVec::new();
+ let err = DeltaEncodedContainer::try_from_actions(actions).unwrap_err();
+ assert_eq!(err, ActionsDataElementError::EmptyActions);
+}
+
+#[test]
+fn encode_empty_delta_offset_container() {
+ let actions = ArrayVec::new();
+ let err = DeltaEncodedOffsetContainer::try_from_actions(actions).unwrap_err();
+ assert_eq!(err, ActionsDataElementError::EmptyActions);
+}
+
+#[test]
+fn encode_empty_bit_vector_offset_container() {
+ let err = BitVectorOffsetContainer::try_from_actions(&mut []).unwrap_err();
+ assert_eq!(err, ActionsDataElementError::EmptyActions);
+}
+
+#[test]
+fn encode_basic_delta_encoding() {
+ let mut actions = ArrayVec::<[ActionId; 64]>::new();
+ actions.extend_from_slice([100u16, 500, 300, 600].map(|v| v.try_into().unwrap()).as_slice());
+ let result = ActionsDataElement::try_from_actions(actions).expect("should succeed");
+ assert_eq!(result.containers[0].container_type, ContainerType::DeltaEncoded);
+ assert_eq!(result.containers[0].payload.as_slice(), &[100, 199, 199, 99]);
+}
+
+#[test]
+fn encode_basic_delta_encoding_id_out_of_range() {
+ let mut actions = ArrayVec::<[ActionId; 64]>::new();
+ actions.extend_from_slice([256].map(|v| v.try_into().unwrap()).as_slice());
+ let err = ActionsDataElement::try_from_actions(actions).unwrap_err();
+ assert_eq!(err, ActionsDataElementError::ActionIdDeltaOverflow);
+}
+
+#[test]
+fn encode_basic_delta_encoding_max_id() {
+ let mut actions = ArrayVec::<[ActionId; 64]>::new();
+ actions.extend_from_slice([255].map(|v| v.try_into().unwrap()).as_slice());
+ let de = ActionsDataElement::try_from_actions(actions).expect("should succeed");
+ assert_eq!(de.containers[0].container_type, ContainerType::DeltaEncoded);
+ assert_eq!(de.containers[0].payload.as_slice(), &[255]);
+}
+
+#[test]
+fn encode_basic_delta_encoding_duplicate_ids() {
+ let mut actions = ArrayVec::<[ActionId; 64]>::new();
+ actions
+ .extend_from_slice([1, 1, 1, 6, 3, 3, 6, 3, 3].map(|v| v.try_into().unwrap()).as_slice());
+ let de = ActionsDataElement::try_from_actions(actions).expect("should succeed");
+ assert_eq!(de.containers[0].container_type, ContainerType::DeltaEncoded);
+ assert_eq!(de.containers[0].payload.as_slice(), &[1, 1, 2]);
+}
+
+#[test]
+fn encode_basic_delta_encoding_delta_out_of_range() {
+ let mut actions = ArrayVec::<[ActionId; 64]>::new();
+ actions.extend_from_slice([100, 400].map(|v| v.try_into().unwrap()).as_slice());
+ let err = ActionsDataElement::try_from_actions(actions).unwrap_err();
+ assert_eq!(err, ActionsDataElementError::ActionIdDeltaOverflow);
+}
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 2989966..a5b7f91 100644
--- a/nearby/presence/np_adv/src/extended/data_elements/mod.rs
+++ b/nearby/presence/np_adv/src/extended/data_elements/mod.rs
@@ -29,6 +29,11 @@
use array_view::ArrayView;
use sink::Sink;
+mod actions;
+pub use actions::ActionId;
+pub use actions::ActionsDataElement;
+pub use actions::DeserializedActionsDE;
+
#[cfg(test)]
mod tests;
@@ -102,44 +107,6 @@
}
}
-/// List of actions
-pub struct ActionsDataElement {
- actions: tinyvec::ArrayVec<[u8; MAX_DE_LEN]>,
-}
-
-impl ActionsDataElement {
- /// Returns `Some` if the actions will fit in a DE, `None` otherwise
- pub fn try_from_actions(actions: &[u8]) -> Result<Self, ActionsDataElementError> {
- let mut de = Self { actions: tinyvec::ArrayVec::new() };
-
- de.actions
- .try_extend_from_slice(actions)
- .map(|_| de)
- .ok_or(ActionsDataElementError::ActionsTooLong)
- }
-}
-
-impl SingleTypeDataElement for ActionsDataElement {
- const DE_TYPE: DeType = DeType::const_from(0x06);
-}
-
-impl WriteDataElement for ActionsDataElement {
- fn de_header(&self) -> DeHeader {
- DeHeader::new(Self::DE_TYPE, self.actions.len().try_into().expect("always <= max length"))
- }
-
- fn write_de_contents<S: Sink<u8>>(&self, sink: &mut S) -> Option<()> {
- sink.try_extend_from_slice(&self.actions)
- }
-}
-
-/// Errors that can occur constructing an [ActionsDataElement].
-#[derive(Debug, PartialEq, Eq)]
-pub enum ActionsDataElementError {
- /// Too many action bytes.
- ActionsTooLong,
-}
-
/// Context sync sequence number
pub struct ContextSyncSeqNumDataElement {
num: ContextSyncSeqNum,
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 018f92c..868a127 100644
--- a/nearby/presence/np_adv/src/extended/data_elements/tests.rs
+++ b/nearby/presence/np_adv/src/extended/data_elements/tests.rs
@@ -16,11 +16,16 @@
extern crate std;
-use super::*;
+use tinyvec::ArrayVec;
+
+use crypto_provider_default::CryptoProviderImpl;
+
+use crate::extended::data_elements::actions::{ActionId, ActionsDataElement};
use crate::extended::serialize::{section_tests::SectionBuilderExt, AdvBuilder};
use crate::extended::serialize::{AdvertisementType, UnencryptedSectionEncoder};
use crate::extended::V1_ENCODING_UNENCRYPTED;
-use crypto_provider_default::CryptoProviderImpl;
+
+use super::*;
#[test]
fn serialize_tx_power_de() {
@@ -40,40 +45,33 @@
);
}
-#[test]
-fn serialize_actions_de_empty() {
- let mut adv_builder = AdvBuilder::new(AdvertisementType::Plaintext);
- let mut section_builder = adv_builder.section_builder(UnencryptedSectionEncoder).unwrap();
-
- section_builder.add_de_res(|_| ActionsDataElement::try_from_actions(&[])).unwrap();
-
- assert_eq!(
- &[
- V1_ENCODING_UNENCRYPTED, //header
- 1, // section len
- 0x06, // len 0 type 0x06
- ],
- section_builder.into_section::<CryptoProviderImpl>().as_slice()
- );
+fn actions_ids_from_u16_collection<const N: usize>(actions: [u16; N]) -> ArrayVec<[ActionId; 64]> {
+ let mut result = ArrayVec::new();
+ result.extend_from_slice(&actions.map(|x| x.try_into().unwrap()));
+ result
}
-#[rustfmt::skip]
#[test]
fn serialize_actions_de_non_empty() {
let mut adv_builder = AdvBuilder::new(AdvertisementType::Plaintext);
- let mut section_builder =
- adv_builder.section_builder(UnencryptedSectionEncoder).unwrap();
+ let mut section_builder = adv_builder.section_builder(UnencryptedSectionEncoder).unwrap();
section_builder
- .add_de_res(|_| ActionsDataElement::try_from_actions(&[1, 1, 2, 3, 5, 8]))
+ .add_de_res(|_| {
+ ActionsDataElement::try_from_actions(actions_ids_from_u16_collection([
+ 1, 1, 2, 3, 5, 8, // fibonacci, of course
+ ]))
+ })
.unwrap();
+ #[rustfmt::skip]
assert_eq!(
&[
V1_ENCODING_UNENCRYPTED,
7, // section len
0x66, // len 6 type 0x06
- 1, 1, 2, 3, 5, 8 // fibonacci, of course
+ 0b00000100, // container type and len TTLLLLLL
+ 1, 0, 0, 1, 2 // de-duped delta encoded fibonacci
],
section_builder.into_section::<CryptoProviderImpl>().as_slice()
);
@@ -196,8 +194,10 @@
}
mod coverage_gaming {
- use super::*;
use alloc::format;
+
+ use super::*;
+
#[test]
fn de_type_const_from() {
let _ = DeType::const_from(3);
@@ -214,13 +214,6 @@
}
#[test]
- fn actions_de_error_derives() {
- let err = ActionsDataElementError::ActionsTooLong;
- let _ = format!("{:?}", err);
- assert_eq!(err, err);
- }
-
- #[test]
fn generic_data_element_debug() {
let generic =
GenericDataElement::try_from(DeType::from(1000_u32), &[10, 11, 12, 13]).unwrap();
diff --git a/nearby/presence/np_adv/src/extended/de_type.rs b/nearby/presence/np_adv/src/extended/de_type.rs
index baefe6a..be26555 100644
--- a/nearby/presence/np_adv/src/extended/de_type.rs
+++ b/nearby/presence/np_adv/src/extended/de_type.rs
@@ -23,7 +23,7 @@
impl DeType {
/// A `const` equivalent to `From<u32>` since trait methods can't yet be const.
- pub(crate) const fn const_from(value: u32) -> Self {
+ pub const fn const_from(value: u32) -> Self {
Self { code: value }
}
diff --git a/nearby/presence/np_adv/src/extended/deserialize/data_element/mod.rs b/nearby/presence/np_adv/src/extended/deserialize/data_element/mod.rs
index e717fa1..b90de8a 100644
--- a/nearby/presence/np_adv/src/extended/deserialize/data_element/mod.rs
+++ b/nearby/presence/np_adv/src/extended/deserialize/data_element/mod.rs
@@ -16,6 +16,7 @@
use crate::extended::{de_requires_extended_bit, de_type::DeType, deserialize, DeLength};
use array_view::ArrayView;
+use core::fmt;
use nom::{branch, bytes, combinator, error, number, sequence};
use np_hkdf::v1_salt;
@@ -34,6 +35,22 @@
}
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
+ }
+
pub(crate) fn new(
offset: v1_salt::DataElementOffset,
de_type: DeType,
@@ -41,6 +58,17 @@
) -> Self {
Self { offset, de_type, contents }
}
+
+ /// Exposes the ability to create a DE for testing purposes, real clients should only obtain
+ /// one by deserializing an advertisement
+ #[cfg(feature = "testing")]
+ pub fn new_for_testing(
+ offset: v1_salt::DataElementOffset,
+ de_type: DeType,
+ contents: &'adv [u8],
+ ) -> Self {
+ Self { offset, de_type, contents }
+ }
}
impl DeHeader {
@@ -144,23 +172,6 @@
}
}
-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
- }
-}
-
/// 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
@@ -225,6 +236,19 @@
NomError(error::ErrorKind),
}
+impl fmt::Display for DataElementParseError {
+ fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+ match self {
+ DataElementParseError::UnexpectedDataAfterEnd => write!(f, "Unexpected data after end"),
+ DataElementParseError::TooManyDataElements => write!(f, "Too many data elements"),
+ DataElementParseError::NomError(_) => write!(f, "Nom error"),
+ }
+ }
+}
+
+#[cfg(feature = "std")]
+impl std::error::Error for DataElementParseError {}
+
/// Deserialize-specific version of a DE header that incorporates the header length.
/// This is needed for encrypted identities that need to construct a slice of everything in the
/// section following the identity DE header.
diff --git a/nearby/presence/np_adv/src/extended/deserialize/mod.rs b/nearby/presence/np_adv/src/extended/deserialize/mod.rs
index c6c7ba4..123cee6 100644
--- a/nearby/presence/np_adv/src/extended/deserialize/mod.rs
+++ b/nearby/presence/np_adv/src/extended/deserialize/mod.rs
@@ -38,7 +38,7 @@
},
},
salt::MultiSalt,
- V1IdentityToken, NP_V1_ADV_MAX_ENCRYPTED_SECTION_COUNT,
+ V1IdentityToken, NP_V1_ADV_MAX_SECTION_COUNT,
},
header::V1AdvHeader,
AdvDeserializationError, AdvDeserializationErrorDetailsHazmat,
@@ -94,9 +94,9 @@
}
/// A section deserialized from a V1 advertisement.
-pub trait Section<'adv, E: Debug> {
+pub trait Section<'adv> {
/// The iterator type used to iterate over data elements
- type Iterator: Iterator<Item = Result<DataElement<'adv>, E>>;
+ type Iterator: Iterator<Item = Result<DataElement<'adv>, DataElementParseError>>;
/// 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
@@ -106,7 +106,7 @@
/// 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>
+ fn collect_data_elements(&self) -> Result<Vec<DataElement<'adv>>, DataElementParseError>
where
Self: Sized,
{
@@ -162,7 +162,7 @@
}
}
-impl<'adv> Section<'adv, DataElementParseError> for DecryptedSection<'adv> {
+impl<'adv> Section<'adv> for DecryptedSection<'adv> {
type Iterator = DataElementParsingIterator<'adv>;
fn iter_data_elements(&self) -> Self::Iterator {
@@ -225,14 +225,10 @@
/// section ordering as they appeared within the original advertisement to ensure
/// that the fully-deserialized advertisement may be correctly reconstructed.
struct SectionsInProcessing<'adv, M: MatchedCredential> {
- deserialized_sections: 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 },
- >,
+ deserialized_sections:
+ ArrayVecOption<(usize, V1DeserializedSection<'adv, M>), { NP_V1_ADV_MAX_SECTION_COUNT }>,
+ encrypted_sections:
+ ArrayVecOption<(usize, ResolvableCiphertextSection<'adv>), { NP_V1_ADV_MAX_SECTION_COUNT }>,
malformed_sections_count: usize,
}
@@ -387,16 +383,13 @@
/// The contents of a deserialized and decrypted V1 advertisement.
#[derive(Debug, PartialEq, Eq)]
pub struct V1AdvertisementContents<'adv, M: MatchedCredential> {
- sections: ArrayVecOption<V1DeserializedSection<'adv, M>, NP_V1_ADV_MAX_ENCRYPTED_SECTION_COUNT>,
+ sections: ArrayVecOption<V1DeserializedSection<'adv, M>, NP_V1_ADV_MAX_SECTION_COUNT>,
invalid_sections: usize,
}
impl<'adv, M: MatchedCredential> V1AdvertisementContents<'adv, M> {
fn new(
- sections: ArrayVecOption<
- V1DeserializedSection<'adv, M>,
- NP_V1_ADV_MAX_ENCRYPTED_SECTION_COUNT,
- >,
+ sections: ArrayVecOption<V1DeserializedSection<'adv, M>, NP_V1_ADV_MAX_SECTION_COUNT>,
invalid_sections: usize,
) -> Self {
Self { sections, invalid_sections }
@@ -406,7 +399,7 @@
/// which could be successfully deserialized and decrypted
pub fn into_sections(
self,
- ) -> ArrayVecOption<V1DeserializedSection<'adv, M>, NP_V1_ADV_MAX_ENCRYPTED_SECTION_COUNT> {
+ ) -> ArrayVecOption<V1DeserializedSection<'adv, M>, NP_V1_ADV_MAX_SECTION_COUNT> {
self.sections
}
@@ -431,6 +424,18 @@
Decrypted(WithMatchedCredential<M, DecryptedSection<'adv>>),
}
+// Allow easy DE iteration if the user doesn't care which kind of section it is
+impl<'adv, M: MatchedCredential> Section<'adv> for V1DeserializedSection<'adv, M> {
+ type Iterator = DataElementParsingIterator<'adv>;
+
+ fn iter_data_elements(&self) -> Self::Iterator {
+ match self {
+ V1DeserializedSection::Plaintext(p) => p.iter_data_elements(),
+ V1DeserializedSection::Decrypted(d) => d.contents().iter_data_elements(),
+ }
+ }
+}
+
/// The level of integrity protection in an encrypted section
#[derive(Clone, Copy, PartialEq, Eq, Debug)]
pub enum VerificationMode {
diff --git a/nearby/presence/np_adv/src/extended/deserialize/section/intermediate/mod.rs b/nearby/presence/np_adv/src/extended/deserialize/section/intermediate/mod.rs
index f998299..707b593 100644
--- a/nearby/presence/np_adv/src/extended/deserialize/section/intermediate/mod.rs
+++ b/nearby/presence/np_adv/src/extended/deserialize/section/intermediate/mod.rs
@@ -29,14 +29,13 @@
DataElementParsingIterator, Section, SectionMic,
},
salt::MultiSalt,
- NP_V1_ADV_MAX_ENCRYPTED_SECTION_COUNT, NP_V1_ADV_MAX_PUBLIC_SECTION_COUNT,
+ NP_V1_ADV_MAX_SECTION_COUNT,
},
header::V1AdvHeader,
};
use crypto_provider::CryptoProvider;
use nom::{branch, bytes, combinator, error, multi};
-use crate::extended::deserialize::data_element::DataElementParseError;
#[cfg(feature = "devtools")]
use crate::{
credential::v1::V1DiscoveryCryptoMaterial, deserialization_arena::DeserializationArenaAllocator,
@@ -55,14 +54,14 @@
adv_header: V1AdvHeader,
adv_body: &[u8],
) -> Result<
- ArrayVecOption<IntermediateSection, NP_V1_ADV_MAX_ENCRYPTED_SECTION_COUNT>,
+ ArrayVecOption<IntermediateSection, NP_V1_ADV_MAX_SECTION_COUNT>,
nom::Err<error::Error<&[u8]>>,
> {
combinator::all_consuming(branch::alt((
// Public advertisement
multi::fold_many_m_n(
1,
- NP_V1_ADV_MAX_PUBLIC_SECTION_COUNT,
+ NP_V1_ADV_MAX_SECTION_COUNT,
IntermediateSection::parser_unencrypted_section,
ArrayVecOption::default,
|mut acc, item| {
@@ -73,7 +72,7 @@
// Encrypted advertisement
multi::fold_many_m_n(
1,
- NP_V1_ADV_MAX_ENCRYPTED_SECTION_COUNT,
+ NP_V1_ADV_MAX_SECTION_COUNT,
IntermediateSection::parser_encrypted_with_header(adv_header),
ArrayVecOption::default,
|mut acc, item| {
@@ -225,7 +224,7 @@
}
}
-impl<'adv> Section<'adv, DataElementParseError> for PlaintextSection<'adv> {
+impl<'adv> Section<'adv> for PlaintextSection<'adv> {
type Iterator = DataElementParsingIterator<'adv>;
fn iter_data_elements(&self) -> Self::Iterator {
diff --git a/nearby/presence/np_adv/src/extended/deserialize/section/intermediate/tests.rs b/nearby/presence/np_adv/src/extended/deserialize/section/intermediate/tests.rs
index 84ce763..313a906 100644
--- a/nearby/presence/np_adv/src/extended/deserialize/section/intermediate/tests.rs
+++ b/nearby/presence/np_adv/src/extended/deserialize/section/intermediate/tests.rs
@@ -32,7 +32,7 @@
},
salt::{ShortV1Salt, SHORT_SALT_LEN},
serialize::{AdvBuilder, AdvertisementType},
- NP_V1_ADV_MAX_PUBLIC_SECTION_COUNT, V1_ENCODING_ENCRYPTED_MIC_WITH_EXTENDED_SALT_AND_TOKEN,
+ NP_V1_ADV_MAX_SECTION_COUNT, V1_ENCODING_ENCRYPTED_MIC_WITH_EXTENDED_SALT_AND_TOKEN,
V1_ENCODING_ENCRYPTED_MIC_WITH_SHORT_SALT_AND_TOKEN,
V1_ENCODING_ENCRYPTED_SIGNATURE_WITH_EXTENDED_SALT_AND_TOKEN, V1_ENCODING_UNENCRYPTED,
V1_IDENTITY_TOKEN_LEN,
@@ -92,7 +92,7 @@
#[test]
fn do_deserialize_max_number_of_public_sections() {
let mut adv_builder = AdvBuilder::new(AdvertisementType::Plaintext);
- for _ in 0..NP_V1_ADV_MAX_PUBLIC_SECTION_COUNT {
+ for _ in 0..NP_V1_ADV_MAX_SECTION_COUNT {
let mut section_builder =
adv_builder.section_builder(UnencryptedSectionEncoder).unwrap();
section_builder
@@ -110,13 +110,13 @@
panic!("incorrect header");
};
let sections = parse_sections(v1_header, remaining).unwrap();
- assert_eq!(NP_V1_ADV_MAX_PUBLIC_SECTION_COUNT, sections.len());
+ assert_eq!(NP_V1_ADV_MAX_SECTION_COUNT, sections.len());
}
#[test]
fn max_number_encrypted_sections_mic() {
let mut adv_body = vec![];
- for _ in 0..NP_V1_ADV_MAX_ENCRYPTED_SECTION_COUNT {
+ for _ in 0..NP_V1_ADV_MAX_SECTION_COUNT {
let _ = add_mic_short_salt_section_to_adv(&mut adv_body);
}
let adv_header = V1AdvHeader::new(0x20);
@@ -126,7 +126,7 @@
#[test]
fn max_number_encrypted_sections_sig() {
let mut adv_body = vec![];
- for _ in 0..NP_V1_ADV_MAX_ENCRYPTED_SECTION_COUNT {
+ for _ in 0..NP_V1_ADV_MAX_SECTION_COUNT {
let _ = add_sig_encrpyted_section(&mut adv_body, 5, &[0x55; EXTENDED_SALT_LEN]);
}
let adv_header = V1AdvHeader::new(0x20);
@@ -346,26 +346,23 @@
// 2 sections
let mut adv_body = vec![];
- // section 1 - plaintext - 9 bytes
- adv_body.push(V1_ENCODING_UNENCRYPTED);
- adv_body.push(6 + 3); // section len
- // de 1 byte header, type 5, len 5
- adv_body.extend_from_slice(&[0x55, 0x01, 0x02, 0x03, 0x04, 0x05]);
- // de 2 byte header, type 6, len 1
- adv_body.extend_from_slice(&[0x81, 0x06, 0x01]);
-
- // section 2 - plaintext - 10 bytes
- adv_body.push(V1_ENCODING_UNENCRYPTED);
- adv_body.push(6 + 3); // section len
- // de 1 byte header, type 5, len 5
- adv_body.extend_from_slice(&[0x55, 0x01, 0x02, 0x03, 0x04, 0x05]);
- // de 2 byte header, type 6, len 1
- adv_body.extend_from_slice(&[0x81, 0x06, 0x01]);
+ for _ in 0..=NP_V1_ADV_MAX_SECTION_COUNT {
+ // section 1..=MAX+1 - plaintext - 11 bytes
+ adv_body.push(V1_ENCODING_UNENCRYPTED);
+ adv_body.push(6 + 3); // section len
+ // de 1 byte header, type 5, len 5
+ adv_body.extend_from_slice(&[0x55, 0x01, 0x02, 0x03, 0x04, 0x05]);
+ // de 2 byte header, type 6, len 1
+ adv_body.extend_from_slice(&[0x81, 0x06, 0x01]);
+ }
let adv_header = V1AdvHeader::new(0x20);
assert_eq!(
- nom::Err::Error(error::Error { input: &adv_body[11..], code: error::ErrorKind::Eof }),
+ nom::Err::Error(error::Error {
+ input: &adv_body[(NP_V1_ADV_MAX_SECTION_COUNT * 11)..],
+ code: error::ErrorKind::Eof
+ }),
parse_sections(adv_header, &adv_body).unwrap_err()
);
}
@@ -374,7 +371,7 @@
fn parse_adv_too_many_encrypted() {
// 3 sections
let mut adv_body = vec![];
- for _ in 0..NP_V1_ADV_MAX_ENCRYPTED_SECTION_COUNT + 1 {
+ for _ in 0..NP_V1_ADV_MAX_SECTION_COUNT + 1 {
let _ = add_mic_short_salt_section_to_adv(&mut adv_body);
}
let adv_header = V1AdvHeader::new(0x20);
diff --git a/nearby/presence/np_adv/src/extended/mod.rs b/nearby/presence/np_adv/src/extended/mod.rs
index 4a13ea5..128fbf1 100644
--- a/nearby/presence/np_adv/src/extended/mod.rs
+++ b/nearby/presence/np_adv/src/extended/mod.rs
@@ -33,10 +33,7 @@
- 2;
/// Maximum number of sections in an advertisement
-pub const NP_V1_ADV_MAX_ENCRYPTED_SECTION_COUNT: usize = 8;
-
-/// Maximum number of public sections in an advertisement
-pub const NP_V1_ADV_MAX_PUBLIC_SECTION_COUNT: usize = 1;
+pub const NP_V1_ADV_MAX_SECTION_COUNT: usize = 8;
/// Maximum size of a NP section, including its length header byte
pub const NP_ADV_MAX_SECTION_LEN: usize = NP_ADV_MAX_SECTION_CONTENTS_LEN + 1;
diff --git a/nearby/presence/np_adv/src/extended/salt.rs b/nearby/presence/np_adv/src/extended/salt.rs
index b35275f..2799173 100644
--- a/nearby/presence/np_adv/src/extended/salt.rs
+++ b/nearby/presence/np_adv/src/extended/salt.rs
@@ -17,10 +17,11 @@
use nom::combinator;
use crypto_provider::{aes::ctr::AesCtrNonce, CryptoProvider, CryptoRng, FromCryptoRng};
-use np_hkdf::v1_salt::ExtendedV1Salt;
use crate::helpers::parse_byte_array;
+pub use np_hkdf::v1_salt::ExtendedV1Salt;
+
/// Common behavior for V1 section salts.
pub trait V1Salt: Copy + Into<MultiSalt> {
/// Derive the nonce used for section encryption.
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 f094992..2d3827e 100644
--- a/nearby/presence/np_adv/src/extended/serialize/adv_tests.rs
+++ b/nearby/presence/np_adv/src/extended/serialize/adv_tests.rs
@@ -16,7 +16,6 @@
extern crate std;
use super::*;
-use crate::extended::serialize::section::header::SectionHeader;
use crate::extended::serialize::section_tests::{fill_section_builder, DummyDataElement};
use crate::extended::V1_ENCODING_UNENCRYPTED;
use crypto_provider_default::CryptoProviderImpl;
@@ -95,29 +94,3 @@
}
// TODO tests for other encoding types interacting with maximum possible section len
-
-/// A placeholder identity with a huge prefix
-#[derive(Default, PartialEq, Eq, Debug)]
-struct EnormousIdentity {}
-
-impl SectionEncoder for EnormousIdentity {
- const SUFFIX_LEN: usize = 0;
- const ADVERTISEMENT_TYPE: AdvertisementType = AdvertisementType::Plaintext;
- type DerivedSalt = ();
-
- fn header(&self) -> SectionHeader {
- unimplemented!("Should never be hit")
- }
- fn postprocess<C: CryptoProvider>(
- &mut self,
- _section_header_without_length: &mut [u8],
- _section_len: u8,
- _remaining_content_bytes: &mut [u8],
- ) {
- panic!("should never be called, just used for its huge prefix")
- }
-
- fn de_salt(&self, _de_offset: DataElementOffset) -> Self::DerivedSalt {
- panic!("should never be called, just used for its huge prefix")
- }
-}
diff --git a/nearby/presence/np_adv/src/extended/serialize/mod.rs b/nearby/presence/np_adv/src/extended/serialize/mod.rs
index d94fee2..ad9acab 100644
--- a/nearby/presence/np_adv/src/extended/serialize/mod.rs
+++ b/nearby/presence/np_adv/src/extended/serialize/mod.rs
@@ -109,6 +109,10 @@
//! .try_into().expect("array sizes match")
//! }
//! ```
+
+#[cfg(feature = "std")]
+extern crate std;
+
use core::fmt::{self, Display};
use array_view::ArrayView;
@@ -118,8 +122,7 @@
use crate::extended::{
de_requires_extended_bit, de_type::DeType, serialize::section::EncodedSection, to_array_view,
- DeLength, BLE_5_ADV_SVC_MAX_CONTENT_LEN, NP_ADV_MAX_SECTION_LEN,
- NP_V1_ADV_MAX_ENCRYPTED_SECTION_COUNT, NP_V1_ADV_MAX_PUBLIC_SECTION_COUNT,
+ DeLength, BLE_5_ADV_SVC_MAX_CONTENT_LEN, NP_ADV_MAX_SECTION_LEN, NP_V1_ADV_MAX_SECTION_COUNT,
};
mod section;
@@ -223,7 +226,7 @@
&self,
section_encoder: &SE,
) -> Result<(usize, CapacityLimitedVec<u8, NP_ADV_MAX_SECTION_LEN>), AddSectionError> {
- if self.section_count >= self.advertisement_type.max_sections() {
+ if self.section_count >= NP_V1_ADV_MAX_SECTION_COUNT {
return Err(AddSectionError::MaxSectionCountExceeded);
}
if self.advertisement_type != SE::ADVERTISEMENT_TYPE {
@@ -267,7 +270,7 @@
pub enum AddSectionError {
/// The advertisement doesn't have enough space to hold the minimum size of the section
InsufficientAdvSpace,
- /// The advertisement can only hold a maximum of NP_V1_ADV_MAX_ENCRYPTED_SECTION_COUNT number of sections
+ /// The advertisement can only hold a maximum of NP_V1_ADV_MAX_SECTION_COUNT number of sections
MaxSectionCountExceeded,
/// An incompatible section trying to be added
IncompatibleSectionType,
@@ -280,7 +283,7 @@
write!(f, "The advertisement (max {BLE_5_ADV_SVC_MAX_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")
+ write!(f, "The advertisement can only hold a maximum of {NP_V1_ADV_MAX_SECTION_COUNT} number of sections")
}
AddSectionError::IncompatibleSectionType => {
write!(f, "Public and Encrypted sections cannot be mixed in the same advertisement")
@@ -289,6 +292,9 @@
}
}
+#[cfg(feature = "std")]
+impl std::error::Error for AddSectionError {}
+
/// An encoded NP V1 advertisement, starting with the NP advertisement header byte.
#[derive(Debug, PartialEq, Eq)]
pub struct EncodedAdvertisement {
@@ -316,15 +322,6 @@
Encrypted,
}
-impl AdvertisementType {
- fn max_sections(&self) -> usize {
- match self {
- AdvertisementType::Plaintext => NP_V1_ADV_MAX_PUBLIC_SECTION_COUNT,
- AdvertisementType::Encrypted => NP_V1_ADV_MAX_ENCRYPTED_SECTION_COUNT,
- }
- }
-}
-
/// Derived salt for an individual data element.
pub struct DeSalt {
salt: ExtendedV1Salt,
diff --git a/nearby/presence/np_adv/src/extended/serialize/section/mod.rs b/nearby/presence/np_adv/src/extended/serialize/section/mod.rs
index 8fb76ab..1b8a331 100644
--- a/nearby/presence/np_adv/src/extended/serialize/section/mod.rs
+++ b/nearby/presence/np_adv/src/extended/serialize/section/mod.rs
@@ -12,7 +12,11 @@
// See the License for the specific language governing permissions and
// limitations under the License.
+#[cfg(feature = "std")]
+extern crate std;
+
use array_view::ArrayView;
+use core::{convert, fmt};
use crypto_provider::CryptoProvider;
use np_hkdf::v1_salt::DataElementOffset;
use sink::Sink as _;
@@ -151,8 +155,8 @@
pub fn add_de<W: WriteDataElement, F: FnOnce(SE::DerivedSalt) -> W>(
&mut self,
build_de: F,
- ) -> Result<(), AddDataElementError<()>> {
- self.add_de_res(|derived_salt| Ok::<_, ()>(build_de(derived_salt)))
+ ) -> Result<(), AddDataElementError<convert::Infallible>> {
+ self.add_de_res(|derived_salt| Ok::<_, convert::Infallible>(build_de(derived_salt)))
}
/// Convert a section builder's contents into an encoded section.
@@ -200,5 +204,19 @@
InsufficientSectionSpace,
}
+impl<E: fmt::Display> fmt::Display for AddDataElementError<E> {
+ fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+ match self {
+ AddDataElementError::BuildDeError(e) => write!(f, "Build DE error: {}", e),
+ AddDataElementError::InsufficientSectionSpace => {
+ write!(f, "Insufficient section space")
+ }
+ }
+ }
+}
+
+#[cfg(feature = "std")]
+impl<E: fmt::Debug + fmt::Display> std::error::Error for AddDataElementError<E> {}
+
/// The encoded form of an advertisement section
pub(crate) type EncodedSection = ArrayView<u8, NP_ADV_MAX_SECTION_LEN>;
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 e1e90b7..9fbd039 100644
--- a/nearby/presence/np_adv/src/extended/serialize/section_tests.rs
+++ b/nearby/presence/np_adv/src/extended/serialize/section_tests.rs
@@ -604,10 +604,10 @@
#[test]
fn serialize_max_number_of_public_sections() {
let mut adv_builder = AdvBuilder::new(AdvertisementType::Plaintext);
- for _ in 0..NP_V1_ADV_MAX_PUBLIC_SECTION_COUNT {
+ for _ in 0..NP_V1_ADV_MAX_SECTION_COUNT {
let mut section_builder = adv_builder.section_builder(UnencryptedSectionEncoder).unwrap();
section_builder
- .add_de(|_| DummyDataElement { de_type: 100_u32.into(), data: vec![0; 98] })
+ .add_de(|_| DummyDataElement { de_type: 100_u32.into(), data: vec![0; 27] })
.unwrap();
section_builder.add_to_advertisement::<CryptoProviderImpl>();
}
diff --git a/nearby/presence/np_adv/src/legacy/data_elements/mod.rs b/nearby/presence/np_adv/src/legacy/data_elements/mod.rs
index 5dd0587..762d752 100644
--- a/nearby/presence/np_adv/src/legacy/data_elements/mod.rs
+++ b/nearby/presence/np_adv/src/legacy/data_elements/mod.rs
@@ -81,6 +81,8 @@
/// The invalid length
len: DeEncodedLength,
},
+ /// The same de type code was encountered more than once in an advertisement
+ DuplicateDeTypes,
/// Other parse error, e.g. the adv is truncated
InvalidStructure,
}
diff --git a/nearby/presence/np_adv/src/legacy/data_elements/tests.rs b/nearby/presence/np_adv/src/legacy/data_elements/tests.rs
index d3f3061..a5e8fbf 100644
--- a/nearby/presence/np_adv/src/legacy/data_elements/tests.rs
+++ b/nearby/presence/np_adv/src/legacy/data_elements/tests.rs
@@ -156,9 +156,11 @@
DataElementDeserializeError, DataElementSerializationBuffer, DataElementSerializeError,
DeserializeDataElement, LengthMapper, SerializeDataElement,
};
- use crate::legacy::deserialize::{DataElementDeserializer, LengthError, RawDataElement};
+ use crate::legacy::deserialize::{
+ DataElementDeserializer, Deserialized, LengthError, RawDataElement,
+ };
use crate::legacy::serialize::tests::helpers::{LongDataElement, ShortDataElement};
- use crate::legacy::{PacketFlavor, NP_MAX_DE_CONTENT_LEN};
+ use crate::legacy::{PacketFlavor, Plaintext, NP_MAX_DE_CONTENT_LEN};
use crate::private::Sealed;
/// A [DataElementDeserializer] that can deserialize the test stubs [ShortDataElement] and
@@ -214,6 +216,19 @@
Long(LongDataElement),
}
+ impl Deserialized for TestDataElement {
+ fn de_type_code(&self) -> DeTypeCode {
+ match self {
+ TestDataElement::Short(s) => {
+ <ShortDataElement as SerializeDataElement<Plaintext>>::de_type_code(s)
+ }
+ TestDataElement::Long(l) => {
+ <LongDataElement as SerializeDataElement<Plaintext>>::de_type_code(l)
+ }
+ }
+ }
+ }
+
impl From<ShortDataElement> for TestDataElement {
fn from(value: ShortDataElement) -> Self {
Self::Short(value)
diff --git a/nearby/presence/np_adv/src/legacy/deserialize/mod.rs b/nearby/presence/np_adv/src/legacy/deserialize/mod.rs
index d305b57..d2ceab6 100644
--- a/nearby/presence/np_adv/src/legacy/deserialize/mod.rs
+++ b/nearby/presence/np_adv/src/legacy/deserialize/mod.rs
@@ -44,6 +44,7 @@
use crate::credential::matched::HasIdentityMatch;
use crate::legacy::data_elements::actions::ActionsDataElement;
use crate::legacy::data_elements::de_type::{DataElementType, DeActualLength};
+use crate::legacy::data_elements::DataElementDeserializeError::DuplicateDeTypes;
use crate::legacy::Plaintext;
/// exposed because the unencrypted case isn't just for intermediate: no further processing is needed
pub use intermediate::UnencryptedAdvContents;
@@ -121,6 +122,44 @@
}
}
+struct DeTypeBitFieldPosition(u16);
+impl DeTypeBitFieldPosition {
+ fn as_u16(&self) -> u16 {
+ self.0
+ }
+}
+
+impl From<u16> for DeTypeBitFieldPosition {
+ fn from(value: u16) -> Self {
+ DeTypeBitFieldPosition(value)
+ }
+}
+
+impl From<DeTypeCode> for DeTypeBitFieldPosition {
+ fn from(value: DeTypeCode) -> Self {
+ match value.as_u8() {
+ 0 => 0.into(),
+ 1 => 0b00000000_00000001.into(),
+ 2 => 0b00000000_00000010.into(),
+ 3 => 0b00000000_00000100.into(),
+ 4 => 0b00000000_00001000.into(),
+ 5 => 0b00000000_00010000.into(),
+ 6 => 0b00000000_00100000.into(),
+ 7 => 0b00000000_01000000.into(),
+ 8 => 0b00000000_10000000.into(),
+ 9 => 0b00000001_00000000.into(),
+ 10 => 0b00000010_00000000.into(),
+ 11 => 0b00000100_00000000.into(),
+ 12 => 0b00001000_00000000.into(),
+ 13 => 0b00010000_00000000.into(),
+ 14 => 0b00100000_00000000.into(),
+ 15 => 0b01000000_00000000.into(),
+ 16 => 0b10000000_00000000.into(),
+ _ => panic!("Invalid v0 De type code"),
+ }
+ }
+}
+
/// The generified innards of [DeIterator] so that it's possible to also use test-only
/// deserializers.
#[derive(Clone, Debug, PartialEq, Eq)]
@@ -128,13 +167,21 @@
/// Data to be parsed, containing a sequence of data elements in serialized
/// form.
data: &'d [u8],
+ /// Keeps track of de_types as we iterate to ensure that multiple des of the same type
+ /// are not present in an advertisement
+ already_encountered: u16,
_flavor_marker: PhantomData<F>,
_deser_marker: PhantomData<D>,
}
impl<'d, F, D> GenericDeIterator<'d, F, D> {
fn new(data: &'d [u8]) -> Self {
- Self { data, _flavor_marker: Default::default(), _deser_marker: Default::default() }
+ Self {
+ data,
+ already_encountered: 0u16,
+ _flavor_marker: Default::default(),
+ _deser_marker: Default::default(),
+ }
}
}
@@ -152,6 +199,14 @@
match parse_result.finish() {
Ok((rem, de)) => {
+ if self.already_encountered
+ & DeTypeBitFieldPosition::from(de.de_type_code()).as_u16()
+ != 0
+ {
+ return Some(Err(DuplicateDeTypes));
+ }
+ self.already_encountered |=
+ DeTypeBitFieldPosition::from(de.de_type_code()).as_u16();
self.data = rem;
Some(Ok(de))
}
@@ -175,6 +230,21 @@
TxPower(TxPowerDataElement),
}
+impl<F: PacketFlavor> Deserialized for DeserializedDataElement<F> {
+ fn de_type_code(&self) -> DeTypeCode {
+ match self {
+ DeserializedDataElement::Actions(_) => {
+ DeTypeCode::try_from(ActionsDataElement::<F>::DE_TYPE_CODE.as_u8())
+ .expect("Actions type code is valid so this will always succeed")
+ }
+ DeserializedDataElement::TxPower(_) => {
+ DeTypeCode::try_from(TxPowerDataElement::DE_TYPE_CODE.as_u8())
+ .expect("TxPower type code is valid so this will always succeed")
+ }
+ }
+ }
+}
+
impl<F: PacketFlavor> DeserializedDataElement<F> {
/// Returns the DE type as a u8
#[cfg(feature = "devtools")]
@@ -254,13 +324,19 @@
}
}
+pub(in crate::legacy) trait Deserialized:
+ fmt::Debug + PartialEq + Eq + Clone
+{
+ fn de_type_code(&self) -> DeTypeCode;
+}
+
/// Overall strategy for deserializing adv contents (once decrypted, if applicable) into data
/// elements
pub(in crate::legacy) trait DataElementDeserializer: Sized {
- /// Disambiguates the intermediate form of a DE
+ /// Disambiguate the intermediate form of a DE
type DeTypeDisambiguator: Copy;
/// The fully deserialized form of a DE
- type Deserialized<F: PacketFlavor>: fmt::Debug + PartialEq + Eq + Clone;
+ type Deserialized<F: PacketFlavor>: Deserialized;
/// Map the encoded len found in a DE header to the actual len that should be consumed from the
/// advertisement payload
diff --git a/nearby/presence/np_adv/src/legacy/deserialize/tests/error_conditions.rs b/nearby/presence/np_adv/src/legacy/deserialize/tests/error_conditions.rs
index 8230548..ace055d 100644
--- a/nearby/presence/np_adv/src/legacy/deserialize/tests/error_conditions.rs
+++ b/nearby/presence/np_adv/src/legacy/deserialize/tests/error_conditions.rs
@@ -83,6 +83,26 @@
}
#[test]
+ fn iterate_multiple_de_types_same_value() {
+ let input = &[0x15, 0x00, 0x15, 0x00];
+ let mut it = IntermediateAdvContents::deserialize::<CryptoProviderImpl>(
+ V0Encoding::Unencrypted,
+ input,
+ )
+ .unwrap()
+ .as_unencrypted()
+ .unwrap()
+ .data_elements();
+
+ // first element will be valid
+ let _ = it.next();
+ // second will be an error since it is the same type as the first
+ let err = it.next().unwrap().unwrap_err();
+
+ assert_eq!(err, DataElementDeserializeError::DuplicateDeTypes);
+ }
+
+ #[test]
fn iterate_truncated_contents_error() {
assert_deser_error(
// length 3, but only 2 bytes provided
diff --git a/nearby/presence/np_adv/src/legacy/deserialize/tests/happy_path.rs b/nearby/presence/np_adv/src/legacy/deserialize/tests/happy_path.rs
index 1b07ff9..e5ae98f 100644
--- a/nearby/presence/np_adv/src/legacy/deserialize/tests/happy_path.rs
+++ b/nearby/presence/np_adv/src/legacy/deserialize/tests/happy_path.rs
@@ -15,7 +15,7 @@
mod unencrypted {
extern crate std;
- use rand::prelude::SliceRandom;
+ use rand::seq::IteratorRandom;
use std::{prelude::rust_2021::*, vec};
use strum::IntoEnumIterator;
@@ -193,8 +193,13 @@
let mut des = Vec::new();
let mut builder = AdvBuilder::new(UnencryptedEncoder);
+ let mut possible_de_types = de_types.clone();
loop {
- let de_type = *de_types.choose(&mut rng).unwrap();
+ if possible_de_types.is_empty() {
+ break;
+ }
+ let (i, _) = possible_de_types.iter().enumerate().choose(&mut rng).unwrap();
+ let de_type = possible_de_types.remove(i);
let de = build_de(de_type, &mut rng);
let add_res = add_de(&mut builder, de.clone());
@@ -257,6 +262,7 @@
mod ldt {
use crate::credential::matched::HasIdentityMatch;
use crate::legacy::data_elements::actions::CallTransfer;
+ use crate::legacy::data_elements::de_type::MAX_DE_ENCODED_LEN;
use crate::{
credential::v0::V0BroadcastCredential,
header::V0Encoding,
@@ -264,7 +270,7 @@
data_elements::{
actions::{ActionBits, ActionsDataElement, NearbyShare},
de_type::DataElementType,
- tests::test_des::{random_test_de, TestDataElementType},
+ tests::test_des::TestDataElementType,
tests::test_des::{TestDataElement, TestDeDeserializer},
tx_power::TxPowerDataElement,
DataElementSerializationBuffer, DynamicSerializeDataElement, SerializeDataElement,
@@ -288,7 +294,7 @@
use alloc::vec::Vec;
use crypto_provider_default::CryptoProviderImpl;
use ldt_np_adv::{V0IdentityToken, V0Salt, V0_IDENTITY_TOKEN_LEN};
- use rand::prelude::SliceRandom;
+ use rand::seq::IteratorRandom;
use rand::Rng;
use strum::IntoEnumIterator;
@@ -384,8 +390,13 @@
#[test]
fn random_test_des_roundtrip() {
do_random_roundtrip_test::<TestDeDeserializer, _>(
- TestDataElementType::iter().collect(),
- random_test_de,
+ vec![TestDataElementType::Short],
+ |_, rng| {
+ let len = rng.gen_range(3_usize..MAX_DE_ENCODED_LEN.into());
+ let mut data = vec![0; len];
+ rng.fill(&mut data[..]);
+ TestDataElement::Short(ShortDataElement::new(data))
+ },
|builder, de| builder.add_data_element(de),
serialized_len,
)
@@ -408,12 +419,12 @@
{
let mut rng = rand_ext::seeded_rng();
- for _ in 0..10_000 {
+ for _ in 0..1_000 {
let mut added_des = Vec::new();
let mut current_len = 0;
let key_seed: [u8; 32] = rng.gen();
- let salt: ldt_np_adv::V0Salt = rng.gen::<[u8; 2]>().into();
+ let salt: V0Salt = rng.gen::<[u8; 2]>().into();
let identity_token = V0IdentityToken::from(rng.gen::<[u8; V0_IDENTITY_TOKEN_LEN]>());
let broadcast_cred = V0BroadcastCredential::new(key_seed, identity_token);
@@ -421,8 +432,13 @@
let mut builder =
AdvBuilder::new(LdtEncoder::<CryptoProviderImpl>::new(salt, &broadcast_cred));
+ let mut possible_de_types = de_types.clone();
loop {
- let de_type = *de_types.choose(&mut rng).unwrap();
+ if possible_de_types.is_empty() {
+ break;
+ }
+ let (i, _) = possible_de_types.iter().enumerate().choose(&mut rng).unwrap();
+ let de_type = possible_de_types.remove(i);
let de = build_de(de_type, &mut rng);
if let Err(e) = add_de(&mut builder, de.clone()) {
@@ -432,6 +448,7 @@
< ldt_np_adv::VALID_INPUT_LEN.start - V0_IDENTITY_TOKEN_LEN
{
// keep trying, not enough for LDT
+ possible_de_types.push(de_type);
continue;
}
// out of room
diff --git a/nearby/presence/np_adv/src/lib.rs b/nearby/presence/np_adv/src/lib.rs
index eb2ef0f..0e958fa 100644
--- a/nearby/presence/np_adv/src/lib.rs
+++ b/nearby/presence/np_adv/src/lib.rs
@@ -24,7 +24,12 @@
#[cfg(any(test, feature = "alloc"))]
extern crate alloc;
+#[cfg(feature = "std")]
+extern crate std;
+
+use core::fmt;
pub use strum;
+pub use tinyvec::ArrayVec;
use crate::credential::matched::MatchedCredential;
use crate::extended::deserialize::{deser_decrypt_v1, V1AdvertisementContents};
@@ -125,7 +130,7 @@
}
impl Debug for AdvDeserializationError {
- fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
+ fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
AdvDeserializationError::VersionHeaderParseError => {
write!(f, "VersionHeaderParseError")
@@ -135,6 +140,20 @@
}
}
+impl fmt::Display for AdvDeserializationError {
+ fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+ match self {
+ AdvDeserializationError::VersionHeaderParseError => {
+ write!(f, "VersionHeaderParseError")
+ }
+ AdvDeserializationError::ParseError { .. } => write!(f, "ParseError"),
+ }
+ }
+}
+
+#[cfg(feature = "std")]
+impl std::error::Error for AdvDeserializationError {}
+
/// Potentially hazardous details about deserialization errors. These error information can
/// potentially expose side-channel information about the plaintext of the advertisements and/or
/// the keys used to decrypt them. For any place that you avoid exposing the keys directly
@@ -167,6 +186,22 @@
}
}
+// trivial Debug/Display that won't leak anything
+impl Debug for AdvDeserializationErrorDetailsHazmat {
+ fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+ write!(f, "AdvDeserializationErrorDetailsHazmat")
+ }
+}
+
+impl fmt::Display for AdvDeserializationErrorDetailsHazmat {
+ fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+ write!(f, "AdvDeserializationErrorDetailsHazmat")
+ }
+}
+
+#[cfg(feature = "std")]
+impl std::error::Error for AdvDeserializationErrorDetailsHazmat {}
+
/// DE length is out of range (e.g. > 4 bits for encoded V0, > max DE size for actual V0, >127 for
/// V1) or invalid for the relevant DE type.
#[derive(Debug, PartialEq, Eq)]
diff --git a/nearby/presence/np_adv/src/tests/deser_v1_tests.rs b/nearby/presence/np_adv/src/tests/deser_v1_tests.rs
index d0865c7..78b8dc4 100644
--- a/nearby/presence/np_adv/src/tests/deser_v1_tests.rs
+++ b/nearby/presence/np_adv/src/tests/deser_v1_tests.rs
@@ -256,7 +256,7 @@
adv_builder: &mut AdvBuilder,
) -> Vec<SectionConfig<'a>> {
let mut expected = Vec::new();
- for _ in 0..rng.gen_range(1..=NP_V1_ADV_MAX_ENCRYPTED_SECTION_COUNT) {
+ for _ in 0..rng.gen_range(1..=NP_V1_ADV_MAX_SECTION_COUNT) {
let identity = identities.pick_random_identity(&mut rng);
let mode: VerificationMode = random();
let res = match mode {
diff --git a/nearby/presence/np_adv/src/tests/deser_v1_tests/error_condition.rs b/nearby/presence/np_adv/src/tests/deser_v1_tests/error_condition.rs
index 9f91dbd..57292cf 100644
--- a/nearby/presence/np_adv/src/tests/deser_v1_tests/error_condition.rs
+++ b/nearby/presence/np_adv/src/tests/deser_v1_tests/error_condition.rs
@@ -17,34 +17,6 @@
use rand::SeedableRng;
#[test]
-fn v1_multiple_plaintext_sections() {
- let mut rng = StdRng::from_entropy();
- let mut adv_builder = AdvBuilder::new(AdvertisementType::Plaintext);
- add_plaintext_section(&mut rng, &mut adv_builder).unwrap();
-
- // append an extra plaintext section
- let adv = [
- adv_builder.into_advertisement().as_slice(),
- &[
- 0x00, // format unencrypted
- 0x03, // section len
- ],
- &[0xDD; 3], // 3 bytes of de contents
- ]
- .concat();
-
- let arena = deserialization_arena!();
- let cred_book = build_empty_cred_book();
- let v1_error = deser_v1_error::<_, CryptoProviderImpl>(arena, &adv, &cred_book);
- assert_eq!(
- v1_error,
- AdvDeserializationError::ParseError {
- details_hazmat: AdvDeserializationErrorDetailsHazmat::AdvertisementDeserializeError
- }
- );
-}
-
-#[test]
fn v1_plaintext_empty_contents() {
let mut adv_builder = AdvBuilder::new(AdvertisementType::Plaintext);
let sb = adv_builder.section_builder(UnencryptedSectionEncoder).unwrap();
@@ -197,7 +169,8 @@
// mangle the last 2 bytes of the suffix to invalidate the advertisement.
// the identity should still correctly match
let adv_len = adv.len();
- adv[adv_len - 2..].copy_from_slice(&[0xFF; 2]);
+ adv[adv_len - 2] = adv[adv_len - 2].wrapping_add(1);
+ adv[adv_len - 1] = adv[adv_len - 1].wrapping_sub(1);
},
)
}
@@ -248,7 +221,6 @@
let mut adv = builder.into_advertisement().as_slice().to_vec();
mangle_adv(&mut adv);
-
let cred_book = identities.build_cred_book::<CryptoProviderImpl>();
let arena = deserialization_arena!();
let v1_contents = deser_v1::<_, CryptoProviderImpl>(arena, adv.as_slice(), &cred_book);
diff --git a/nearby/presence/np_adv/src/tests/deser_v1_tests/happy_path.rs b/nearby/presence/np_adv/src/tests/deser_v1_tests/happy_path.rs
index 2171bd6..ee0077d 100644
--- a/nearby/presence/np_adv/src/tests/deser_v1_tests/happy_path.rs
+++ b/nearby/presence/np_adv/src/tests/deser_v1_tests/happy_path.rs
@@ -68,6 +68,30 @@
}
#[test]
+fn v1_multiple_plaintext_sections() {
+ let mut rng = StdRng::from_entropy();
+ let mut adv_builder = AdvBuilder::new(AdvertisementType::Plaintext);
+ add_plaintext_section(&mut rng, &mut adv_builder).unwrap();
+
+ // append an extra plaintext section
+ let adv = [
+ adv_builder.into_advertisement().as_slice(),
+ &[
+ 0x00, // format unencrypted
+ 0x03, // section len
+ ],
+ &[0xDD; 3], // 3 bytes of de contents
+ ]
+ .concat();
+
+ let arena = deserialization_arena!();
+ let cred_book = build_empty_cred_book();
+ let v1_contents = deser_v1::<_, CryptoProviderImpl>(arena, &adv, &cred_book);
+ assert_eq!(0, v1_contents.invalid_sections_count());
+ assert_eq!(2, v1_contents.sections().len());
+}
+
+#[test]
fn v1_all_identities_resolvable_ciphertext() {
let mut rng = StdRng::from_entropy();
for _ in 0..100 {
diff --git a/nearby/presence/np_adv/tests/examples_v1.rs b/nearby/presence/np_adv/tests/examples_v1.rs
index 57e41fa..7b89f8d 100644
--- a/nearby/presence/np_adv/tests/examples_v1.rs
+++ b/nearby/presence/np_adv/tests/examples_v1.rs
@@ -35,7 +35,7 @@
AdvBuilder, AdvertisementType, SignedEncryptedSectionEncoder, SingleTypeDataElement,
UnencryptedSectionEncoder,
},
- NP_V1_ADV_MAX_PUBLIC_SECTION_COUNT,
+ NP_V1_ADV_MAX_SECTION_COUNT,
},
shared_data::TxPower,
AdvDeserializationError, AdvDeserializationErrorDetailsHazmat,
@@ -260,7 +260,7 @@
#[test]
fn v1_deser_plaintext_over_max_sections() {
let mut adv_builder = AdvBuilder::new(AdvertisementType::Plaintext);
- for _ in 0..NP_V1_ADV_MAX_PUBLIC_SECTION_COUNT {
+ for _ in 0..NP_V1_ADV_MAX_SECTION_COUNT {
let mut section_builder = adv_builder.section_builder(UnencryptedSectionEncoder).unwrap();
section_builder
.add_de(|_salt| TxPowerDataElement::from(TxPower::try_from(7).unwrap()))
diff --git a/nearby/presence/np_adv_dynamic/Cargo.toml b/nearby/presence/np_adv_dynamic/Cargo.toml
index 5eb6863..17ff642 100644
--- a/nearby/presence/np_adv_dynamic/Cargo.toml
+++ b/nearby/presence/np_adv_dynamic/Cargo.toml
@@ -3,6 +3,7 @@
version.workspace = true
edition.workspace = true
publish.workspace = true
+license.workspace = true
[lints]
workspace = true
diff --git a/nearby/presence/np_c_ffi/Cargo.toml b/nearby/presence/np_c_ffi/Cargo.toml
index 517c2c5..e482cbe 100644
--- a/nearby/presence/np_c_ffi/Cargo.toml
+++ b/nearby/presence/np_c_ffi/Cargo.toml
@@ -3,6 +3,7 @@
version = "0.1.0"
edition = "2021"
publish = false
+license = "Apache-2.0"
[dependencies]
np_ffi_core = {workspace = true, default-features=false}
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 d84a49a..2cef974 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
@@ -689,7 +689,7 @@
* A representation of a MatchedCredential which is passable across the FFI boundary
*/
typedef struct {
- uint32_t cred_id;
+ int64_t cred_id;
const uint8_t *encrypted_metadata_bytes_buffer;
uintptr_t encrypted_metadata_bytes_len;
} np_ffi_FfiMatchedCredential;
@@ -972,7 +972,7 @@
* The ID of the credential which
* matched the deserialized adv
*/
- uint32_t cred_id;
+ int64_t cred_id;
/**
* The 14-byte legacy identity token
*/
@@ -1123,7 +1123,7 @@
* The ID of the credential which
* matched the deserialized section.
*/
- uint32_t cred_id;
+ int64_t cred_id;
/**
* The 16-byte metadata key.
*/
@@ -1229,18 +1229,10 @@
} np_ffi_FixedSizeArray_2;
/**
- * A `#[repr(C)]` handle to a value of type `V1AdvertisementBuilderInternals`
- */
-typedef struct {
- uint64_t handle_id;
-} np_ffi_V1AdvertisementBuilderHandle;
-
-/**
* A handle to a builder for V1 advertisements.
*/
typedef struct {
- np_ffi_AdvertisementBuilderKind kind;
- np_ffi_V1AdvertisementBuilderHandle handle;
+ uint64_t handle_id;
} np_ffi_V1AdvertisementBuilder;
/**
@@ -1248,7 +1240,13 @@
* the advertisement builder the section builder was originated from.
*/
typedef struct {
+ /**
+ * The parent advertisement builder for this section
+ */
np_ffi_V1AdvertisementBuilder adv_builder;
+ /**
+ * This section's index in the parent advertisement
+ */
uint8_t section_index;
} np_ffi_V1SectionBuilder;
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 0206f80..919249d 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
@@ -457,7 +457,7 @@
/// A representation of a MatchedCredential which is passable across the FFI boundary
struct FfiMatchedCredential {
- uint32_t cred_id;
+ int64_t cred_id;
const uint8_t *encrypted_metadata_bytes_buffer;
uintptr_t encrypted_metadata_bytes_len;
};
@@ -704,7 +704,7 @@
struct DeserializedV0IdentityDetails {
/// The ID of the credential which
/// matched the deserialized adv
- uint32_t cred_id;
+ int64_t cred_id;
/// The 14-byte legacy identity token
uint8_t identity_token[14];
/// The 2-byte advertisement salt
@@ -820,7 +820,7 @@
V1VerificationMode verification_mode;
/// The ID of the credential which
/// matched the deserialized section.
- uint32_t cred_id;
+ int64_t cred_id;
/// The 16-byte metadata key.
uint8_t identity_token[16];
};
@@ -900,21 +900,17 @@
uint8_t identity_token[14];
};
-/// A `#[repr(C)]` handle to a value of type `V1AdvertisementBuilderInternals`
-struct V1AdvertisementBuilderHandle {
- uint64_t handle_id;
-};
-
/// A handle to a builder for V1 advertisements.
struct V1AdvertisementBuilder {
- AdvertisementBuilderKind kind;
- V1AdvertisementBuilderHandle handle;
+ uint64_t handle_id;
};
/// A handle to a builder for V1 sections. This is not a unique handle; it is the same handle as
/// the advertisement builder the section builder was originated from.
struct V1SectionBuilder {
+ /// The parent advertisement builder for this section
V1AdvertisementBuilder adv_builder;
+ /// This section's index in the parent advertisement
uint8_t section_index;
};
diff --git a/nearby/presence/np_c_ffi/src/credentials.rs b/nearby/presence/np_c_ffi/src/credentials.rs
index d03f0f6..0c8a316 100644
--- a/nearby/presence/np_c_ffi/src/credentials.rs
+++ b/nearby/presence/np_c_ffi/src/credentials.rs
@@ -153,7 +153,7 @@
/// A representation of a MatchedCredential which is passable across the FFI boundary
#[repr(C)]
pub struct FfiMatchedCredential {
- cred_id: u32,
+ cred_id: i64,
encrypted_metadata_bytes_buffer: *const u8,
encrypted_metadata_bytes_len: usize,
}
diff --git a/nearby/presence/np_cpp_ffi/tests/v0_encrypted_deserialization_tests.cc b/nearby/presence/np_cpp_ffi/tests/v0_encrypted_deserialization_tests.cc
index bd1f771..37d2746 100644
--- a/nearby/presence/np_cpp_ffi/tests/v0_encrypted_deserialization_tests.cc
+++ b/nearby/presence/np_cpp_ffi/tests/v0_encrypted_deserialization_tests.cc
@@ -166,6 +166,6 @@
// Make sure the correct credential matches
auto identity_details = payload.TryGetIdentityDetails();
ASSERT_TRUE(identity_details.ok());
- ASSERT_EQ(identity_details->cred_id, 456u);
+ ASSERT_EQ(identity_details->cred_id, 456);
}
// NOLINTEND(readability-magic-numbers)
diff --git a/nearby/presence/np_cpp_ffi/tests/v1_encrypted_deserialization_tests.cc b/nearby/presence/np_cpp_ffi/tests/v1_encrypted_deserialization_tests.cc
index 65b9730..69a042d 100644
--- a/nearby/presence/np_cpp_ffi/tests/v1_encrypted_deserialization_tests.cc
+++ b/nearby/presence/np_cpp_ffi/tests/v1_encrypted_deserialization_tests.cc
@@ -59,7 +59,7 @@
auto identity_details = section->GetIdentityDetails();
ASSERT_TRUE(identity_details.ok());
- ASSERT_EQ(identity_details->cred_id, 123U);
+ ASSERT_EQ(identity_details->cred_id, 123);
ASSERT_EQ(identity_details->verification_mode,
nearby_protocol::V1VerificationMode::Signature);
diff --git a/nearby/presence/np_ed25519/Cargo.toml b/nearby/presence/np_ed25519/Cargo.toml
index bd42c65..5addbb1 100644
--- a/nearby/presence/np_ed25519/Cargo.toml
+++ b/nearby/presence/np_ed25519/Cargo.toml
@@ -3,6 +3,7 @@
version.workspace = true
edition.workspace = true
publish.workspace = true
+license.workspace = true
[lints]
workspace = true
diff --git a/nearby/presence/np_ffi_core/Cargo.toml b/nearby/presence/np_ffi_core/Cargo.toml
index 7c3c112..4faebe6 100644
--- a/nearby/presence/np_ffi_core/Cargo.toml
+++ b/nearby/presence/np_ffi_core/Cargo.toml
@@ -3,6 +3,7 @@
version.workspace = true
edition.workspace = true
publish.workspace = true
+license.workspace = true
[lints]
workspace = true
@@ -25,3 +26,5 @@
default = ["rustcrypto"]
rustcrypto = ["crypto_provider_default/rustcrypto", "crypto_provider_default/std"]
boringssl = ["crypto_provider_default/boringssl"]
+testing = ["np_adv/testing"]
+std = []
diff --git a/nearby/presence/np_ffi_core/src/common.rs b/nearby/presence/np_ffi_core/src/common.rs
index da7cb5c..607a6af 100644
--- a/nearby/presence/np_ffi_core/src/common.rs
+++ b/nearby/presence/np_ffi_core/src/common.rs
@@ -219,7 +219,7 @@
/// ArrayView, which is assumed to be trusted to be
/// properly initialized, and with a size-bound
/// under 255 bytes.
- pub(crate) fn from_array_view(array_view: ArrayView<u8, N>) -> Self {
+ pub fn from_array_view(array_view: ArrayView<u8, N>) -> Self {
let (len, bytes) = array_view.into_raw_parts();
let len = len as u8;
Self { len, bytes }
@@ -267,22 +267,3 @@
/// in an entirely unexpected way.
#[derive(Debug)]
pub struct InvalidStackDataStructure;
-
-/// Error raised when attempting to cast an enum to
-/// one of its variants, but the value is actually
-/// of a different variant than the requested one.
-pub struct EnumCastError {
- pub(crate) projection_method_name: String,
- pub(crate) variant_enum_name: String,
- pub(crate) variant_type_name: String,
-}
-
-impl core::fmt::Debug for EnumCastError {
- fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
- write!(
- f,
- "Attempted to cast a non-{} to a {} via {}",
- &self.variant_enum_name, &self.variant_type_name, &self.projection_method_name
- )
- }
-}
diff --git a/nearby/presence/np_ffi_core/src/credentials.rs b/nearby/presence/np_ffi_core/src/credentials.rs
index 1ad303f..4f6c3cd 100644
--- a/nearby/presence/np_ffi_core/src/credentials.rs
+++ b/nearby/presence/np_ffi_core/src/credentials.rs
@@ -100,24 +100,24 @@
/// the FFI boundary.
#[derive(Debug, Clone)]
pub struct MatchedCredential {
- cred_id: u32,
+ cred_id: i64,
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,
+ /// (some arbitrary `i64` identifier) and encrypted metadata bytes,
/// copied from the given slice.
- pub fn new(cred_id: u32, encrypted_metadata_bytes: &[u8]) -> Self {
+ pub fn new(cred_id: i64, encrypted_metadata_bytes: &[u8]) -> Self {
Self::from_arc_bytes(cred_id, encrypted_metadata_bytes.to_vec().into())
}
/// Constructs a new matched credential from the given match-id
/// (some arbitrary `u32` identifier) and encrypted metadata bytes.
- pub fn from_arc_bytes(cred_id: u32, encrypted_metadata_bytes: Arc<[u8]>) -> Self {
+ pub fn from_arc_bytes(cred_id: i64, encrypted_metadata_bytes: Arc<[u8]>) -> Self {
Self { cred_id, encrypted_metadata_bytes }
}
/// Gets the pre-specified numerical identifier for this matched-credential.
- pub(crate) fn id(&self) -> u32 {
+ pub(crate) fn id(&self) -> i64 {
self.cred_id
}
}
diff --git a/nearby/presence/np_ffi_core/src/deserialize/v0.rs b/nearby/presence/np_ffi_core/src/deserialize/v0.rs
index 79528a0..67d798d 100644
--- a/nearby/presence/np_ffi_core/src/deserialize/v0.rs
+++ b/nearby/presence/np_ffi_core/src/deserialize/v0.rs
@@ -184,7 +184,7 @@
pub struct DeserializedV0IdentityDetails {
/// The ID of the credential which
/// matched the deserialized adv
- cred_id: u32,
+ cred_id: i64,
/// The 14-byte legacy identity token
identity_token: [u8; 14],
/// The 2-byte advertisement salt
@@ -193,7 +193,7 @@
impl DeserializedV0IdentityDetails {
pub(crate) fn new(
- cred_id: u32,
+ cred_id: i64,
salt: ldt_np_adv::V0Salt,
identity_token: ldt_np_adv::V0IdentityToken,
) -> Self {
@@ -201,7 +201,7 @@
Self { cred_id, salt, identity_token: identity_token.bytes() }
}
/// Returns the ID of the credential which matched the deserialized adv
- pub fn cred_id(&self) -> u32 {
+ pub fn cred_id(&self) -> i64 {
self.cred_id
}
/// Returns the 14-byte legacy metadata key
diff --git a/nearby/presence/np_ffi_core/src/deserialize/v1.rs b/nearby/presence/np_ffi_core/src/deserialize/v1.rs
index 6007975..849abab 100644
--- a/nearby/presence/np_ffi_core/src/deserialize/v1.rs
+++ b/nearby/presence/np_ffi_core/src/deserialize/v1.rs
@@ -545,14 +545,14 @@
verification_mode: V1VerificationMode,
/// The ID of the credential which
/// matched the deserialized section.
- cred_id: u32,
+ cred_id: i64,
/// The 16-byte metadata key.
identity_token: [u8; 16],
}
impl DeserializedV1IdentityDetails {
pub(crate) fn new(
- cred_id: u32,
+ cred_id: i64,
verification_mode: np_adv::extended::deserialize::VerificationMode,
identity_token: np_adv::extended::V1IdentityToken,
) -> Self {
@@ -560,7 +560,7 @@
Self { cred_id, verification_mode, identity_token: identity_token.into_bytes() }
}
/// Returns the ID of the credential which matched the deserialized section.
- pub fn cred_id(&self) -> u32 {
+ pub fn cred_id(&self) -> i64 {
self.cred_id
}
/// Returns the verification mode (MIC/Signature) employed for the decrypted section.
diff --git a/nearby/presence/np_ffi_core/src/lib.rs b/nearby/presence/np_ffi_core/src/lib.rs
index 2c03ea5..0fd7f1a 100644
--- a/nearby/presence/np_ffi_core/src/lib.rs
+++ b/nearby/presence/np_ffi_core/src/lib.rs
@@ -15,9 +15,6 @@
//! Core functionality common to all NP Rust FFI layers
#[macro_use]
-extern crate lazy_static;
-
-#[macro_use]
pub mod utils;
pub mod common;
pub mod credentials;
diff --git a/nearby/presence/np_ffi_core/src/serialize/v1.rs b/nearby/presence/np_ffi_core/src/serialize/v1.rs
index c87446f..1191ac1 100644
--- a/nearby/presence/np_ffi_core/src/serialize/v1.rs
+++ b/nearby/presence/np_ffi_core/src/serialize/v1.rs
@@ -20,15 +20,25 @@
use crate::v1::V1VerificationMode;
use crypto_provider_default::CryptoProviderImpl;
use handle_map::{declare_handle_map, HandleLike, HandleMapFullError};
+use np_adv::extended::serialize::AdvertisementType;
+
+#[cfg(feature = "testing")]
+use np_adv::extended::salt::MultiSalt;
/// A handle to a builder for V1 advertisements.
-#[derive(Clone, Copy)]
#[repr(C)]
+#[derive(Clone, Copy, PartialEq, Eq)]
pub struct V1AdvertisementBuilder {
- kind: AdvertisementBuilderKind,
- handle: V1AdvertisementBuilderHandle,
+ handle_id: u64,
}
+declare_handle_map!(
+ advertisement_builder,
+ crate::common::default_handle_map_dimensions(),
+ super::V1AdvertisementBuilder,
+ super::V1AdvertisementBuilderInternals
+);
+
impl V1AdvertisementBuilder {
/// Attempts to create a builder for a new public section within this advertisement, returning
/// an owned handle to the newly-created section builder if successful.
@@ -41,11 +51,6 @@
self.section_builder_internals(|internals| internals.public_section_builder())
}
- /// Gets the kind of advertisement builder (public/encrypted)
- pub fn kind(&self) -> AdvertisementBuilderKind {
- self.kind
- }
-
/// Attempts to create a builder for a new encrypted section within this advertisement,
/// returning an owned handle to the newly-created section builder if successful.
///
@@ -69,12 +74,38 @@
})
}
+ /// Attempts to create a builder for a new encrypted section within this advertisement,
+ /// returning an owned 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).
+ ///
+ ///
+ /// The section will be encrypted with the MIC verification mode using the given salt.
+ ///
+ /// 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.
+ #[cfg(feature = "testing")]
+ pub fn mic_custom_salt_section_builder(
+ &self,
+ broadcast_cred: V1BroadcastCredential,
+ salt: MultiSalt,
+ ) -> CreateV1SectionBuilderResult {
+ self.section_builder_internals(move |internals| {
+ internals.mic_custom_salt_section_builder(broadcast_cred, salt)
+ })
+ }
+
/// Attempts to serialize the contents of the advertisement builder behind this handle to
/// bytes. Assuming that the handle is valid, this operation will always take ownership of the
/// handle and result in the contents behind the advertisement builder handle being
/// deallocated.
pub fn into_advertisement(self) -> SerializeV1AdvertisementResult {
- match self.handle.deallocate() {
+ match self.deallocate() {
Ok(adv_builder) => adv_builder.into_advertisement(),
Err(_) => SerializeV1AdvertisementResult::InvalidAdvertisementBuilderHandle,
}
@@ -86,7 +117,7 @@
&mut V1AdvertisementBuilderInternals,
) -> Result<usize, SectionBuilderError>,
) -> CreateV1SectionBuilderResult {
- match self.handle.get_mut() {
+ match self.get_mut() {
Ok(mut adv_builder_write_guard) => {
match builder_supplier(&mut adv_builder_write_guard) {
Ok(section_index) => CreateV1SectionBuilderResult::Success(V1SectionBuilder {
@@ -157,9 +188,10 @@
pub fn create_v1_advertisement_builder(
kind: AdvertisementBuilderKind,
) -> CreateV1AdvertisementBuilderResult {
- V1AdvertisementBuilderHandle::allocate(move || V1AdvertisementBuilderInternals::new(kind))
- .map(|handle| V1AdvertisementBuilder { kind, handle })
- .into()
+ V1AdvertisementBuilder::allocate(move || {
+ V1AdvertisementBuilderInternals::new(kind.as_internal_v1())
+ })
+ .into()
}
pub(crate) enum V1AdvertisementBuilderState {
@@ -256,8 +288,7 @@
}
impl V1AdvertisementBuilderInternals {
- pub(crate) fn new(kind: AdvertisementBuilderKind) -> Self {
- let adv_type = kind.as_internal_v1();
+ pub(crate) fn new(adv_type: AdvertisementType) -> Self {
let builder = np_adv::extended::serialize::AdvBuilder::new(adv_type);
let builder = builder.into();
let state = Some(V1AdvertisementBuilderState::Advertisement(builder));
@@ -324,6 +355,21 @@
};
self.section_builder_internal(encoder)
}
+
+ #[cfg(feature = "testing")]
+ pub(crate) fn mic_custom_salt_section_builder(
+ &mut self,
+ broadcast_cred: V1BroadcastCredential,
+ salt: MultiSalt,
+ ) -> Result<usize, SectionBuilderError> {
+ let internal_broadcast_cred = broadcast_cred.into_internal();
+ let encoder = np_adv::extended::serialize::MicEncryptedSectionEncoder::new_with_salt::<
+ CryptoProviderImpl,
+ >(salt, &internal_broadcast_cred);
+ let encoder = np_adv_dynamic::extended::BoxedEncoder::MicEncrypted(encoder);
+ self.section_builder_internal(encoder)
+ }
+
fn into_advertisement(self) -> SerializeV1AdvertisementResult {
match self.state {
Some(V1AdvertisementBuilderState::Advertisement(adv_builder)) => {
@@ -335,20 +381,6 @@
}
}
-/// A `#[repr(C)]` handle to a value of type `V1AdvertisementBuilderInternals`
-#[repr(C)]
-#[derive(Clone, Copy, PartialEq, Eq)]
-struct V1AdvertisementBuilderHandle {
- handle_id: u64,
-}
-
-declare_handle_map!(
- advertisement_builder,
- crate::common::default_handle_map_dimensions(),
- super::V1AdvertisementBuilderHandle,
- super::V1AdvertisementBuilderInternals
-);
-
/// Discriminant for `CreateV1SectionBuilderResult`
#[derive(Copy, Clone)]
#[repr(u8)]
@@ -493,8 +525,10 @@
#[derive(Clone, Copy)]
#[repr(C)]
pub struct V1SectionBuilder {
- adv_builder: V1AdvertisementBuilder,
- section_index: u8,
+ /// The parent advertisement builder for this section
+ pub adv_builder: V1AdvertisementBuilder,
+ /// This section's index in the parent advertisement
+ pub section_index: u8,
}
impl V1SectionBuilder {
@@ -502,7 +536,7 @@
/// 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() {
+ match self.adv_builder.get_mut() {
Ok(mut adv_builder) => {
let state = adv_builder.state.take();
match state {
@@ -579,7 +613,7 @@
) -> R,
invalid_handle_error: R,
) -> R {
- match self.adv_builder.handle.get_mut() {
+ match self.adv_builder.get_mut() {
Ok(mut adv_builder) => {
match adv_builder.state.as_mut() {
Some(V1AdvertisementBuilderState::Section(ref mut section_builder)) => {
@@ -641,8 +675,7 @@
#[allow(clippy::expect_used)]
#[test]
fn test_build_section_fails_with_outstanding_section() {
- let mut adv_builder =
- V1AdvertisementBuilderInternals::new(AdvertisementBuilderKind::Encrypted);
+ let mut adv_builder = V1AdvertisementBuilderInternals::new(AdvertisementType::Encrypted);
let adv_builder_state =
adv_builder.state.as_ref().expect("Adv builder state should be present.");
diff --git a/nearby/presence/np_hkdf/Cargo.toml b/nearby/presence/np_hkdf/Cargo.toml
index cd7fb4c..4923d61 100644
--- a/nearby/presence/np_hkdf/Cargo.toml
+++ b/nearby/presence/np_hkdf/Cargo.toml
@@ -3,6 +3,7 @@
version.workspace = true
edition.workspace = true
publish.workspace = true
+license.workspace = true
[lints]
workspace = true
diff --git a/nearby/presence/np_java_ffi/AndroidManifest.xml b/nearby/presence/np_java_ffi/AndroidManifest.xml
new file mode 100644
index 0000000..04ad56d
--- /dev/null
+++ b/nearby/presence/np_java_ffi/AndroidManifest.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ Copyright 2024 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.presence.rust">
+ <uses-sdk android:minSdkVersion="33" />
+</manifest>
diff --git a/nearby/presence/np_java_ffi/Cargo.toml b/nearby/presence/np_java_ffi/Cargo.toml
index 556b481..134dfbb 100644
--- a/nearby/presence/np_java_ffi/Cargo.toml
+++ b/nearby/presence/np_java_ffi/Cargo.toml
@@ -3,11 +3,16 @@
version.workspace = true
edition.workspace = true
publish.workspace = true
+license.workspace = true
[lints]
workspace = true
+[features]
+testing = ["np_ffi_core/testing"]
+
[dependencies]
+array_view.workspace = true
handle_map.workspace = true
np_adv.workspace = true
np_ffi_core.workspace = true
diff --git a/nearby/presence/np_java_ffi/build.gradle.kts b/nearby/presence/np_java_ffi/build.gradle.kts
index c502cdb..6fe9e82 100644
--- a/nearby/presence/np_java_ffi/build.gradle.kts
+++ b/nearby/presence/np_java_ffi/build.gradle.kts
@@ -14,8 +14,17 @@
* limitations under the License.
*/
+import net.ltgt.gradle.errorprone.errorprone;
+
plugins {
`java-library`
+ id("net.ltgt.errorprone") version "4.0.0"
+}
+
+java {
+ // Gradle JUnit test finder doesn't support Java 21 class files.
+ sourceCompatibility = JavaVersion.VERSION_1_9
+ targetCompatibility = JavaVersion.VERSION_1_9
}
repositories {
@@ -25,13 +34,14 @@
dependencies {
implementation("androidx.annotation:annotation:1.6.0")
+ implementation("com.google.errorprone:error_prone_core:2.28.0")
+ implementation("org.checkerframework:checker-qual:3.45.0")
// JUnit Test Support
- testImplementation("org.junit.jupiter:junit-jupiter-api:5.8.1")
+ testImplementation("junit:junit:4.13")
testImplementation("com.google.truth:truth:1.1.4")
+ testImplementation("com.google.code.gson:gson:2.10.1")
testImplementation("org.mockito:mockito-core:5.+")
- testImplementation("org.mockito:mockito-junit-jupiter:5.+")
- testRuntimeOnly("org.junit.jupiter:junit-jupiter-engine:5.8.1")
}
// Flattened directory layout
@@ -49,7 +59,7 @@
}
tasks.test {
- useJUnitPlatform()
+ useJUnit()
jvmArgs = mutableListOf(
// libnp_java_ffi.so
"-Djava.library.path=$projectDir/../../target/debug",
@@ -57,3 +67,18 @@
"-XX:+EnableDynamicAgentLoading"
)
}
+
+tasks.withType<JavaCompile>().configureEach {
+ options.errorprone {
+ error("CheckReturnValue")
+ error("UnnecessaryStaticImport")
+ error("WildcardImport")
+ error("RemoveUnusedImports")
+ error("ReturnMissingNullable")
+ error("FieldMissingNullable")
+ error("AnnotationPosition")
+ error("CheckedExceptionNotThrown")
+ error("NonFinalStaticField")
+ error("InvalidLink")
+ }
+}
diff --git a/nearby/presence/np_java_ffi/java/com/google/android/nearby/presence/rust/CooperativeCleaner.java b/nearby/presence/np_java_ffi/java/com/google/android/nearby/presence/rust/CooperativeCleaner.java
new file mode 100644
index 0000000..e420f71
--- /dev/null
+++ b/nearby/presence/np_java_ffi/java/com/google/android/nearby/presence/rust/CooperativeCleaner.java
@@ -0,0 +1,174 @@
+/*
+ * Copyright 2024 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.nearby.presence.rust;
+
+import androidx.annotation.GuardedBy;
+import androidx.annotation.Nullable;
+import androidx.annotation.VisibleForTesting;
+import java.lang.ref.PhantomReference;
+import java.lang.ref.ReferenceQueue;
+import java.util.HashSet;
+import org.checkerframework.checker.initialization.qual.Initialized;
+import org.checkerframework.checker.initialization.qual.UnknownInitialization;
+
+/**
+ * A similar class to {@link java.lang.ref.Cleaner} that doesn't require a dedicated thread.
+ *
+ * <p>To avoid using a thread for the reference queue, clients are expected to call {@link
+ * Registration#close()} on the object returned from {@link register(Object, Runnable)}. This will
+ * perform two actions:
+ *
+ * <ol>
+ * <li>Run the cleanup actions for any registered objects that have been finaized without {@code
+ * close()} being called.
+ * <li>Run the cleanup action for the object being closed.
+ * </ol>
+ *
+ * <p>The application should keep a reference to {@code this} instance to avoid the cleanup actions
+ * themselves being garbage collected!
+ */
+public class CooperativeCleaner {
+
+ /**
+ * An object registration with this CooperativeCleaner. Clients should call the {@link
+ * Registration#close()} method when the registered object is ready to be cleaned up.
+ *
+ * @see CooperativeCleaner documentation for more details.
+ */
+ public class Registration implements AutoCloseable {
+ private final CleanableReference ref;
+
+ private Registration(CleanableReference ref) {
+ this.ref = ref;
+ }
+
+ @Override
+ public void close() {
+ // Cleanup any queued objects first (Cooperation!)
+ processQueuedObjects();
+
+ // Run cleanup for the referenced object.
+ ref.performCleanup();
+ }
+ }
+
+ /**
+ * Objects that are not closed will be posted to this reference queue by the garbage collector.
+ */
+ @GuardedBy("queue")
+ private final ReferenceQueue<Object> queue = new ReferenceQueue<>();
+
+ /**
+ * Holds strong references to {@link CleanableReference} instances so that the reference objects
+ * are not garbage collected.
+ */
+ @GuardedBy("refs")
+ private final HashSet<CleanableReference> refs = new HashSet<>();
+
+ /**
+ * Register an object for cleanup. The object will be cleaned up when the returned {@code
+ * Registration} is closed or sometime after the object has been finalized. The returned value
+ * should always be closed if possible. If it is closed the {@code cleanupAction} will be run on
+ * the thread that called {@code close()}; otherwise, it will be run during {@code close()} for a
+ * different object. In all cases {@code cleanupAction} will be run exactly once.
+ *
+ * <p>The given {@code cleanupAction} <b>SHOULD NOT</b> hold a reference to {@code object}.
+ * Holding such a reference would mean that {@code object} is always reachable and it will never
+ * be garbaged collected.
+ */
+ public Registration register(@UnknownInitialization Object object, Runnable cleanupAction) {
+ // The cast here is safe because it is impossible to get object back out of this reference.
+ // PhantomReference will always return null for {@link PhantomReference#get()}. We don't care
+ // about the initialization state of the object; rather, we only care about the identity of the
+ // object. Likewise, an actual reference to object is never stored by this class; that would
+ // stop the object from ever being garbage collected.
+ @SuppressWarnings("nullness:cast.unsafe")
+ CleanableReference ref = new CleanableReference((@Initialized Object) object, cleanupAction);
+
+ // Keep a copy of the reference in {@code refs} to avoid being garbage collected.
+ synchronized (refs) {
+ refs.add(ref);
+ }
+
+ return new Registration(ref);
+ }
+
+ /** Gets the number of objects that are currently registered with this cleaner. */
+ @VisibleForTesting
+ public int getRegisteredObjectCount() {
+ synchronized (refs) {
+ return refs.size();
+ }
+ }
+
+ /** Gets the next {@link CleanableReference} from the queue if there is one. */
+ @Nullable
+ private CleanableReference pollQueue() {
+ synchronized (queue) {
+ return (CleanableReference) queue.poll();
+ }
+ }
+
+ /** Process cleanup actions for all objects in the reference queue. */
+ @VisibleForTesting
+ public void processQueuedObjects() {
+ CleanableReference ref;
+ while ((ref = pollQueue()) != null) {
+ ref.performCleanup();
+ }
+ }
+
+ /**
+ * Stores the {@code cleanupAction} and tracks the lifecycle of {@code object}. {@link
+ * #performCleanup()} can be called early to perform the cleanup action.
+ */
+ private class CleanableReference extends PhantomReference<Object> {
+ @Nullable private Runnable cleanupAction;
+
+ private CleanableReference(Object object, Runnable cleanupAction) {
+ // This reference will be enqueued onto {@code queue} if object becomes phantom-reachable and
+ // {@link #clear()} hasn't been called.
+ super(object, queue);
+
+ this.cleanupAction = cleanupAction;
+ }
+
+ private void performCleanup() {
+ // Cleanup this object
+ @Nullable Runnable action;
+ synchronized (refs) {
+ // Do not post this to the reference queue if this was called before the object was
+ // posted.
+ this.clear();
+
+ // This object is no longer needed, so we can remove our reference to it so that it may be
+ // garbaged collected.
+ refs.remove(this);
+
+ // Make sure that cleanupAction is only run once by taking the value before running it
+ action = this.cleanupAction;
+ this.cleanupAction = null;
+ }
+
+ if (action != null) {
+ // Run cleanupAction outside of the lock so that multiple cleanups may occur on separate
+ // threads.
+ action.run();
+ }
+ }
+ }
+}
diff --git a/nearby/presence/np_java_ffi/java/com/google/android/nearby/presence/rust/DeserializeResult.java b/nearby/presence/np_java_ffi/java/com/google/android/nearby/presence/rust/DeserializeResult.java
index c0dc922..5be7f61 100644
--- a/nearby/presence/np_java_ffi/java/com/google/android/nearby/presence/rust/DeserializeResult.java
+++ b/nearby/presence/np_java_ffi/java/com/google/android/nearby/presence/rust/DeserializeResult.java
@@ -34,6 +34,7 @@
public final class DeserializeResult<M extends CredentialBook.MatchedMetadata>
implements AutoCloseable {
+ /** The kind of result. Negative values are errors and do not contain advertisement instances. */
@IntDef({
Kind.UNKNOWN_ERROR,
Kind.V0_ADVERTISEMENT,
@@ -52,8 +53,8 @@
return kind <= 0;
}
- private final @Kind int kind;
- private final @Nullable DeserializedAdvertisement advertisement;
+ @Kind private final int kind;
+ @Nullable private final DeserializedAdvertisement advertisement;
/** Create a DeserializeResult containing an error code */
/* package */ DeserializeResult(@Kind int errorKind) {
@@ -66,13 +67,13 @@
}
/** Create a DeserializeResult containing a V0 advertisement */
- /* package */ DeserializeResult(DeserializedV0Advertisement advertisement) {
+ /* package */ DeserializeResult(DeserializedV0Advertisement<M> advertisement) {
this.kind = Kind.V0_ADVERTISEMENT;
this.advertisement = advertisement;
}
/** Create a DeserializeResult containing a V1 advertisement */
- /* package */ DeserializeResult(DeserializedV1Advertisement advertisement) {
+ /* package */ DeserializeResult(DeserializedV1Advertisement<M> advertisement) {
this.kind = Kind.V1_ADVERTISEMENT;
this.advertisement = advertisement;
}
@@ -93,6 +94,7 @@
*
* @return the contained V0 advertisement or {@code null} if not present
*/
+ @SuppressWarnings("unchecked")
@Nullable
public DeserializedV0Advertisement<M> getAsV0() {
if (this.kind != Kind.V0_ADVERTISEMENT) {
@@ -106,12 +108,13 @@
*
* @return the contained V1 advertisement or {@code null} if not present
*/
+ @SuppressWarnings("unchecked")
@Nullable
public DeserializedV1Advertisement<M> getAsV1() {
if (this.kind != Kind.V1_ADVERTISEMENT) {
return null;
}
- return (DeserializedV1Advertisement) this.advertisement;
+ return (DeserializedV1Advertisement<M>) this.advertisement;
}
/** Closes the contained advertisement if it exists. */
diff --git a/nearby/presence/np_java_ffi/java/com/google/android/nearby/presence/rust/DeserializedV0Advertisement.java b/nearby/presence/np_java_ffi/java/com/google/android/nearby/presence/rust/DeserializedV0Advertisement.java
index cb557c5..7df838e 100644
--- a/nearby/presence/np_java_ffi/java/com/google/android/nearby/presence/rust/DeserializedV0Advertisement.java
+++ b/nearby/presence/np_java_ffi/java/com/google/android/nearby/presence/rust/DeserializedV0Advertisement.java
@@ -19,6 +19,7 @@
import androidx.annotation.Nullable;
import com.google.android.nearby.presence.rust.credential.CredentialBook;
import java.util.Iterator;
+import org.checkerframework.checker.nullness.qual.EnsuresNonNull;
/**
* A deserialized V0 advertisement. This class is backed by native data behind the {@link V0Payload}
@@ -32,9 +33,9 @@
}
private final int numDataElements;
- private final @Nullable V0Payload payload;
- private final @IdentityKind int identity;
- private final CredentialBook<M> credentialBook;
+ @Nullable private final V0Payload payload;
+ @IdentityKind private final int identity;
+ @Nullable private final CredentialBook<M> credentialBook;
/** Create an illegible instance with the given error identity. */
/* package */ DeserializedV0Advertisement(@IdentityKind int illegibleIdentity) {
@@ -79,8 +80,9 @@
}
/** Throws {@code IllegalStateException} if this advertisement is not legible. */
+ @EnsuresNonNull({"payload", "credentialBook"})
private void ensureLegible(String action) {
- if (!isLegible()) {
+ if (!isLegible() || payload == null || credentialBook == null) {
throw new IllegalStateException(
String.format("Cannot %s for non-legible advertisement", action));
}
@@ -122,7 +124,11 @@
/** Gets all the data elements for iteration. */
public Iterable<V0DataElement> getDataElements() {
- return () -> new DataElementIterator(payload, numDataElements);
+ ensureLegible("get data elements");
+ return () -> {
+ ensureLegible("get data element iterator");
+ return new DataElementIterator(payload, numDataElements);
+ };
}
/** Visits all the data elements with the given visitor. */
@@ -179,6 +185,7 @@
*/
@Nullable
public byte[] getDecryptedMetadata() {
+ ensureLegible("get decrypted metadata");
return payload.getDecryptedMetadata();
}
@@ -196,7 +203,7 @@
@Override
public boolean hasNext() {
- return position < (numDataElements - 1);
+ return position < numDataElements;
}
@Override
diff --git a/nearby/presence/np_java_ffi/java/com/google/android/nearby/presence/rust/DeserializedV1Advertisement.java b/nearby/presence/np_java_ffi/java/com/google/android/nearby/presence/rust/DeserializedV1Advertisement.java
index 8f4c3a3..cbe855a 100644
--- a/nearby/presence/np_java_ffi/java/com/google/android/nearby/presence/rust/DeserializedV1Advertisement.java
+++ b/nearby/presence/np_java_ffi/java/com/google/android/nearby/presence/rust/DeserializedV1Advertisement.java
@@ -20,7 +20,7 @@
import java.util.Iterator;
/**
- * A deserialized V0 advertisement. This class is backed by native data behind the {@link
+ * A deserialized V1 advertisement. This class is backed by native data behind the {@link
* LegibleV1Sections} handle. If this class is closed then the underlying handle will be closed too.
* Methods on this class should not be called if {@link #close()} has already been called.
*/
@@ -67,13 +67,13 @@
}
/** Get an iterable of this advertisement's legible sections. */
- public Iterable<DeserializedV1Section> getSections() {
- return () -> new SectionIterator(numLegibleSections, legibleSections, credentialBook);
+ public Iterable<DeserializedV1Section<M>> getSections() {
+ return () -> new SectionIterator<>(numLegibleSections, legibleSections, credentialBook);
}
/** Iterator instance for sections in DeserializedV1Advertisement. */
private static final class SectionIterator<M extends CredentialBook.MatchedMetadata>
- implements Iterator<DeserializedV1Section> {
+ implements Iterator<DeserializedV1Section<M>> {
private final LegibleV1Sections legibleSections;
private final int numSections;
private final CredentialBook<M> credentialBook;
@@ -89,11 +89,11 @@
@Override
public boolean hasNext() {
- return position < (numSections - 1);
+ return position < numSections;
}
@Override
- public DeserializedV1Section next() {
+ public DeserializedV1Section<M> next() {
return legibleSections.getSection(position++, credentialBook);
}
}
diff --git a/nearby/presence/np_java_ffi/java/com/google/android/nearby/presence/rust/DeserializedV1Section.java b/nearby/presence/np_java_ffi/java/com/google/android/nearby/presence/rust/DeserializedV1Section.java
index d320963..3c26e9e 100644
--- a/nearby/presence/np_java_ffi/java/com/google/android/nearby/presence/rust/DeserializedV1Section.java
+++ b/nearby/presence/np_java_ffi/java/com/google/android/nearby/presence/rust/DeserializedV1Section.java
@@ -16,27 +16,21 @@
package com.google.android.nearby.presence.rust;
-import static java.lang.annotation.RetentionPolicy.SOURCE;
-
-import androidx.annotation.IntDef;
import androidx.annotation.Nullable;
import com.google.android.nearby.presence.rust.credential.CredentialBook;
-import java.lang.annotation.Retention;
import java.util.Iterator;
+/**
+ * A section from a deserialized V1 advertisement. This object is valid for only as long as the
+ * containing advertisement object is valid; it is backed by the same {@link LegibleV1Sections}
+ * instance.
+ */
public final class DeserializedV1Section<M extends CredentialBook.MatchedMetadata> {
- @IntDef({VerificationMode.MIC, VerificationMode.SIGNATURE})
- @Retention(SOURCE)
- public @interface VerificationMode {
- public static final int MIC = 0;
- public static final int SIGNATURE = 1;
- }
-
private final LegibleV1Sections legibleSections;
private final int legibleSectionsIndex;
private final int numDataElements;
- private final @IdentityKind int identityTag;
+ @IdentityKind private final int identityTag;
private final CredentialBook<M> credentialBook;
/* package */ DeserializedV1Section(
@@ -66,7 +60,6 @@
/**
* Gets the data element at the given {@code index} in this advertisement.
*
- * @throws IllegalStateException if the advertisement is not legible ({@link #isLegible()}).
* @throws IndexOutOfBoundsException if the index is invalid
*/
public V1DataElement getDataElement(int index) {
@@ -148,7 +141,7 @@
@Override
public boolean hasNext() {
- return position < (numDataElements - 1);
+ return position < numDataElements;
}
@Override
diff --git a/nearby/presence/np_java_ffi/java/com/google/android/nearby/presence/rust/LegibleV1Sections.java b/nearby/presence/np_java_ffi/java/com/google/android/nearby/presence/rust/LegibleV1Sections.java
index 4a089c6..a5ac931 100644
--- a/nearby/presence/np_java_ffi/java/com/google/android/nearby/presence/rust/LegibleV1Sections.java
+++ b/nearby/presence/np_java_ffi/java/com/google/android/nearby/presence/rust/LegibleV1Sections.java
@@ -16,11 +16,8 @@
package com.google.android.nearby.presence.rust;
-import static com.google.android.nearby.presence.rust.DeserializedV1Section.VerificationMode;
-
import androidx.annotation.Nullable;
import com.google.android.nearby.presence.rust.credential.CredentialBook;
-import java.lang.ref.Cleaner;
import java.util.Arrays;
/** Internal handle for a V1 deserialized advertisement. */
@@ -71,7 +68,7 @@
}
/** Create a LegibleV1Sections handle from the raw handle id. */
- /* package-visible */ LegibleV1Sections(long handleId, Cleaner cleaner) {
+ private LegibleV1Sections(long handleId, CooperativeCleaner cleaner) {
super(handleId, cleaner, LegibleV1Sections::deallocate);
}
@@ -84,11 +81,11 @@
*/
public <M extends CredentialBook.MatchedMetadata> DeserializedV1Section<M> getSection(
int index, CredentialBook<M> credentialBook) {
- DeserializedV1Section section = nativeGetSection(index, credentialBook);
+ DeserializedV1Section<M> section = nativeGetSection(index, credentialBook);
if (section == null) {
throw new IndexOutOfBoundsException();
}
- return (DeserializedV1Section<M>) section;
+ return section;
}
/**
@@ -118,7 +115,8 @@
}
@Nullable
- private native DeserializedV1Section nativeGetSection(int index, CredentialBook credentialBook);
+ private native <M extends CredentialBook.MatchedMetadata>
+ DeserializedV1Section<M> nativeGetSection(int index, CredentialBook<M> credentialBook);
@Nullable
private native V1DataElement nativeGetSectionDataElement(int sectionIndex, int deIndex);
diff --git a/nearby/presence/np_java_ffi/java/com/google/android/nearby/presence/rust/NpAdv.java b/nearby/presence/np_java_ffi/java/com/google/android/nearby/presence/rust/NpAdv.java
index a349ebc..b7639ab 100644
--- a/nearby/presence/np_java_ffi/java/com/google/android/nearby/presence/rust/NpAdv.java
+++ b/nearby/presence/np_java_ffi/java/com/google/android/nearby/presence/rust/NpAdv.java
@@ -18,14 +18,10 @@
import androidx.annotation.Nullable;
import com.google.android.nearby.presence.rust.credential.CredentialBook;
-import java.lang.ref.Cleaner;
/**
* The main entrypoint to the library.
*
- * <p>On Android call {@link #setCleaner} with a {@code SystemCleaner} instance before any other
- * method to avoid creating a new cleaner thread.
- *
* <h3>Supported Features:</h3>
*
* <ul>
@@ -43,7 +39,12 @@
System.loadLibrary(LIBRARY_NAME);
}
- private static @Nullable Cleaner CLEANER = null;
+ // This is effectively an injected variable, but without depending on a DI implementation.
+ @SuppressWarnings("NonFinalStaticField")
+ @Nullable
+ private static CooperativeCleaner cleaner = null;
+
+ private NpAdv() {}
/**
* Deserialize a Nearby Presence advertisement from its service data bytes.
@@ -55,40 +56,26 @@
public static <M extends CredentialBook.MatchedMetadata>
DeserializeResult<M> deserializeAdvertisement(
byte[] serviceData, CredentialBook<M> credentialBook) {
- DeserializeResult result = nativeDeserializeAdvertisement(serviceData, credentialBook);
+ DeserializeResult<M> result = nativeDeserializeAdvertisement(serviceData, credentialBook);
if (result == null) {
- result = new DeserializeResult(DeserializeResult.Kind.UNKNOWN_ERROR);
+ result = new DeserializeResult<M>(DeserializeResult.Kind.UNKNOWN_ERROR);
}
return result;
}
/**
- * Get the currently configured cleaner. If a cleaner is not configured, a new one will be created
- * via the {@link Cleaner#create()} factory function.
+ * Get the currently configured cleaner. If a cleaner is not configured, a new one will be
+ * created.
*/
- public static synchronized Cleaner getCleaner() {
- if (CLEANER == null) {
- CLEANER = Cleaner.create();
+ public static synchronized CooperativeCleaner getCleaner() {
+ if (cleaner == null) {
+ cleaner = new CooperativeCleaner();
}
- return CLEANER;
- }
-
- /**
- * Configure a {@link Cleaner} to be used by this library. This cleaner will be used to ensure
- * that {@link OwnedHandle} instances are properly freed. Since each {@code Cleaner} instance
- * requires its own thread; this can be used to share a {@code Cleaner} instance to reduce the
- * number of threads used.
- *
- * <p>On Android the {@code SystemCleaner} should be provided.
- */
- @Nullable
- public static synchronized Cleaner setCleaner(Cleaner cleaner) {
- Cleaner old = CLEANER;
- CLEANER = cleaner;
- return old;
+ return cleaner;
}
@Nullable
- private static native DeserializeResult nativeDeserializeAdvertisement(
- byte[] serviceData, CredentialBook credentialBook);
+ private static native <M extends CredentialBook.MatchedMetadata>
+ DeserializeResult<M> nativeDeserializeAdvertisement(
+ byte[] serviceData, CredentialBook<M> credentialBook);
}
diff --git a/nearby/presence/np_java_ffi/java/com/google/android/nearby/presence/rust/OwnedHandle.java b/nearby/presence/np_java_ffi/java/com/google/android/nearby/presence/rust/OwnedHandle.java
index c505f81..a1c1a79 100644
--- a/nearby/presence/np_java_ffi/java/com/google/android/nearby/presence/rust/OwnedHandle.java
+++ b/nearby/presence/np_java_ffi/java/com/google/android/nearby/presence/rust/OwnedHandle.java
@@ -16,8 +16,8 @@
package com.google.android.nearby.presence.rust;
+import androidx.annotation.GuardedBy;
import androidx.annotation.Nullable;
-import java.lang.ref.Cleaner;
/**
* A handle to natively-allocated object with lifetime control. This is a {@code Handle} that also
@@ -25,7 +25,8 @@
*
* <p>Users should call {@link OwnedHandle#close()} when finished with this handle to free the
* native resources. This can be automatically done when using try-with-resources. If neither are
- * use the handle will still be closed when it is garbage collected.
+ * use the handle will still be freed sometime after this object has been garbage collected by a
+ * subsequent call to {@link OwnedHandle#close()} on another {@code OwnedHandle} instance.
*/
public abstract class OwnedHandle extends Handle implements AutoCloseable {
@@ -34,6 +35,11 @@
*
* <p>This MUST not hold a reference to the {@link OwnedHandle} instance. Do not implement this on
* your subclass; however, it may be implemented by a method reference to a static method.
+ *
+ * <p>This destructor will be run unless {@link OwnedHandle#leak()} is called. It will be run on
+ * the thread that calls {@link OwnedHandle#close()} if the handle is closed. If neither leak nor
+ * close are called and this handle is garbage collected, then it will be run on an unspecified
+ * thread. See {@link CooperativeCleaner}.
*/
public interface Destructor {
void deallocate(long handleId);
@@ -47,6 +53,7 @@
}
private final CleanupAction cleanupAction;
+ private final CooperativeCleaner.Registration cleanerRegistration;
/**
* Create a new instance and register it with the given cleaner.
@@ -55,22 +62,23 @@
* @param cleaner The cleaner thread to register with for GC cleanup
* @param destructor The destructor to run when this handle is closed
*/
- protected OwnedHandle(long handleId, Cleaner cleaner, Destructor destructor) {
+ protected OwnedHandle(long handleId, CooperativeCleaner cleaner, Destructor destructor) {
super(handleId);
this.cleanupAction = new CleanupAction(handleId, destructor);
- cleaner.register(this, this.cleanupAction);
+ cleanerRegistration = cleaner.register(this, this.cleanupAction);
}
/** Leak this handle. The associated native object will not be deallocated. */
protected final void leak() {
- this.cleanupAction.leak();
+ cleanupAction.leak();
+ close();
}
/** Implement AutoCloseable for try-with-resources support */
@Override
public final void close() {
- this.cleanupAction.cleanupFromCloseable();
+ cleanerRegistration.close();
}
/**
@@ -79,8 +87,10 @@
*/
private static final class CleanupAction implements Runnable {
private final long handleId;
- private @Nullable Destructor destructor;
- private boolean freed = false;
+
+ @GuardedBy("this")
+ @Nullable
+ private Destructor destructor;
public CleanupAction(long handleId, Destructor destructor) {
this.handleId = handleId;
@@ -89,7 +99,9 @@
/** Skip performing cleanup and leak the object instead */
private void leak() {
- this.destructor = null;
+ synchronized (this) {
+ this.destructor = null;
+ }
}
/**
@@ -101,29 +113,26 @@
* @return {@code true} if the destructor was called.
*/
private boolean deallocate() {
- if (this.destructor != null) {
- this.destructor.deallocate(this.handleId);
- this.destructor = null;
+ // Take the destructor if present
+ Destructor destructor = null;
+ synchronized (this) {
+ if (this.destructor != null) {
+ destructor = this.destructor;
+ this.destructor = null;
+ }
+ }
+
+ // Run destructor if it was present
+ if (destructor != null) {
+ destructor.deallocate(handleId);
return true;
}
return false;
}
- /**
- * Perform the cleanup action. This is separate from {@link #run()} so that we can track if the
- * handle was manually closed or if it was cleaned up via the {@link Cleaner}.
- */
- public void cleanupFromCloseable() {
- if (!deallocate()) {
- // FUTURE: log that OwnedHandle#close() was called multiple times.
- }
- }
-
@Override
public void run() {
- if (deallocate()) {
- // FUTURE: log that OwnedHandle#close() was not called.
- }
+ boolean unused = deallocate();
}
}
}
diff --git a/nearby/presence/np_java_ffi/java/com/google/android/nearby/presence/rust/SerializationException.java b/nearby/presence/np_java_ffi/java/com/google/android/nearby/presence/rust/SerializationException.java
index 4fa4f4f..741b030 100644
--- a/nearby/presence/np_java_ffi/java/com/google/android/nearby/presence/rust/SerializationException.java
+++ b/nearby/presence/np_java_ffi/java/com/google/android/nearby/presence/rust/SerializationException.java
@@ -29,6 +29,19 @@
}
}
+ public static final class UnclosedActiveSectionException extends RuntimeException {
+ public UnclosedActiveSectionException() {
+ super(
+ "There is currently an active section builder which must be finished before continuing");
+ }
+ }
+
+ public static final class InvalidSectionKindException extends RuntimeException {
+ public InvalidSectionKindException() {
+ super("A section of this kind is not allowed in this advertisement");
+ }
+ }
+
public static final class InsufficientSpaceException extends SerializationException {
public InsufficientSpaceException() {
super("There isn't enough space remaining in the advertisement");
diff --git a/nearby/presence/np_java_ffi/java/com/google/android/nearby/presence/rust/V0AdvertisementBuilder.java b/nearby/presence/np_java_ffi/java/com/google/android/nearby/presence/rust/V0AdvertisementBuilder.java
index e597303..0061fff 100644
--- a/nearby/presence/np_java_ffi/java/com/google/android/nearby/presence/rust/V0AdvertisementBuilder.java
+++ b/nearby/presence/np_java_ffi/java/com/google/android/nearby/presence/rust/V0AdvertisementBuilder.java
@@ -18,12 +18,15 @@
import com.google.android.nearby.presence.rust.SerializationException.InsufficientSpaceException;
import com.google.android.nearby.presence.rust.SerializationException.InvalidDataElementException;
+import com.google.android.nearby.presence.rust.V0DataElement.TxPower;
+import com.google.android.nearby.presence.rust.V0DataElement.V0Actions;
import com.google.android.nearby.presence.rust.credential.V0BroadcastCredential;
-import java.lang.ref.Cleaner;
+import com.google.errorprone.annotations.CanIgnoreReturnValue;
/**
- * A builder for V0 advertisements. Create a new instance with {@link #newPublic()} for a public
- * advertisement or {@link #newEncrypted()} for an encrypted advertisement.
+ * A builder for V0 advertisements. Create a new instance with {@link
+ * V0AdvertisementBuilder#newPublic()} for a public advertisement or {@link
+ * V0AdvertisementBuilder#newEncrypted()} for an encrypted advertisement.
*/
public final class V0AdvertisementBuilder implements AutoCloseable {
@@ -32,19 +35,19 @@
return newPublic(NpAdv.getCleaner());
}
+ /** Create a builder for a public advertisement with a specific cleaner. */
+ private static V0AdvertisementBuilder newPublic(CooperativeCleaner cleaner) {
+ return new V0AdvertisementBuilder(new V0BuilderHandle(cleaner));
+ }
+
/** Create a builder for an encrypted advertisement. */
public static V0AdvertisementBuilder newEncrypted(V0BroadcastCredential credential, byte[] salt) {
return newEncrypted(NpAdv.getCleaner(), credential, salt);
}
- /** Create a builder for a public advertisement with a specific Cleaner. */
- public static V0AdvertisementBuilder newPublic(Cleaner cleaner) {
- return new V0AdvertisementBuilder(new V0BuilderHandle(cleaner));
- }
-
- /** Create a builder for an encrypted advertisement with a specific Cleaner. */
- public static V0AdvertisementBuilder newEncrypted(
- Cleaner cleaner, V0BroadcastCredential credential, byte[] salt) {
+ /** Create a builder for an encrypted advertisement with a specific cleaner. */
+ private static V0AdvertisementBuilder newEncrypted(
+ CooperativeCleaner cleaner, V0BroadcastCredential credential, byte[] salt) {
return new V0AdvertisementBuilder(new V0BuilderHandle(cleaner, credential, salt));
}
@@ -62,8 +65,11 @@
* of range)
* @throws InsufficientSpaceException when the data element will not fit in the remaining space.
*/
- public void addDataElement(V0DataElement dataElement) throws InsufficientSpaceException {
+ @CanIgnoreReturnValue
+ public V0AdvertisementBuilder addDataElement(V0DataElement dataElement)
+ throws InsufficientSpaceException {
builder.addDataElement(dataElement);
+ return this;
}
/**
@@ -89,26 +95,59 @@
System.loadLibrary(NpAdv.LIBRARY_NAME);
}
- public V0BuilderHandle(Cleaner cleaner) {
+ /** Create a public builder. */
+ public V0BuilderHandle(CooperativeCleaner cleaner) {
super(allocatePublic(), cleaner, V0BuilderHandle::deallocate);
}
- public V0BuilderHandle(Cleaner cleaner, V0BroadcastCredential credential, byte[] salt) {
+ /** Create an encrypted builder. */
+ public V0BuilderHandle(
+ CooperativeCleaner cleaner, V0BroadcastCredential credential, byte[] salt) {
super(allocatePrivate(credential, salt), cleaner, V0BuilderHandle::deallocate);
}
- public void addDataElement(V0DataElement dataElement) {
- // Call the appropriate native add call based on the data element type.
- dataElement.visit(
- new V0DataElement.Visitor() {
- public void visitTxPower(V0DataElement.TxPower txPower) {
- nativeAddTxPowerDataElement(txPower);
- }
+ private class AddDataElementVisitor implements V0DataElement.Visitor {
+ @Override
+ public void visitTxPower(TxPower txPower) {
+ try {
+ nativeAddTxPowerDataElement(txPower);
+ } catch (InsufficientSpaceException ise) {
+ throw new SmuggledInsufficientSpaceException(ise);
+ }
+ }
- public void visitV0Actions(V0DataElement.V0Actions v0Actions) {
- nativeAddV0ActionsDataElement(v0Actions);
- }
- });
+ @Override
+ public void visitV0Actions(V0Actions actions) {
+ try {
+ nativeAddV0ActionsDataElement(actions);
+ } catch (InsufficientSpaceException ise) {
+ throw new SmuggledInsufficientSpaceException(ise);
+ }
+ }
+ }
+
+ /**
+ * Helper to smuggle {@link InsufficientSpaceException} (a checked exception) through APIs that
+ * don't support checked exceptions.
+ */
+ private static class SmuggledInsufficientSpaceException extends RuntimeException {
+ private final InsufficientSpaceException ise;
+
+ public SmuggledInsufficientSpaceException(InsufficientSpaceException ise) {
+ this.ise = ise;
+ }
+
+ public void throwChecked() throws InsufficientSpaceException {
+ throw ise;
+ }
+ }
+
+ public void addDataElement(V0DataElement dataElement) throws InsufficientSpaceException {
+ try {
+ dataElement.visit(new AddDataElementVisitor());
+ } catch (SmuggledInsufficientSpaceException ex) {
+ ex.throwChecked();
+ }
}
public byte[] build()
@@ -123,9 +162,11 @@
private static native long allocatePrivate(V0BroadcastCredential credential, byte[] salt);
- private native void nativeAddTxPowerDataElement(V0DataElement.TxPower txPower);
+ private native void nativeAddTxPowerDataElement(TxPower txPower)
+ throws InsufficientSpaceException;
- private native void nativeAddV0ActionsDataElement(V0DataElement.V0Actions v0Actions);
+ private native void nativeAddV0ActionsDataElement(V0Actions v0Actions)
+ throws InsufficientSpaceException;
private native byte[] nativeBuild()
throws SerializationException.LdtEncryptionException,
diff --git a/nearby/presence/np_java_ffi/java/com/google/android/nearby/presence/rust/V0DataElement.java b/nearby/presence/np_java_ffi/java/com/google/android/nearby/presence/rust/V0DataElement.java
index c2c6325..0ee2157 100644
--- a/nearby/presence/np_java_ffi/java/com/google/android/nearby/presence/rust/V0DataElement.java
+++ b/nearby/presence/np_java_ffi/java/com/google/android/nearby/presence/rust/V0DataElement.java
@@ -52,6 +52,7 @@
return txPower;
}
+ @Override
public void visit(Visitor v) {
v.visitTxPower(this);
}
@@ -68,7 +69,7 @@
})
@Retention(SOURCE)
public @interface V0ActionType {
- // NOTE: Copied from `np_ffi_core::v0::BooleanActionType`.
+ // NOTE: Copied from `np_ffi_core::v0::ActionType`.
public static final int CROSS_DEV_SDK = 1;
public static final int CALL_TRANSFER = 4;
public static final int ACTIVE_UNLOCK = 8;
@@ -83,7 +84,7 @@
System.loadLibrary(NpAdv.LIBRARY_NAME);
}
- private final @IdentityKind int identityKind;
+ @IdentityKind private final int identityKind;
private final int actionBits;
/**
@@ -99,8 +100,8 @@
this(identityKind, nativeMergeActions(identityKind, actions));
}
- /** Used by native code. */
- V0Actions(@IdentityKind int identityKind, int actionBits) {
+ /** Used by native code. This must be private to avoid being confused with the above method. */
+ private V0Actions(@IdentityKind int identityKind, int actionBits) {
this.identityKind = identityKind;
this.actionBits = actionBits;
}
@@ -119,6 +120,7 @@
return nativeHasAction(identityKind, actionBits, action);
}
+ @Override
public void visit(Visitor v) {
v.visitV0Actions(this);
}
diff --git a/nearby/presence/np_java_ffi/java/com/google/android/nearby/presence/rust/V0Payload.java b/nearby/presence/np_java_ffi/java/com/google/android/nearby/presence/rust/V0Payload.java
index e6abea8..588a2ca 100644
--- a/nearby/presence/np_java_ffi/java/com/google/android/nearby/presence/rust/V0Payload.java
+++ b/nearby/presence/np_java_ffi/java/com/google/android/nearby/presence/rust/V0Payload.java
@@ -17,7 +17,6 @@
package com.google.android.nearby.presence.rust;
import androidx.annotation.Nullable;
-import java.lang.ref.Cleaner;
import java.util.Arrays;
/**
@@ -68,7 +67,7 @@
}
/** Create a V0Payload handle from the raw handle id. */
- /* package-visible */ V0Payload(long handleId, Cleaner cleaner) {
+ private V0Payload(long handleId, CooperativeCleaner cleaner) {
super(handleId, cleaner, V0Payload::deallocate);
}
diff --git a/nearby/presence/np_java_ffi/java/com/google/android/nearby/presence/rust/V1AdvertisementBuilder.java b/nearby/presence/np_java_ffi/java/com/google/android/nearby/presence/rust/V1AdvertisementBuilder.java
new file mode 100644
index 0000000..058494b
--- /dev/null
+++ b/nearby/presence/np_java_ffi/java/com/google/android/nearby/presence/rust/V1AdvertisementBuilder.java
@@ -0,0 +1,196 @@
+/*
+ * 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.nearby.presence.rust;
+
+import androidx.annotation.VisibleForTesting;
+import com.google.android.nearby.presence.rust.SerializationException.InsufficientSpaceException;
+import com.google.android.nearby.presence.rust.credential.V1BroadcastCredential;
+
+/**
+ * A builder for V1 advertisements. Create a new instance with {@link
+ * V1AdvertisementBuilder#newPublic()} for a public advertisement or {@link
+ * V1AdvertisementBuilder#newEncrypted()} for an encrypted advertisement.
+ *
+ * <p>Public advertisements may only contain public sections. Encrypted advertisements may only
+ * contain encrypted sections. Use {@link V1AdvertisementBuilder#addPublicSection()} and {@link
+ * V1AdvertisementBuilder#addEncryptedSection()} to add sections to the advertisement. Only one
+ * section may be built at a time.
+ */
+public final class V1AdvertisementBuilder implements AutoCloseable {
+
+ /** Create a builder for a public advertisement. */
+ public static V1AdvertisementBuilder newPublic() {
+ return newPublic(NpAdv.getCleaner());
+ }
+
+ /** Create a builder for a public advertisement with a specific cleaner. */
+ private static V1AdvertisementBuilder newPublic(CooperativeCleaner cleaner) {
+ return new V1AdvertisementBuilder(new V1BuilderHandle(cleaner, false));
+ }
+
+ /** Create a builder for an encrypted advertisement. */
+ public static V1AdvertisementBuilder newEncrypted() {
+ return newEncrypted(NpAdv.getCleaner());
+ }
+
+ /** Create a builder for an encrypted advertisement with a specific cleaner. */
+ private static V1AdvertisementBuilder newEncrypted(CooperativeCleaner cleaner) {
+ return new V1AdvertisementBuilder(new V1BuilderHandle(cleaner, true));
+ }
+
+ @VisibleForTesting final V1BuilderHandle builder;
+
+ private V1AdvertisementBuilder(V1BuilderHandle builder) {
+ this.builder = builder;
+ }
+
+ /**
+ * Adds a public section to this advertisement. This is only valid on public advertisements.
+ *
+ * @throws UnclosedActiveSectionException when there is already an active section builder.
+ * @throws InvalidHandleException if this builder has already been closed.
+ * @throws InvalidSectionKindException if this advertisement builder is not a public advertisement
+ * builder.
+ * @throws InsufficientSpaceException if this advertisement does not have enough space for a new
+ * section
+ */
+ public V1SectionBuilder addPublicSection() throws InsufficientSpaceException {
+ return builder.addPublicSection();
+ }
+
+ /**
+ * Adds an encrypted section to this advertisement. This is only valid on encrypted
+ * advertisements.
+ *
+ * @throws UnclosedActiveSectionException when there is already an active section builder.
+ * @throws InvalidHandleException if this builder has already been closed.
+ * @throws InvalidSectionKindException if this advertisement builder is not an encrypted
+ * advertisement builder.
+ * @throws InsufficientSpaceException if this advertisement does not have enough space for a new
+ * section
+ */
+ public V1SectionBuilder addEncryptedSection(
+ V1BroadcastCredential credential, @VerificationMode int verificationMode)
+ throws InsufficientSpaceException {
+ return builder.addEncryptedSection(credential, verificationMode);
+ }
+
+ /**
+ * Build this advertisement into a byte buffer. This will consume the builder when called.
+ *
+ * @throws UnclosedActiveSectionException when there is an active section builder.
+ */
+ public byte[] build() {
+ return builder.build();
+ }
+
+ /** Deallocates this section builder. */
+ @Override
+ public void close() {
+ builder.close();
+ }
+
+ /** Internal builder handle object. */
+ static final class V1BuilderHandle extends OwnedHandle {
+ static {
+ System.loadLibrary(NpAdv.LIBRARY_NAME);
+ }
+
+ public V1BuilderHandle(CooperativeCleaner cleaner, boolean encrypted) {
+ super(allocate(encrypted), cleaner, V1BuilderHandle::deallocate);
+ }
+
+ public V1SectionBuilder addPublicSection() throws InsufficientSpaceException {
+ return nativeAddPublicSection();
+ }
+
+ public V1SectionBuilder addEncryptedSection(
+ V1BroadcastCredential credential, @VerificationMode int verificationMode)
+ throws InsufficientSpaceException {
+ return nativeAddEncryptedSection(credential, verificationMode);
+ }
+
+ private class AddDataElementVisitor implements V1DataElement.Visitor {
+ private final int section;
+
+ private AddDataElementVisitor(int section) {
+ this.section = section;
+ }
+
+ @Override
+ public void visitGeneric(V1DataElement.Generic generic) {
+ try {
+ nativeAddGenericDataElement(section, generic);
+ } catch (InsufficientSpaceException ise) {
+ throw new SmuggledInsufficientSpaceException(ise);
+ }
+ }
+ }
+
+ /**
+ * Helper to smuggle {@link InsufficientSpaceException} (a checked exception) through APIs that
+ * don't support checked exceptions.
+ */
+ private static class SmuggledInsufficientSpaceException extends RuntimeException {
+ private final InsufficientSpaceException ise;
+
+ public SmuggledInsufficientSpaceException(InsufficientSpaceException ise) {
+ this.ise = ise;
+ }
+
+ public void throwChecked() throws InsufficientSpaceException {
+ throw ise;
+ }
+ }
+
+ public void addDataElement(int section, V1DataElement dataElement)
+ throws InsufficientSpaceException {
+ try {
+ dataElement.visit(new AddDataElementVisitor(section));
+ } catch (SmuggledInsufficientSpaceException ex) {
+ ex.throwChecked();
+ }
+ }
+
+ public void finishSection(int section) {
+ nativeFinishSection(section);
+ }
+
+ public byte[] build() {
+ // `nativeBuild` takes ownership so we leak the Java side here.
+ leak();
+ return nativeBuild();
+ }
+
+ private static native long allocate(boolean encrypted);
+
+ private native V1SectionBuilder nativeAddPublicSection() throws InsufficientSpaceException;
+
+ private native V1SectionBuilder nativeAddEncryptedSection(
+ V1BroadcastCredential credential, @VerificationMode int verificationMode)
+ throws InsufficientSpaceException;
+
+ private native void nativeAddGenericDataElement(int section, V1DataElement.Generic generic)
+ throws InsufficientSpaceException;
+
+ private native void nativeFinishSection(int section);
+
+ private native byte[] nativeBuild();
+
+ private static native void deallocate(long handleId);
+ }
+}
diff --git a/nearby/presence/np_java_ffi/java/com/google/android/nearby/presence/rust/V1DataElement.java b/nearby/presence/np_java_ffi/java/com/google/android/nearby/presence/rust/V1DataElement.java
index b52c7bb..e7d921e 100644
--- a/nearby/presence/np_java_ffi/java/com/google/android/nearby/presence/rust/V1DataElement.java
+++ b/nearby/presence/np_java_ffi/java/com/google/android/nearby/presence/rust/V1DataElement.java
@@ -56,6 +56,7 @@
return Arrays.copyOf(data, data.length);
}
+ @Override
public void visit(Visitor v) {
v.visitGeneric(this);
}
diff --git a/nearby/presence/np_java_ffi/java/com/google/android/nearby/presence/rust/V1SectionBuilder.java b/nearby/presence/np_java_ffi/java/com/google/android/nearby/presence/rust/V1SectionBuilder.java
new file mode 100644
index 0000000..d2d62cc
--- /dev/null
+++ b/nearby/presence/np_java_ffi/java/com/google/android/nearby/presence/rust/V1SectionBuilder.java
@@ -0,0 +1,83 @@
+/*
+ * Copyright 2024 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.nearby.presence.rust;
+
+import com.google.android.nearby.presence.rust.SerializationException.InsufficientSpaceException;
+import com.google.android.nearby.presence.rust.V1AdvertisementBuilder.V1BuilderHandle;
+import com.google.errorprone.annotations.CanIgnoreReturnValue;
+
+/**
+ * A builder for V1 advertisement sections. Create a new instance with {@link
+ * V1AdvertisementBuilder#addPublicSection()} for a public section or {@link
+ * V1AdvertisementBuilder#addEncryptedSection()} for an encrypted section.
+ */
+public final class V1SectionBuilder implements AutoCloseable {
+
+ private final V1BuilderHandle builder;
+ private final int section;
+
+ /**
+ * Whether {@link #finishSection()} has already been called by the client. This is used to avoid
+ * double-finishing when used in a try-with-resources block.
+ */
+ private boolean finishCalled = false;
+
+ // Native used
+ @SuppressWarnings("UnusedMethod")
+ private V1SectionBuilder(V1BuilderHandle builder, int section) {
+ this.builder = builder;
+ this.section = section;
+ }
+
+ /**
+ * Add a data element to the advertisement. If it cannot be added an exception will be thrown. A
+ * thrown exception will not invalidate this builder.
+ *
+ * @throws InvalidDataElementException when the given data element is not valid (e.g. value out of
+ * range, too large, etc.)
+ * @throws InsufficientSpaceException when the data element will not fit in the remaining space.
+ */
+ @CanIgnoreReturnValue
+ public V1SectionBuilder addDataElement(V1DataElement dataElement)
+ throws InsufficientSpaceException {
+ builder.addDataElement(section, dataElement);
+ return this;
+ }
+
+ /**
+ * Finishes writing the current seciton. This will consume the section builder when called.
+ *
+ * @throws InvalidHandleException if called multiple times or if the parent advertisement has been
+ * closed.
+ */
+ public void finishSection() {
+ builder.finishSection(section);
+ finishCalled = true;
+ }
+
+ /**
+ * Close this section builder. This will {@link #finishSection()} if the builder hasn't been
+ * finished yet.
+ */
+ @Override
+ public void close() {
+ // Finish the section if the user has not already done so explicitly
+ if (!finishCalled) {
+ finishSection();
+ }
+ }
+}
diff --git a/nearby/presence/np_java_ffi/java/com/google/android/nearby/presence/rust/VerificationMode.java b/nearby/presence/np_java_ffi/java/com/google/android/nearby/presence/rust/VerificationMode.java
new file mode 100644
index 0000000..ba7953b
--- /dev/null
+++ b/nearby/presence/np_java_ffi/java/com/google/android/nearby/presence/rust/VerificationMode.java
@@ -0,0 +1,29 @@
+/*
+ * Copyright 2024 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.nearby.presence.rust;
+
+import static java.lang.annotation.RetentionPolicy.SOURCE;
+
+import androidx.annotation.IntDef;
+import java.lang.annotation.Retention;
+
+@IntDef({VerificationMode.MIC, VerificationMode.SIGNATURE})
+@Retention(SOURCE)
+public @interface VerificationMode {
+ public static final int MIC = 0;
+ public static final int SIGNATURE = 1;
+}
diff --git a/nearby/presence/np_java_ffi/java/com/google/android/nearby/presence/rust/credential/CredentialBook.java b/nearby/presence/np_java_ffi/java/com/google/android/nearby/presence/rust/credential/CredentialBook.java
index 953c6e9..e264f13 100644
--- a/nearby/presence/np_java_ffi/java/com/google/android/nearby/presence/rust/credential/CredentialBook.java
+++ b/nearby/presence/np_java_ffi/java/com/google/android/nearby/presence/rust/credential/CredentialBook.java
@@ -17,11 +17,17 @@
package com.google.android.nearby.presence.rust.credential;
import androidx.annotation.Nullable;
+import com.google.android.nearby.presence.rust.CooperativeCleaner;
import com.google.android.nearby.presence.rust.NpAdv;
import com.google.android.nearby.presence.rust.OwnedHandle;
-import java.lang.ref.Cleaner;
+import com.google.errorprone.annotations.CanIgnoreReturnValue;
import java.util.ArrayList;
+/**
+ * A set of credentials used for deserialization. Create a credential book using the builder from
+ * {@link #builder()}. An empty credential book ({@link #empty()}) can be used to deserialize public
+ * advertisements and sections.
+ */
public final class CredentialBook<M extends CredentialBook.MatchedMetadata> extends OwnedHandle {
static {
@@ -68,7 +74,7 @@
* {@code getMatchedMetadata()} method on their {@code DeserializedAdvertisement} instance.
*/
public static final class Builder<M extends MatchedMetadata> {
- private Cleaner cleaner;
+ private CooperativeCleaner cleaner;
private CredentialSlab slab;
/**
@@ -79,19 +85,22 @@
/**
* Create a builder instance. The {@link CredentialBook#builder()} factory method can be used to
- * create a {@code Builder} with the default {@link Cleaner}. This can fail if there isn't room
- * to create a new {@code CredentialSlab} handle.
+ * create a {@code Builder} with the default {@link CooperativeCleaner}.
+ *
+ * <p>This can fail if there isn't room to create a new {@code CredentialSlab} handle with
+ * {@link OwnedHandle.NoSpaceLeftException}.
*
* @param cleaner The cleaner instance to use for the {@link CredentialSlab} and {@code
* CredentialBook}.
*/
- public Builder(Cleaner cleaner) {
+ private Builder(CooperativeCleaner cleaner) {
this.cleaner = cleaner;
this.slab = new CredentialSlab(cleaner);
this.matchDataList = new ArrayList<>();
}
/** Add a {@link V0DiscoveryCredential} to the book. */
+ @CanIgnoreReturnValue
public Builder<M> addDiscoveryCredential(V0DiscoveryCredential credential, M matchData) {
int credIdx = matchDataList.size();
matchDataList.add(matchData);
@@ -104,6 +113,7 @@
* CredentialBook.InvalidPublicKeyException} if the key inside {@code credential} is improperly
* formatted.
*/
+ @CanIgnoreReturnValue
public Builder<M> addDiscoveryCredential(V1DiscoveryCredential credential, M matchData) {
int credIdx = matchDataList.size();
matchDataList.add(matchData);
@@ -116,7 +126,7 @@
* CredentialBook} handle.
*/
public CredentialBook<M> build() {
- return new CredentialBook(slab, matchDataList, cleaner);
+ return new CredentialBook<>(slab, matchDataList, cleaner);
}
}
@@ -140,7 +150,8 @@
* from {@code Builder}. {@code matchData} is formatted so that each credential is given an id of
* the index of its metadata in {@code matchData}.
*/
- private CredentialBook(CredentialSlab slab, ArrayList<M> matchData, Cleaner cleaner) {
+ @SuppressWarnings("NonApiType")
+ private CredentialBook(CredentialSlab slab, ArrayList<M> matchData, CooperativeCleaner cleaner) {
super(allocate(slab.move()), cleaner, CredentialBook::deallocate);
this.matchData = matchData;
}
diff --git a/nearby/presence/np_java_ffi/java/com/google/android/nearby/presence/rust/credential/CredentialSlab.java b/nearby/presence/np_java_ffi/java/com/google/android/nearby/presence/rust/credential/CredentialSlab.java
index 35fc610..29f2a08 100644
--- a/nearby/presence/np_java_ffi/java/com/google/android/nearby/presence/rust/credential/CredentialSlab.java
+++ b/nearby/presence/np_java_ffi/java/com/google/android/nearby/presence/rust/credential/CredentialSlab.java
@@ -16,9 +16,9 @@
package com.google.android.nearby.presence.rust.credential;
+import com.google.android.nearby.presence.rust.CooperativeCleaner;
import com.google.android.nearby.presence.rust.NpAdv;
import com.google.android.nearby.presence.rust.OwnedHandle;
-import java.lang.ref.Cleaner;
/**
* A {@code CredentialSlab} handle that is used to build the native-side structures for a {@link
@@ -30,14 +30,16 @@
}
/** Create a new {@code CredentialSlab} with the given {@code cleaner}. */
- public CredentialSlab(Cleaner cleaner) {
+ public CredentialSlab(CooperativeCleaner cleaner) {
super(allocate(), cleaner, CredentialSlab::deallocate);
}
/** Add a V0 discovery credential to this slab. */
public void addDiscoveryCredential(
V0DiscoveryCredential credential, int credId, byte[] encryptedMetadataBytes) {
- nativeAddV0DiscoveryCredential(handleId, credential, credId, encryptedMetadataBytes);
+ if (!nativeAddV0DiscoveryCredential(handleId, credential, credId, encryptedMetadataBytes)) {
+ throw new IllegalStateException("Failed to add credential to slab");
+ }
}
/**
@@ -47,7 +49,9 @@
*/
public void addDiscoveryCredential(
V1DiscoveryCredential credential, int credId, byte[] encryptedMetadataBytes) {
- nativeAddV1DiscoveryCredential(handleId, credential, credId, encryptedMetadataBytes);
+ if (!nativeAddV1DiscoveryCredential(handleId, credential, credId, encryptedMetadataBytes)) {
+ throw new IllegalStateException("Failed to add credential to slab");
+ }
}
/**
diff --git a/nearby/presence/np_java_ffi/java/com/google/android/nearby/presence/rust/credential/Utils.java b/nearby/presence/np_java_ffi/java/com/google/android/nearby/presence/rust/credential/Utils.java
index 4f070e4..a7b8f3d 100644
--- a/nearby/presence/np_java_ffi/java/com/google/android/nearby/presence/rust/credential/Utils.java
+++ b/nearby/presence/np_java_ffi/java/com/google/android/nearby/presence/rust/credential/Utils.java
@@ -21,6 +21,8 @@
/** Util functions used by multiple files. */
final class Utils {
+ private Utils() {}
+
/**
* Create a copy of a {@code n}-byte array of data. Will throw {@code IllegalArgumentException} if
* the array is not exactly {@code n} bytes.
diff --git a/nearby/presence/np_java_ffi/java/com/google/android/nearby/presence/rust/credential/V0BroadcastCredential.java b/nearby/presence/np_java_ffi/java/com/google/android/nearby/presence/rust/credential/V0BroadcastCredential.java
index 9e8474b..d47a15a 100644
--- a/nearby/presence/np_java_ffi/java/com/google/android/nearby/presence/rust/credential/V0BroadcastCredential.java
+++ b/nearby/presence/np_java_ffi/java/com/google/android/nearby/presence/rust/credential/V0BroadcastCredential.java
@@ -19,6 +19,7 @@
import static com.google.android.nearby.presence.rust.credential.Utils.copyBytes;
/** A V0 broadcast credential in a format that is ready to be passed to native code. */
+@SuppressWarnings("UnusedVariable")
public final class V0BroadcastCredential {
private final byte[] keySeed;
private final byte[] identityToken;
diff --git a/nearby/presence/np_java_ffi/java/com/google/android/nearby/presence/rust/credential/V0DiscoveryCredential.java b/nearby/presence/np_java_ffi/java/com/google/android/nearby/presence/rust/credential/V0DiscoveryCredential.java
index 522b7d8..f2d1a41 100644
--- a/nearby/presence/np_java_ffi/java/com/google/android/nearby/presence/rust/credential/V0DiscoveryCredential.java
+++ b/nearby/presence/np_java_ffi/java/com/google/android/nearby/presence/rust/credential/V0DiscoveryCredential.java
@@ -19,6 +19,7 @@
import static com.google.android.nearby.presence.rust.credential.Utils.copyBytes;
/** A V0 discovery credential in a format that is ready to be passed to native code. */
+@SuppressWarnings("UnusedVariable")
public final class V0DiscoveryCredential {
private final byte[] keySeed;
private final byte[] identityTokenHmac;
diff --git a/nearby/presence/np_java_ffi/java/com/google/android/nearby/presence/rust/credential/V1BroadcastCredential.java b/nearby/presence/np_java_ffi/java/com/google/android/nearby/presence/rust/credential/V1BroadcastCredential.java
index 5c2547a..d9108a3 100644
--- a/nearby/presence/np_java_ffi/java/com/google/android/nearby/presence/rust/credential/V1BroadcastCredential.java
+++ b/nearby/presence/np_java_ffi/java/com/google/android/nearby/presence/rust/credential/V1BroadcastCredential.java
@@ -19,6 +19,7 @@
import static com.google.android.nearby.presence.rust.credential.Utils.copyBytes;
/** A V1 broadcast credential in a format that is ready to be passed to native code. */
+@SuppressWarnings("UnusedVariable")
public final class V1BroadcastCredential {
private final byte[] keySeed;
private final byte[] identityToken;
diff --git a/nearby/presence/np_java_ffi/java/com/google/android/nearby/presence/rust/credential/V1DiscoveryCredential.java b/nearby/presence/np_java_ffi/java/com/google/android/nearby/presence/rust/credential/V1DiscoveryCredential.java
index 2fbf608..53cfcfe 100644
--- a/nearby/presence/np_java_ffi/java/com/google/android/nearby/presence/rust/credential/V1DiscoveryCredential.java
+++ b/nearby/presence/np_java_ffi/java/com/google/android/nearby/presence/rust/credential/V1DiscoveryCredential.java
@@ -19,6 +19,7 @@
import static com.google.android.nearby.presence.rust.credential.Utils.copyBytes;
/** A V1 discovery credential in a format that is ready to be passed to native code. */
+@SuppressWarnings("UnusedVariable")
public final class V1DiscoveryCredential {
private final byte[] keySeed;
private final byte[] expectedMicShortSaltIdentityTokenHmac;
diff --git a/nearby/presence/np_java_ffi/src/class.rs b/nearby/presence/np_java_ffi/src/class.rs
index ea53df7..d8742cb 100644
--- a/nearby/presence/np_java_ffi/src/class.rs
+++ b/nearby/presence/np_java_ffi/src/class.rs
@@ -47,8 +47,10 @@
mod v0_broadcast_credential;
mod v0_discovery_credential;
mod v0_payload;
+mod v1_advertisement_builder;
mod v1_broadcast_credential;
mod v1_discovery_credential;
+mod verification_mode;
pub mod v0_data_element;
pub mod v1_data_element;
@@ -58,16 +60,18 @@
pub use deserialize_result::{DeserializeResult, DeserializeResultError};
pub use deserialized_v0_advertisement::{DeserializedV0Advertisement, V0AdvertisementError};
pub use deserialized_v1_advertisement::DeserializedV1Advertisement;
-pub use deserialized_v1_section::{DeserializedV1Section, VerificationMode};
+pub use deserialized_v1_section::DeserializedV1Section;
pub use handle::InvalidHandleException;
pub use identity_kind::IdentityKind;
pub use legible_v1_sections::LegibleV1Sections;
pub use owned_handle::NoSpaceLeftException;
pub use serialization_exception::{
- InsufficientSpaceException, InvalidDataElementException, LdtEncryptionException,
- UnencryptedSizeException,
+ InsufficientSpaceException, InvalidDataElementException, InvalidSectionKindException,
+ LdtEncryptionException, UnclosedActiveSectionException, UnencryptedSizeException,
};
pub use v0_broadcast_credential::V0BroadcastCredential;
pub use v0_discovery_credential::V0DiscoveryCredential;
+pub use v1_advertisement_builder::{V1BuilderHandle, V1SectionBuilder};
pub use v1_broadcast_credential::V1BroadcastCredential;
pub use v1_discovery_credential::V1DiscoveryCredential;
+pub use verification_mode::VerificationMode;
diff --git a/nearby/presence/np_java_ffi/src/class/credential_slab.rs b/nearby/presence/np_java_ffi/src/class/credential_slab.rs
index 7e8f37d..d22dea1 100644
--- a/nearby/presence/np_java_ffi/src/class/credential_slab.rs
+++ b/nearby/presence/np_java_ffi/src/class/credential_slab.rs
@@ -86,7 +86,7 @@
let core_cred = credential.get_as_core(&mut env)?;
let match_data = MatchedCredential::from_arc_bytes(
- cred_id as u32,
+ cred_id as i64,
env.convert_byte_array(&encrypted_metadata_bytes)?.into(),
);
@@ -122,7 +122,7 @@
let core_cred = credential.get_as_core(&mut env)?;
let match_data = MatchedCredential::from_arc_bytes(
- cred_id as u32,
+ cred_id as i64,
env.convert_byte_array(&encrypted_metadata_bytes)?.into(),
);
diff --git a/nearby/presence/np_java_ffi/src/class/deserialized_v1_section.rs b/nearby/presence/np_java_ffi/src/class/deserialized_v1_section.rs
index 3b6b5ac..96b514b 100644
--- a/nearby/presence/np_java_ffi/src/class/deserialized_v1_section.rs
+++ b/nearby/presence/np_java_ffi/src/class/deserialized_v1_section.rs
@@ -13,9 +13,8 @@
// limitations under the License.
use jni::{objects::JObject, sys::jint, JNIEnv};
-use np_ffi_core::{deserialize::v1::DeserializedV1IdentityKind, v1::V1VerificationMode};
-use pourover::desc::{ClassDesc, StaticFieldDesc};
-use std::sync::RwLock;
+use np_ffi_core::deserialize::v1::DeserializedV1IdentityKind;
+use pourover::desc::ClassDesc;
use crate::class::{CredentialBook, IdentityKind, LegibleV1Sections};
@@ -51,70 +50,3 @@
.map(Self)
}
}
-
-static VERIFICATION_MODE_CLASS: ClassDesc = ClassDesc::new(
- "com/google/android/nearby/presence/rust/DeserializedV1Section$VerificationMode",
-);
-
-/// Rust representation of `@VerificationMode`. These are `jints` on the Java side, so this type can't
-/// be instantiated.
-pub enum VerificationMode {}
-
-impl VerificationMode {
- /// Convert a Rust verification mode enum to the Java `jint` representation.
- pub fn value_for<'local>(
- env: &mut JNIEnv<'local>,
- mode: V1VerificationMode,
- ) -> jni::errors::Result<jint> {
- match mode {
- V1VerificationMode::Mic => Self::mic(env),
- V1VerificationMode::Signature => Self::signature(env),
- }
- }
-
- /// Fetch the `SIGNATURE` constant
- pub fn signature<'local>(env: &mut JNIEnv<'local>) -> jni::errors::Result<jint> {
- static SIGNATURE: StaticFieldDesc = VERIFICATION_MODE_CLASS.static_field("SIGNATURE", "I");
- static VALUE: RwLock<Option<jint>> = RwLock::new(None);
- Self::lookup_static_value(env, &SIGNATURE, &VALUE)
- }
-
- /// Fetch the `MIC` constant
- pub fn mic<'local>(env: &mut JNIEnv<'local>) -> jni::errors::Result<jint> {
- static MIC: StaticFieldDesc = VERIFICATION_MODE_CLASS.static_field("MIC", "I");
- static VALUE: RwLock<Option<jint>> = RwLock::new(None);
- Self::lookup_static_value(env, &MIC, &VALUE)
- }
-
- /// Look up the given field and cache it in the given cache. The lookup will only be performed
- /// once if successful. This uses `RwLock` instead of `OnceCell` since the fallible `OnceCell`
- /// APIs are nightly only.
- fn lookup_static_value<'local>(
- env: &mut JNIEnv<'local>,
- field: &StaticFieldDesc,
- cache: &RwLock<Option<jint>>,
- ) -> jni::errors::Result<jint> {
- use jni::signature::{JavaType, Primitive};
-
- // Read from cache
- if let Some(value) = *cache.read().unwrap_or_else(|poison| poison.into_inner()) {
- return Ok(value);
- }
-
- // Get exclusive access to the cache for the lookup
- let mut guard = cache.write().unwrap_or_else(|poison| poison.into_inner());
-
- // In case of races, only lookup the value once
- if let Some(value) = *guard {
- return Ok(value);
- }
-
- let value = env
- .get_static_field_unchecked(field.cls(), field, JavaType::Primitive(Primitive::Int))
- .and_then(|ret| ret.i())?;
-
- *guard = Some(value);
-
- Ok(value)
- }
-}
diff --git a/nearby/presence/np_java_ffi/src/class/legible_v1_sections.rs b/nearby/presence/np_java_ffi/src/class/legible_v1_sections.rs
index 6bc9225..0e089ab 100644
--- a/nearby/presence/np_java_ffi/src/class/legible_v1_sections.rs
+++ b/nearby/presence/np_java_ffi/src/class/legible_v1_sections.rs
@@ -102,10 +102,10 @@
pub fn construct_from_parts(
env: &mut JNIEnv<'local>,
verification_mode: V1VerificationMode,
- credential_id: u32,
+ credential_id: i64,
identity_token: [u8; 16],
) -> jni::errors::Result<Self> {
- let verification_mode = VerificationMode::value_for(env, verification_mode)?;
+ let verification_mode = VerificationMode::from(verification_mode).to_java(env)?;
let credential_id = credential_id as jint;
let identity_token = env.byte_array_from_slice(&identity_token)?;
diff --git a/nearby/presence/np_java_ffi/src/class/serialization_exception.rs b/nearby/presence/np_java_ffi/src/class/serialization_exception.rs
index 3e3235e..e021927 100644
--- a/nearby/presence/np_java_ffi/src/class/serialization_exception.rs
+++ b/nearby/presence/np_java_ffi/src/class/serialization_exception.rs
@@ -57,83 +57,52 @@
}
}
-static INSUFFICIENT_SPACE_EXCEPTION: ClassDesc = ClassDesc::new(
- "com/google/android/nearby/presence/rust/SerializationException$InsufficientSpaceException",
-);
+/// Helper to generate wrapper types for exception classes with no-arg constructors
+macro_rules! exception_wrapper {
+ ($(#[$docs:meta])* $name:ident, $cls:literal) => {
+ $(#[$docs])*
+ #[repr(transparent)]
+ pub struct $name<Obj>(pub Obj);
-/// Rust representation of `class SerializationException.InsufficientSpaceException`.
-#[repr(transparent)]
-pub struct InsufficientSpaceException<Obj>(pub Obj);
+ impl<'local> $name<JObject<'local>> {
+ /// Create a new instance.
+ pub fn construct(env: &mut JNIEnv<'local>) -> jni::errors::Result<Self> {
+ static CLS: ClassDesc = ClassDesc::new($cls);
+ pourover::call_constructor!(env, &CLS, "()V",).map(Self)
+ }
-impl<'local> InsufficientSpaceException<JObject<'local>> {
- /// Create a new instance.
- pub fn construct(env: &mut JNIEnv<'local>) -> jni::errors::Result<Self> {
- pourover::call_constructor!(env, &INSUFFICIENT_SPACE_EXCEPTION, "()V",).map(Self)
- }
+ /// Create a new instance and throw it.
+ pub fn throw_new(env: &mut JNIEnv<'local>) -> jni::errors::Result<()> {
+ Self::construct(env)?.throw(env)
+ }
+ }
- /// Create a new instance and throw it.
- pub fn throw_new(env: &mut JNIEnv<'local>) -> jni::errors::Result<()> {
- Self::construct(env)?.throw(env)
+ impl<'local, Obj: AsRef<JObject<'local>>> $name<Obj> {
+ /// Throw this exception.
+ pub fn throw<'env>(&self, env: &mut JNIEnv<'env>) -> jni::errors::Result<()> {
+ env.throw(<&JThrowable>::from(self.0.as_ref()))
+ }
+ }
+
}
}
-impl<'local, Obj: AsRef<JObject<'local>>> InsufficientSpaceException<Obj> {
- /// Throw this exception.
- pub fn throw<'env>(&self, env: &mut JNIEnv<'env>) -> jni::errors::Result<()> {
- env.throw(<&JThrowable>::from(self.0.as_ref()))
- }
-}
+exception_wrapper!(
+ /// Rust representation of `class SerializationException.UnclosedActiveSectionException`.
+ UnclosedActiveSectionException, "com/google/android/nearby/presence/rust/SerializationException$UnclosedActiveSectionException");
-static LDT_ENCRYPTION_EXCEPTION_CLASS: ClassDesc = ClassDesc::new(
- "com/google/android/nearby/presence/rust/SerializationException$LdtEncryptionException",
-);
+exception_wrapper!(
+ /// Rust representation of `class SerializationException.InvalidSectionKindException`.
+ InvalidSectionKindException, "com/google/android/nearby/presence/rust/SerializationException$InvalidSectionKindException");
-/// Rust representation of `class SerializationException.LdtEncryptionException`.
-#[repr(transparent)]
-pub struct LdtEncryptionException<Obj>(pub Obj);
+exception_wrapper!(
+ /// Rust representation of `class SerializationException.InsufficientSpaceException`.
+ InsufficientSpaceException, "com/google/android/nearby/presence/rust/SerializationException$InsufficientSpaceException");
-impl<'local> LdtEncryptionException<JObject<'local>> {
- /// Create a new instance.
- pub fn construct(env: &mut JNIEnv<'local>) -> jni::errors::Result<Self> {
- pourover::call_constructor!(env, &LDT_ENCRYPTION_EXCEPTION_CLASS, "()V").map(Self)
- }
+exception_wrapper!(
+ /// Rust representation of `class SerializationException.LdtEncryptionException`.
+ LdtEncryptionException, "com/google/android/nearby/presence/rust/SerializationException$LdtEncryptionException");
- /// Create a new instance and throw it.
- pub fn throw_new(env: &mut JNIEnv<'local>) -> jni::errors::Result<()> {
- Self::construct(env)?.throw(env)
- }
-}
-
-impl<'local, Obj: AsRef<JObject<'local>>> LdtEncryptionException<Obj> {
- /// Throw this exception.
- pub fn throw<'env>(&self, env: &mut JNIEnv<'env>) -> jni::errors::Result<()> {
- env.throw(<&JThrowable>::from(self.0.as_ref()))
- }
-}
-
-static UNENCRYPTED_SIZE_EXCEPTION_CLASS: ClassDesc = ClassDesc::new(
- "com/google/android/nearby/presence/rust/SerializationException$UnencryptedSizeException",
-);
-
-/// Rust representation of `class SerializationException.UnencryptedSizeException`.
-#[repr(transparent)]
-pub struct UnencryptedSizeException<Obj>(pub Obj);
-
-impl<'local> UnencryptedSizeException<JObject<'local>> {
- /// Create a new instance.
- pub fn construct(env: &mut JNIEnv<'local>) -> jni::errors::Result<Self> {
- pourover::call_constructor!(env, &UNENCRYPTED_SIZE_EXCEPTION_CLASS, "()V").map(Self)
- }
-
- /// Create a new instance and throw it.
- pub fn throw_new(env: &mut JNIEnv<'local>) -> jni::errors::Result<()> {
- Self::construct(env)?.throw(env)
- }
-}
-
-impl<'local, Obj: AsRef<JObject<'local>>> UnencryptedSizeException<Obj> {
- /// Throw this exception.
- pub fn throw<'env>(&self, env: &mut JNIEnv<'env>) -> jni::errors::Result<()> {
- env.throw(<&JThrowable>::from(self.0.as_ref()))
- }
-}
+exception_wrapper!(
+ /// Rust representation of `class SerializationException.UnencryptedSizeException`.
+ UnencryptedSizeException, "com/google/android/nearby/presence/rust/SerializationException$UnencryptedSizeException");
diff --git a/nearby/presence/np_java_ffi/src/class/v0_advertisement_builder.rs b/nearby/presence/np_java_ffi/src/class/v0_advertisement_builder.rs
index 1ba39b6..ccb3a9e 100644
--- a/nearby/presence/np_java_ffi/src/class/v0_advertisement_builder.rs
+++ b/nearby/presence/np_java_ffi/src/class/v0_advertisement_builder.rs
@@ -71,8 +71,13 @@
data_element: V0DataElement,
) -> jni::errors::Result<()> {
let builder = self.as_rust_handle(env)?;
- #[allow(clippy::expect_used)]
- let res = builder.add_de(data_element).expect("valid data structure (created in Rust)");
+ let Ok(res) = builder.add_de(data_element) else {
+ let _ = env.throw_new(
+ "java/lang/IllegalStateException",
+ "V0Actions is not validly constructed",
+ );
+ return Err(jni::errors::Error::JavaException);
+ };
match res {
AddV0DEResult::Success => {}
diff --git a/nearby/presence/np_java_ffi/src/class/v0_data_element.rs b/nearby/presence/np_java_ffi/src/class/v0_data_element.rs
index 44f0636..4d05a6a 100644
--- a/nearby/presence/np_java_ffi/src/class/v0_data_element.rs
+++ b/nearby/presence/np_java_ffi/src/class/v0_data_element.rs
@@ -283,5 +283,5 @@
};
}
- 0
+ v0_actions.as_u32() as jint
}
diff --git a/nearby/presence/np_java_ffi/src/class/v0_payload.rs b/nearby/presence/np_java_ffi/src/class/v0_payload.rs
index e962f5e..c54de0d 100644
--- a/nearby/presence/np_java_ffi/src/class/v0_payload.rs
+++ b/nearby/presence/np_java_ffi/src/class/v0_payload.rs
@@ -43,7 +43,7 @@
/// Create an IdentityDetails instance
pub fn construct_from_parts(
env: &mut JNIEnv<'local>,
- credential_id: u32,
+ credential_id: i64,
identity_token: [u8; 14],
salt: [u8; 2],
) -> jni::errors::Result<Self> {
diff --git a/nearby/presence/np_java_ffi/src/class/v1_advertisement_builder.rs b/nearby/presence/np_java_ffi/src/class/v1_advertisement_builder.rs
new file mode 100644
index 0000000..ba1a3ed
--- /dev/null
+++ b/nearby/presence/np_java_ffi/src/class/v1_advertisement_builder.rs
@@ -0,0 +1,354 @@
+// Copyright 2024 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 handle_map::{Handle, HandleLike};
+use jni::{
+ objects::{JClass, JObject},
+ sys::{jboolean, jint, jlong, JNI_TRUE},
+ JNIEnv,
+};
+use np_ffi_core::{
+ serialize::v1::{
+ self, create_v1_advertisement_builder, AddV1DEResult, AddV1SectionToAdvertisementResult,
+ CreateV1AdvertisementBuilderResult, CreateV1SectionBuilderResult,
+ SerializeV1AdvertisementResult, V1AdvertisementBuilder,
+ },
+ serialize::AdvertisementBuilderKind,
+};
+use pourover::{desc::ClassDesc, jni_method};
+
+use crate::class::{
+ v1_data_element::Generic, InsufficientSpaceException, InvalidDataElementException,
+ InvalidHandleException, InvalidSectionKindException, NoSpaceLeftException,
+ UnclosedActiveSectionException, V1BroadcastCredential, VerificationMode,
+};
+
+static V1_BUILDER_HANDLE_CLASS: ClassDesc = ClassDesc::new(
+ "com/google/android/nearby/presence/rust/V1AdvertisementBuilder$V1BuilderHandle",
+);
+
+/// Rust representation of `class V1AdvertisementBuilder.V1BuilderHandle`.
+#[repr(transparent)]
+pub struct V1BuilderHandle<Obj>(pub Obj);
+
+impl<'local, Obj: AsRef<JObject<'local>>> V1BuilderHandle<Obj> {
+ /// Get a reference to the inner `jni` crate [`JObject`].
+ pub fn as_obj(&self) -> &JObject<'local> {
+ self.0.as_ref()
+ }
+
+ /// Get the Rust [`HandleLike`] representation from this Java object.
+ pub fn as_rust_handle<'env>(
+ &self,
+ env: &mut JNIEnv<'env>,
+ ) -> jni::errors::Result<V1AdvertisementBuilder> {
+ let handle_id = self.get_handle_id(env)?;
+ Ok(V1AdvertisementBuilder::from_handle(Handle::from_id(handle_id as u64)))
+ }
+
+ /// Get `long handleId` from the Java object
+ fn get_handle_id<'env_local>(
+ &self,
+ env: &mut JNIEnv<'env_local>,
+ ) -> jni::errors::Result<jlong> {
+ use V1_BUILDER_HANDLE_CLASS as CLS;
+ pourover::call_method!(env, &CLS, "getId", "()J", self.as_obj())
+ }
+}
+
+static V1_SECTION_BUILDER: ClassDesc =
+ ClassDesc::new("com/google/android/nearby/presence/rust/V1SectionBuilder");
+
+/// Rust representation of `class V1SectionBuilder`.
+#[repr(transparent)]
+pub struct V1SectionBuilder<Obj>(pub Obj);
+
+impl<'local> V1SectionBuilder<JObject<'local>> {
+ /// Create a new V1SectionBuilder instance.
+ pub fn construct<'h>(
+ env: &mut JNIEnv<'local>,
+ handle: V1BuilderHandle<impl AsRef<JObject<'h>>>,
+ section: jint,
+ ) -> jni::errors::Result<Self> {
+ pourover::call_constructor!(
+ env,
+ &V1_SECTION_BUILDER,
+ "(Lcom/google/android/nearby/presence/rust/V1AdvertisementBuilder$V1BuilderHandle;I)V",
+ handle.as_obj(),
+ section,
+ )
+ .map(Self)
+ }
+}
+
+#[jni_method(
+ package = "com.google.android.nearby.presence.rust",
+ class = "V1AdvertisementBuilder.V1BuilderHandle"
+)]
+extern "system" fn allocate<'local>(
+ mut env: JNIEnv<'local>,
+ _cls: JClass<'local>,
+ encrypted: jboolean,
+) -> jlong {
+ let kind = if encrypted == JNI_TRUE {
+ AdvertisementBuilderKind::Encrypted
+ } else {
+ AdvertisementBuilderKind::Public
+ };
+ match create_v1_advertisement_builder(kind) {
+ CreateV1AdvertisementBuilderResult::Success(builder) => {
+ builder.get_as_handle().get_id() as jlong
+ }
+ CreateV1AdvertisementBuilderResult::NoSpaceLeft => {
+ let _ = NoSpaceLeftException::throw_new(&mut env);
+ 0
+ }
+ }
+}
+
+#[jni_method(
+ package = "com.google.android.nearby.presence.rust",
+ class = "V1AdvertisementBuilder.V1BuilderHandle"
+)]
+extern "system" fn nativeAddPublicSection<'local>(
+ mut env: JNIEnv<'local>,
+ this: V1BuilderHandle<JObject<'local>>,
+) -> JObject<'local> {
+ let Ok(handle) = this.as_rust_handle(&mut env) else {
+ return JObject::null();
+ };
+
+ let builder = match handle.public_section_builder() {
+ CreateV1SectionBuilderResult::Success(builder) => builder,
+ CreateV1SectionBuilderResult::UnclosedActiveSection => {
+ let _ = UnclosedActiveSectionException::throw_new(&mut env);
+ return JObject::null();
+ }
+ CreateV1SectionBuilderResult::InvalidAdvertisementBuilderHandle => {
+ let _ = InvalidHandleException::throw_new(&mut env);
+ return JObject::null();
+ }
+ CreateV1SectionBuilderResult::IdentityKindMismatch => {
+ let _ = InvalidSectionKindException::throw_new(&mut env);
+ return JObject::null();
+ }
+ CreateV1SectionBuilderResult::NoSpaceLeft => {
+ let _ = InsufficientSpaceException::throw_new(&mut env);
+ return JObject::null();
+ }
+ };
+
+ assert_eq!(
+ handle.get_as_handle().get_id(),
+ builder.adv_builder.get_as_handle().get_id(),
+ "Section builder must be the same handle so that we can reuse the Java handle object below"
+ );
+
+ let Ok(java_builder) =
+ V1SectionBuilder::construct(&mut env, this, builder.section_index.into())
+ else {
+ return JObject::null();
+ };
+
+ java_builder.0
+}
+
+#[jni_method(
+ package = "com.google.android.nearby.presence.rust",
+ class = "V1AdvertisementBuilder.V1BuilderHandle"
+)]
+extern "system" fn nativeAddEncryptedSection<'local>(
+ mut env: JNIEnv<'local>,
+ this: V1BuilderHandle<JObject<'local>>,
+ credential: V1BroadcastCredential<JObject<'local>>,
+ verification_mode: jint,
+) -> JObject<'local> {
+ let Ok(handle) = this.as_rust_handle(&mut env) else {
+ return JObject::null();
+ };
+
+ let Ok(credential) = credential.get_as_core(&mut env) else {
+ return JObject::null();
+ };
+
+ let verification_mode = match VerificationMode::from_java(&mut env, verification_mode) {
+ Ok(Some(verification_mode)) => verification_mode.into(),
+ Ok(None) => {
+ let _ = env
+ .throw_new("java/lang/IllegalArgumentException", "Invalid @VerificationMode value");
+ return JObject::null();
+ }
+ Err(_) => return JObject::null(),
+ };
+
+ let builder = match handle.encrypted_section_builder(credential, verification_mode) {
+ CreateV1SectionBuilderResult::Success(builder) => builder,
+ CreateV1SectionBuilderResult::UnclosedActiveSection => {
+ let _ = UnclosedActiveSectionException::throw_new(&mut env);
+ return JObject::null();
+ }
+ CreateV1SectionBuilderResult::InvalidAdvertisementBuilderHandle => {
+ let _ = InvalidHandleException::throw_new(&mut env);
+ return JObject::null();
+ }
+ CreateV1SectionBuilderResult::IdentityKindMismatch => {
+ let _ = InvalidSectionKindException::throw_new(&mut env);
+ return JObject::null();
+ }
+ CreateV1SectionBuilderResult::NoSpaceLeft => {
+ let _ = InsufficientSpaceException::throw_new(&mut env);
+ return JObject::null();
+ }
+ };
+
+ assert_eq!(
+ handle.get_as_handle().get_id(),
+ builder.adv_builder.get_as_handle().get_id(),
+ "Section builder must be the same handle so that we can reuse the Java handle object below"
+ );
+
+ let Ok(java_builder) =
+ V1SectionBuilder::construct(&mut env, this, builder.section_index.into())
+ else {
+ return JObject::null();
+ };
+
+ java_builder.0
+}
+
+#[jni_method(
+ package = "com.google.android.nearby.presence.rust",
+ class = "V1AdvertisementBuilder.V1BuilderHandle"
+)]
+extern "system" fn nativeAddGenericDataElement<'local>(
+ mut env: JNIEnv<'local>,
+ this: V1BuilderHandle<JObject<'local>>,
+ section_index: jint,
+ generic_de: Generic<JObject<'local>>,
+) {
+ let Ok(adv_builder) = this.as_rust_handle(&mut env) else {
+ return;
+ };
+
+ let Ok(section_index) = u8::try_from(section_index) else {
+ return;
+ };
+
+ let section_builder = v1::V1SectionBuilder { adv_builder, section_index };
+
+ let de = match generic_de.as_core_byte_buffer_de(&mut env) {
+ Ok(Some(de)) => de,
+ Ok(None) => {
+ let _ = env
+ .new_string("Data is too long")
+ .map(|s| env.auto_local(s))
+ .and_then(|reason| InvalidDataElementException::throw_new(&mut env, &reason));
+ return;
+ }
+ Err(_jni_err) => {
+ return;
+ }
+ };
+
+ match section_builder.add_127_byte_buffer_de(de) {
+ AddV1DEResult::Success => {}
+ AddV1DEResult::InvalidSectionHandle => {
+ let _ = InvalidHandleException::throw_new(&mut env);
+ }
+ AddV1DEResult::InsufficientSectionSpace => {
+ let _ = InsufficientSpaceException::throw_new(&mut env);
+ }
+ AddV1DEResult::InvalidDataElement => {
+ let _ = env
+ .new_string("Invalid generic data element")
+ .map(|string| env.auto_local(string))
+ .and_then(|reason| InvalidDataElementException::throw_new(&mut env, &reason));
+ }
+ }
+}
+
+#[jni_method(
+ package = "com.google.android.nearby.presence.rust",
+ class = "V1AdvertisementBuilder.V1BuilderHandle"
+)]
+extern "system" fn nativeFinishSection<'local>(
+ mut env: JNIEnv<'local>,
+ this: V1BuilderHandle<JObject<'local>>,
+ section_index: jint,
+) {
+ let Ok(adv_builder) = this.as_rust_handle(&mut env) else {
+ return;
+ };
+
+ let Ok(section_index) = u8::try_from(section_index) else {
+ return;
+ };
+
+ let section_builder = v1::V1SectionBuilder { adv_builder, section_index };
+
+ match section_builder.add_to_advertisement() {
+ AddV1SectionToAdvertisementResult::Success => {
+ // It worked.
+ }
+ AddV1SectionToAdvertisementResult::Error => {
+ // This case covers:
+ // * The handle is invalid
+ // * The handle is in an incorrect state (no open section)
+ // * The section builder is not for the currently opened section
+ let _ = InvalidHandleException::throw_new(&mut env);
+ }
+ }
+}
+
+#[jni_method(
+ package = "com.google.android.nearby.presence.rust",
+ class = "V1AdvertisementBuilder.V1BuilderHandle"
+)]
+extern "system" fn nativeBuild<'local>(
+ mut env: JNIEnv<'local>,
+ this: V1BuilderHandle<JObject<'local>>,
+) -> JObject<'local> {
+ let Ok(handle) = this.as_rust_handle(&mut env) else {
+ return JObject::null();
+ };
+
+ match handle.into_advertisement() {
+ SerializeV1AdvertisementResult::Success(bytes) => {
+ #[allow(clippy::expect_used)]
+ let adv_bytes = bytes.as_slice().expect("should never be malformed from core");
+ env.byte_array_from_slice(adv_bytes).map_or(JObject::null(), JObject::from)
+ }
+ SerializeV1AdvertisementResult::InvalidBuilderState => {
+ let _ = UnclosedActiveSectionException::throw_new(&mut env);
+ JObject::null()
+ }
+ SerializeV1AdvertisementResult::InvalidAdvertisementBuilderHandle => {
+ let _ = InvalidHandleException::throw_new(&mut env);
+ JObject::null()
+ }
+ }
+}
+
+#[jni_method(
+ package = "com.google.android.nearby.presence.rust",
+ class = "V1AdvertisementBuilder.V1BuilderHandle"
+)]
+extern "system" fn deallocate<'local>(
+ _env: JNIEnv<'local>,
+ _cls: JClass<'local>,
+ handle_id: jlong,
+) {
+ let handle = V1AdvertisementBuilder::from_handle(Handle::from_id(handle_id as u64));
+ let _ = handle.deallocate();
+}
diff --git a/nearby/presence/np_java_ffi/src/class/v1_data_element.rs b/nearby/presence/np_java_ffi/src/class/v1_data_element.rs
index 6401165..c404502 100644
--- a/nearby/presence/np_java_ffi/src/class/v1_data_element.rs
+++ b/nearby/presence/np_java_ffi/src/class/v1_data_element.rs
@@ -14,11 +14,13 @@
//! Data Elements for v1 advertisements. See `class V1DataElement`.
+use array_view::ArrayView;
use jni::{
objects::{JByteArray, JObject},
sys::jlong,
JNIEnv,
};
+use np_ffi_core::{common::ByteBuffer, serialize::v1::V1DE127ByteBuffer};
use pourover::desc::ClassDesc;
static GENERIC_CLASS: ClassDesc =
@@ -48,4 +50,48 @@
) -> jni::errors::Result<Option<Self>> {
Ok(env.is_instance_of(obj.as_ref(), &GENERIC_CLASS)?.then(|| Self(obj)))
}
+
+ /// Get a reference to the inner `jni` crate [`JObject`].
+ pub fn as_obj(&self) -> &JObject<'local> {
+ self.0.as_ref()
+ }
+
+ /// Get the data element's type
+ pub fn get_type<'env>(&self, env: &mut JNIEnv<'env>) -> jni::errors::Result<jlong> {
+ pourover::call_method!(env, &GENERIC_CLASS, "getType", "()J", self.as_obj())
+ }
+
+ /// Get the data element's data. Returns `None` if the data is too long.
+ pub fn get_data<'env>(
+ &self,
+ env: &mut JNIEnv<'env>,
+ ) -> jni::errors::Result<Option<ByteBuffer<127>>> {
+ let data = pourover::call_method!(env, &GENERIC_CLASS, "getData", "()[B", self.as_obj())?;
+ let len = env.get_array_length(&data)? as usize;
+
+ if len > 127 {
+ return Ok(None);
+ }
+
+ // Length is validated above
+ #[allow(clippy::unwrap_used)]
+ {
+ let mut buffer = [0; 127];
+ env.get_byte_array_region(&data, 0, buffer.get_mut(..len).unwrap())?;
+ let buffer = buffer.map(|b| b as u8);
+ Ok(Some(ByteBuffer::from_array_view(ArrayView::try_from_array(buffer, len).unwrap())))
+ }
+ }
+
+ /// Get the data element as a `np_ffi_core` byte buffer. Returns `None` if the data element is
+ /// not valid.
+ pub fn as_core_byte_buffer_de<'env>(
+ &self,
+ env: &mut JNIEnv<'env>,
+ ) -> jni::errors::Result<Option<V1DE127ByteBuffer>> {
+ let Some(payload) = self.get_data(env)? else {
+ return Ok(None);
+ };
+ Ok(Some(V1DE127ByteBuffer { de_type: self.get_type(env)? as u32, payload }))
+ }
}
diff --git a/nearby/presence/np_java_ffi/src/class/verification_mode.rs b/nearby/presence/np_java_ffi/src/class/verification_mode.rs
new file mode 100644
index 0000000..f217207
--- /dev/null
+++ b/nearby/presence/np_java_ffi/src/class/verification_mode.rs
@@ -0,0 +1,119 @@
+// Copyright 2024 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 jni::{sys::jint, JNIEnv};
+use np_ffi_core::v1::V1VerificationMode;
+use pourover::desc::{ClassDesc, StaticFieldDesc};
+use std::sync::RwLock;
+
+static VERIFICATION_MODE_CLASS: ClassDesc =
+ ClassDesc::new("com/google/android/nearby/presence/rust/VerificationMode");
+
+/// Rust representation of `@VerificationMode`.
+#[derive(Copy, Clone, PartialEq, Eq)]
+pub enum VerificationMode {
+ /// Verification is done using the Mic scheme.
+ Mic,
+ /// Verification is done using the Signature scheme.
+ Signature,
+}
+
+impl VerificationMode {
+ /// Convert a Java `int` to the Rust version. Will return `None` if the given `value` is not
+ /// valid.
+ pub fn from_java<'local>(
+ env: &mut JNIEnv<'local>,
+ value: jint,
+ ) -> jni::errors::Result<Option<Self>> {
+ if value == Self::mic(env)? {
+ Ok(Some(Self::Mic))
+ } else if value == Self::signature(env)? {
+ Ok(Some(Self::Signature))
+ } else {
+ Ok(None)
+ }
+ }
+
+ /// Convert to a Java `int` value.
+ pub fn to_java<'local>(&self, env: &mut JNIEnv<'local>) -> jni::errors::Result<jint> {
+ match self {
+ Self::Mic => Self::mic(env),
+ Self::Signature => Self::signature(env),
+ }
+ }
+
+ /// Fetch the `SIGNATURE` constant
+ fn signature<'local>(env: &mut JNIEnv<'local>) -> jni::errors::Result<jint> {
+ static SIGNATURE: StaticFieldDesc = VERIFICATION_MODE_CLASS.static_field("SIGNATURE", "I");
+ static VALUE: RwLock<Option<jint>> = RwLock::new(None);
+ Self::lookup_static_value(env, &SIGNATURE, &VALUE)
+ }
+
+ /// Fetch the `MIC` constant
+ fn mic<'local>(env: &mut JNIEnv<'local>) -> jni::errors::Result<jint> {
+ static MIC: StaticFieldDesc = VERIFICATION_MODE_CLASS.static_field("MIC", "I");
+ static VALUE: RwLock<Option<jint>> = RwLock::new(None);
+ Self::lookup_static_value(env, &MIC, &VALUE)
+ }
+
+ /// Look up the given field and cache it in the given cache. The lookup will only be performed
+ /// once if successful. This uses `RwLock` instead of `OnceCell` since the fallible `OnceCell`
+ /// APIs are nightly only.
+ fn lookup_static_value<'local>(
+ env: &mut JNIEnv<'local>,
+ field: &StaticFieldDesc,
+ cache: &RwLock<Option<jint>>,
+ ) -> jni::errors::Result<jint> {
+ use jni::signature::{JavaType, Primitive};
+
+ // Read from cache
+ if let Some(value) = *cache.read().unwrap_or_else(|poison| poison.into_inner()) {
+ return Ok(value);
+ }
+
+ // Get exclusive access to the cache for the lookup
+ let mut guard = cache.write().unwrap_or_else(|poison| poison.into_inner());
+
+ // In case of races, only lookup the value once
+ if let Some(value) = *guard {
+ return Ok(value);
+ }
+
+ let value = env
+ .get_static_field_unchecked(field.cls(), field, JavaType::Primitive(Primitive::Int))
+ .and_then(|ret| ret.i())?;
+
+ *guard = Some(value);
+
+ Ok(value)
+ }
+}
+
+impl From<V1VerificationMode> for VerificationMode {
+ fn from(mode: V1VerificationMode) -> Self {
+ match mode {
+ V1VerificationMode::Mic => Self::Mic,
+ V1VerificationMode::Signature => Self::Signature,
+ }
+ }
+}
+
+impl From<VerificationMode> for V1VerificationMode {
+ fn from(mode: VerificationMode) -> Self {
+ match mode {
+ VerificationMode::Mic => Self::Mic,
+ VerificationMode::Signature => Self::Signature,
+ }
+ }
+}
diff --git a/nearby/presence/np_java_ffi/src/lib.rs b/nearby/presence/np_java_ffi/src/lib.rs
index 7a688e8..bd03c81 100644
--- a/nearby/presence/np_java_ffi/src/lib.rs
+++ b/nearby/presence/np_java_ffi/src/lib.rs
@@ -19,3 +19,5 @@
#![allow(clippy::needless_lifetimes)]
pub mod class;
+#[cfg(feature = "testing")]
+pub mod testing;
diff --git a/common/derive_fuzztest/fuzz/src/bin/integer_add.rs b/nearby/presence/np_java_ffi/src/testing.rs
similarity index 74%
rename from common/derive_fuzztest/fuzz/src/bin/integer_add.rs
rename to nearby/presence/np_java_ffi/src/testing.rs
index 38b8172..5717c7c 100644
--- a/common/derive_fuzztest/fuzz/src/bin/integer_add.rs
+++ b/nearby/presence/np_java_ffi/src/testing.rs
@@ -12,12 +12,6 @@
// See the License for the specific language governing permissions and
// limitations under the License.
-#![cfg_attr(fuzzing, no_main)]
+//! Java bindings for test-only APIs
-use derive_fuzztest::fuzztest;
-
-#[fuzztest]
-pub fn test(a: u8, b: u8) {
- let _ = a.checked_add(b);
- // a + b; // This fails because a + b can overflow.
-}
+mod test_vectors;
diff --git a/nearby/presence/np_java_ffi/src/testing/test_vectors.rs b/nearby/presence/np_java_ffi/src/testing/test_vectors.rs
new file mode 100644
index 0000000..f2d3079
--- /dev/null
+++ b/nearby/presence/np_java_ffi/src/testing/test_vectors.rs
@@ -0,0 +1,103 @@
+// Copyright 2024 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 jni::{
+ objects::{JByteArray, JClass, JObject},
+ JNIEnv,
+};
+use np_adv::extended::salt::{ExtendedV1Salt, MultiSalt, ShortV1Salt};
+use np_ffi_core::serialize::v1::CreateV1SectionBuilderResult;
+use pourover::jni_method;
+
+use crate::class::{
+ InsufficientSpaceException, InvalidHandleException, InvalidSectionKindException,
+ UnclosedActiveSectionException, V1BroadcastCredential, V1BuilderHandle, V1SectionBuilder,
+};
+
+#[jni_method(package = "com.google.android.nearby.presence.rust", class = "TestVectors")]
+extern "system" fn nativeAddSaltedSection<'local>(
+ mut env: JNIEnv<'local>,
+ _cls: JClass<'local>,
+ j_handle: V1BuilderHandle<JObject<'local>>,
+ credential: V1BroadcastCredential<JObject<'local>>,
+ salt: JByteArray<'local>,
+) -> JObject<'local> {
+ let Ok(handle) = j_handle.as_rust_handle(&mut env) else {
+ return JObject::null();
+ };
+
+ let Ok(credential) = credential.get_as_core(&mut env) else {
+ return JObject::null();
+ };
+
+ let salt = {
+ match env.get_array_length(&salt) {
+ Ok(2) => {
+ let mut buf = [0; 2];
+ let Ok(_) = env.get_byte_array_region(&salt, 0, &mut buf[..]) else {
+ return JObject::null();
+ };
+ let buf = buf.map(|b| b as u8);
+ MultiSalt::Short(ShortV1Salt::from(buf))
+ }
+ Ok(16) => {
+ let mut buf = [0; 16];
+ let Ok(_) = env.get_byte_array_region(&salt, 0, &mut buf[..]) else {
+ return JObject::null();
+ };
+ let buf = buf.map(|b| b as u8);
+ MultiSalt::Extended(ExtendedV1Salt::from(buf))
+ }
+ Ok(_) => {
+ let _ = env.throw_new("java/lang/RuntimeException", "Invalid salt length");
+ return JObject::null();
+ }
+ Err(_jni_err) => return JObject::null(),
+ }
+ };
+
+ let builder = match handle.mic_custom_salt_section_builder(credential, salt) {
+ CreateV1SectionBuilderResult::Success(builder) => builder,
+ CreateV1SectionBuilderResult::UnclosedActiveSection => {
+ let _ = UnclosedActiveSectionException::throw_new(&mut env);
+ return JObject::null();
+ }
+ CreateV1SectionBuilderResult::InvalidAdvertisementBuilderHandle => {
+ let _ = InvalidHandleException::throw_new(&mut env);
+ return JObject::null();
+ }
+ CreateV1SectionBuilderResult::IdentityKindMismatch => {
+ let _ = InvalidSectionKindException::throw_new(&mut env);
+ return JObject::null();
+ }
+ CreateV1SectionBuilderResult::NoSpaceLeft => {
+ let _ = InsufficientSpaceException::throw_new(&mut env);
+ return JObject::null();
+ }
+ };
+
+ assert_eq!(
+ handle.get_as_handle().get_id(),
+ builder.adv_builder.get_as_handle().get_id(),
+ "Section builder must be the same handle so that we can reuse the Java handle object below"
+ );
+
+ let Ok(java_builder) =
+ V1SectionBuilder::construct(&mut env, j_handle, builder.section_index.into())
+ else {
+ return JObject::null();
+ };
+
+ java_builder.0
+}
diff --git a/nearby/presence/np_java_ffi/test/com/google/android/nearby/presence/rust/DecryptTests.java b/nearby/presence/np_java_ffi/test/com/google/android/nearby/presence/rust/DecryptTests.java
index f9a7ede..d951cbf 100644
--- a/nearby/presence/np_java_ffi/test/com/google/android/nearby/presence/rust/DecryptTests.java
+++ b/nearby/presence/np_java_ffi/test/com/google/android/nearby/presence/rust/DecryptTests.java
@@ -16,12 +16,22 @@
package com.google.android.nearby.presence.rust;
-import static com.google.android.nearby.presence.rust.TestData.*;
+import static com.google.android.nearby.presence.rust.TestData.ALICE_METADATA;
+import static com.google.android.nearby.presence.rust.TestData.V0_CRED;
+import static com.google.android.nearby.presence.rust.TestData.V0_ENCRYPTED_ALICE_METADATA;
+import static com.google.android.nearby.presence.rust.TestData.V0_PRIVATE;
+import static com.google.android.nearby.presence.rust.TestData.V1_ALICE_METADATA;
+import static com.google.android.nearby.presence.rust.TestData.V1_CRED;
+import static com.google.android.nearby.presence.rust.TestData.V1_ENCRYPTED_ALICE_METADATA;
+import static com.google.android.nearby.presence.rust.TestData.V1_PRIVATE;
import static com.google.common.truth.Truth.assertThat;
import com.google.android.nearby.presence.rust.credential.CredentialBook;
-import org.junit.jupiter.api.Test;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.JUnit4;
+@RunWith(JUnit4.class)
public class DecryptTests {
public static final class TestMetadata implements CredentialBook.MatchedMetadata {
@@ -55,11 +65,11 @@
}
@Test
- void deserializeAdvertisement_v0_canParsePrivate() {
- try (DeserializeResult result = parsePrivateAdv(V0_PRIVATE)) {
+ public void deserializeAdvertisement_v0_canParsePrivate() {
+ try (DeserializeResult<TestMetadata> result = parsePrivateAdv(V0_PRIVATE)) {
assertThat(result.getKind()).isEqualTo(DeserializeResult.Kind.V0_ADVERTISEMENT);
- DeserializedV0Advertisement adv = result.getAsV0();
+ DeserializedV0Advertisement<TestMetadata> adv = result.getAsV0();
assertThat(adv).isNotNull();
assertThat(adv.isLegible()).isTrue();
@@ -70,20 +80,20 @@
}
@Test
- void deserializeAdvertisement_v1_canParsePrivate() {
- try (DeserializeResult result = parsePrivateAdv(V1_PRIVATE)) {
+ public void deserializeAdvertisement_v1_canParsePrivate() {
+ try (DeserializeResult<TestMetadata> result = parsePrivateAdv(V1_PRIVATE)) {
assertThat(result.getKind()).isEqualTo(DeserializeResult.Kind.V1_ADVERTISEMENT);
- DeserializedV1Advertisement adv = result.getAsV1();
+ DeserializedV1Advertisement<TestMetadata> adv = result.getAsV1();
assertThat(adv).isNotNull();
assertThat(adv.getNumLegibleSections()).isEqualTo(1);
assertThat(adv.getNumUndecryptableSections()).isEqualTo(0);
- DeserializedV1Section section = adv.getSection(0);
+ DeserializedV1Section<TestMetadata> section = adv.getSection(0);
assertThat(section.getIdentityKind()).isEqualTo(IdentityKind.DECRYPTED);
assertThat(section.getMatchedMetadata()).isSameInstanceAs(V1_METADATA);
- assertThat(section.getDecryptedMetadata()).isEqualTo(ALICE_METADATA);
+ assertThat(section.getDecryptedMetadata()).isEqualTo(V1_ALICE_METADATA);
}
}
}
diff --git a/nearby/presence/np_java_ffi/test/com/google/android/nearby/presence/rust/DeserializeTests.java b/nearby/presence/np_java_ffi/test/com/google/android/nearby/presence/rust/DeserializeTests.java
index 7457edb..b4370d2 100644
--- a/nearby/presence/np_java_ffi/test/com/google/android/nearby/presence/rust/DeserializeTests.java
+++ b/nearby/presence/np_java_ffi/test/com/google/android/nearby/presence/rust/DeserializeTests.java
@@ -16,24 +16,34 @@
package com.google.android.nearby.presence.rust;
-import static com.google.android.nearby.presence.rust.DeserializedV1Section.VerificationMode;
-import static com.google.android.nearby.presence.rust.TestData.*;
-import static com.google.android.nearby.presence.rust.credential.CredentialBook.NoMetadata;
+import static com.google.android.nearby.presence.rust.TestData.V0_CRED;
+import static com.google.android.nearby.presence.rust.TestData.V0_IDENTITY_TOKEN;
+import static com.google.android.nearby.presence.rust.TestData.V0_PRIVATE;
+import static com.google.android.nearby.presence.rust.TestData.V0_PUBLIC;
+import static com.google.android.nearby.presence.rust.TestData.V1_CRED;
+import static com.google.android.nearby.presence.rust.TestData.V1_IDENTITY_TOKEN;
+import static com.google.android.nearby.presence.rust.TestData.V1_PRIVATE;
+import static com.google.android.nearby.presence.rust.TestData.V1_PUBLIC;
import static com.google.common.truth.Truth.assertThat;
import com.google.android.nearby.presence.rust.credential.CredentialBook;
-import org.junit.jupiter.api.Test;
+import com.google.android.nearby.presence.rust.credential.CredentialBook.NoMetadata;
+import java.util.ArrayList;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.JUnit4;
+@RunWith(JUnit4.class)
public class DeserializeTests {
- DeserializeResult parsePublicAdv(byte[] bytes) {
+ DeserializeResult<NoMetadata> parsePublicAdv(byte[] bytes) {
// Call parse with an empty CredentialBook
try (CredentialBook<NoMetadata> book = CredentialBook.empty()) {
return NpAdv.deserializeAdvertisement(bytes, book);
}
}
- DeserializeResult parsePrivateAdv(byte[] bytes) {
+ DeserializeResult<NoMetadata> parsePrivateAdv(byte[] bytes) {
try (CredentialBook<NoMetadata> book =
CredentialBook.<NoMetadata>builder()
.addDiscoveryCredential(V0_CRED, NoMetadata.INSTANCE)
@@ -44,11 +54,11 @@
}
@Test
- void deserializeAdvertisement_v0_canParsePublic() {
- try (DeserializeResult result = parsePublicAdv(V0_PUBLIC)) {
+ public void deserializeAdvertisement_v0_canParsePublic() {
+ try (DeserializeResult<NoMetadata> result = parsePublicAdv(V0_PUBLIC)) {
assertThat(result.getKind()).isEqualTo(DeserializeResult.Kind.V0_ADVERTISEMENT);
- DeserializedV0Advertisement adv = result.getAsV0();
+ DeserializedV0Advertisement<NoMetadata> adv = result.getAsV0();
assertThat(adv).isNotNull();
assertThat(adv.isLegible()).isTrue();
@@ -60,11 +70,11 @@
}
@Test
- void deserializeAdvertisement_v0_canParsePublicWithCreds() {
- try (DeserializeResult result = parsePrivateAdv(V0_PUBLIC)) {
+ public void deserializeAdvertisement_v0_canParsePublicWithCreds() {
+ try (DeserializeResult<NoMetadata> result = parsePrivateAdv(V0_PUBLIC)) {
assertThat(result.getKind()).isEqualTo(DeserializeResult.Kind.V0_ADVERTISEMENT);
- DeserializedV0Advertisement adv = result.getAsV0();
+ DeserializedV0Advertisement<NoMetadata> adv = result.getAsV0();
assertThat(adv).isNotNull();
assertThat(adv.isLegible()).isTrue();
@@ -76,11 +86,11 @@
}
@Test
- void deserializeAdvertisement_v0_canParsePrivate() {
- try (DeserializeResult result = parsePrivateAdv(V0_PRIVATE)) {
+ public void deserializeAdvertisement_v0_canParsePrivate() {
+ try (DeserializeResult<NoMetadata> result = parsePrivateAdv(V0_PRIVATE)) {
assertThat(result.getKind()).isEqualTo(DeserializeResult.Kind.V0_ADVERTISEMENT);
- DeserializedV0Advertisement adv = result.getAsV0();
+ DeserializedV0Advertisement<NoMetadata> adv = result.getAsV0();
assertThat(adv).isNotNull();
assertThat(adv.isLegible()).isTrue();
@@ -88,18 +98,18 @@
assertThat(adv.getDataElementCount()).isEqualTo(1);
assertThat(adv.getDataElement(0)).isInstanceOf(V0DataElement.TxPower.class);
assertThat(adv.getIdentityToken()).isEqualTo(V0_IDENTITY_TOKEN);
- assertThat(adv.getSalt()).asList().containsExactly((byte) 0x22, (byte) 0x22);
+ assertThat(adv.getSalt()).isEqualTo(new byte[] {(byte) 0x22, (byte) 0x22});
assertThat(adv.getMatchedMetadata()).isSameInstanceAs(NoMetadata.INSTANCE);
assertThat(adv.getDecryptedMetadata()).isNull();
}
}
@Test
- void deserializeAdvertisement_v0_cannotParsePrivateWithoutCreds() {
- try (DeserializeResult result = parsePublicAdv(V0_PRIVATE)) {
+ public void deserializeAdvertisement_v0_cannotParsePrivateWithoutCreds() {
+ try (DeserializeResult<NoMetadata> result = parsePublicAdv(V0_PRIVATE)) {
assertThat(result.getKind()).isEqualTo(DeserializeResult.Kind.V0_ADVERTISEMENT);
- DeserializedV0Advertisement adv = result.getAsV0();
+ DeserializedV0Advertisement<NoMetadata> adv = result.getAsV0();
assertThat(adv).isNotNull();
assertThat(adv.isLegible()).isFalse();
@@ -107,9 +117,9 @@
}
@Test
- void deserializeAdvertisement_v0_canReadTxPowerDe() {
- try (DeserializeResult result = parsePublicAdv(V0_PUBLIC)) {
- DeserializedV0Advertisement adv = result.getAsV0();
+ public void deserializeAdvertisement_v0_canReadTxPowerDe() {
+ try (DeserializeResult<NoMetadata> result = parsePublicAdv(V0_PUBLIC)) {
+ DeserializedV0Advertisement<NoMetadata> adv = result.getAsV0();
V0DataElement de = adv.getDataElement(0);
@@ -120,9 +130,37 @@
}
@Test
- void deserializeAdvertisement_v0_canReadActionsDe() {
- try (DeserializeResult result = parsePublicAdv(V0_PUBLIC)) {
- DeserializedV0Advertisement adv = result.getAsV0();
+ public void deserializeAdvertisement_v0_canIterateDataElements() throws Exception {
+ final int numDes = 2;
+ byte[] advBytes;
+
+ try (V0AdvertisementBuilder builder = V0AdvertisementBuilder.newPublic()) {
+ builder.addDataElement(
+ new V0DataElement.V0Actions(
+ IdentityKind.PLAINTEXT, V0DataElement.V0ActionType.NEARBY_SHARE));
+ builder.addDataElement(new V0DataElement.TxPower(10));
+ advBytes = builder.build();
+ }
+
+ try (DeserializeResult<NoMetadata> result = parsePublicAdv(advBytes)) {
+ DeserializedV0Advertisement<NoMetadata> adv = result.getAsV0();
+ ArrayList<V0DataElement> deList = new ArrayList<>();
+ for (V0DataElement de : adv.getDataElements()) {
+ deList.add(de);
+ }
+
+ // Validate de order
+ assertThat(deList.get(0)).isInstanceOf(V0DataElement.V0Actions.class);
+ assertThat(deList.get(1)).isInstanceOf(V0DataElement.TxPower.class);
+ // Validate de count
+ assertThat(deList.size()).isEqualTo(numDes);
+ }
+ }
+
+ @Test
+ public void deserializeAdvertisement_v0_canReadActionsDe() {
+ try (DeserializeResult<NoMetadata> result = parsePublicAdv(V0_PUBLIC)) {
+ DeserializedV0Advertisement<NoMetadata> adv = result.getAsV0();
V0DataElement de = adv.getDataElement(1);
@@ -135,11 +173,11 @@
}
@Test
- void deserializeAdvertisement_v1_canParsePublic() {
- try (DeserializeResult result = parsePublicAdv(V1_PUBLIC)) {
+ public void deserializeAdvertisement_v1_canParsePublic() {
+ try (DeserializeResult<NoMetadata> result = parsePublicAdv(V1_PUBLIC)) {
assertThat(result.getKind()).isEqualTo(DeserializeResult.Kind.V1_ADVERTISEMENT);
- DeserializedV1Advertisement adv = result.getAsV1();
+ DeserializedV1Advertisement<NoMetadata> adv = result.getAsV1();
assertThat(adv).isNotNull();
assertThat(adv.getNumLegibleSections()).isEqualTo(1);
@@ -148,17 +186,17 @@
}
@Test
- void deserializeAdvertisement_v1_canParsePrivate() {
- try (DeserializeResult result = parsePrivateAdv(V1_PRIVATE)) {
+ public void deserializeAdvertisement_v1_canParsePrivate() {
+ try (DeserializeResult<NoMetadata> result = parsePrivateAdv(V1_PRIVATE)) {
assertThat(result.getKind()).isEqualTo(DeserializeResult.Kind.V1_ADVERTISEMENT);
- DeserializedV1Advertisement adv = result.getAsV1();
+ DeserializedV1Advertisement<NoMetadata> adv = result.getAsV1();
assertThat(adv).isNotNull();
assertThat(adv.getNumLegibleSections()).isEqualTo(1);
assertThat(adv.getNumUndecryptableSections()).isEqualTo(0);
- DeserializedV1Section section = adv.getSection(0);
+ DeserializedV1Section<NoMetadata> section = adv.getSection(0);
assertThat(section.getIdentityKind()).isEqualTo(IdentityKind.DECRYPTED);
assertThat(section.getDataElementCount()).isEqualTo(1);
assertThat(section.getIdentityToken()).isEqualTo(V1_IDENTITY_TOKEN);
@@ -169,11 +207,11 @@
}
@Test
- void deserializeAdvertisement_v1_canParsePrivateWithoutCreds() {
- try (DeserializeResult result = parsePublicAdv(V1_PRIVATE)) {
+ public void deserializeAdvertisement_v1_canParsePrivateWithoutCreds() {
+ try (DeserializeResult<NoMetadata> result = parsePublicAdv(V1_PRIVATE)) {
assertThat(result.getKind()).isEqualTo(DeserializeResult.Kind.V1_ADVERTISEMENT);
- DeserializedV1Advertisement adv = result.getAsV1();
+ DeserializedV1Advertisement<NoMetadata> adv = result.getAsV1();
assertThat(adv).isNotNull();
assertThat(adv.getNumLegibleSections()).isEqualTo(0);
@@ -182,11 +220,11 @@
}
@Test
- void deserializeAdvertisement_v1_canParsePublicSection() {
- try (DeserializeResult result = parsePublicAdv(V1_PUBLIC)) {
- DeserializedV1Advertisement adv = result.getAsV1();
+ public void deserializeAdvertisement_v1_canParsePublicSection() {
+ try (DeserializeResult<NoMetadata> result = parsePublicAdv(V1_PUBLIC)) {
+ DeserializedV1Advertisement<NoMetadata> adv = result.getAsV1();
- DeserializedV1Section section = adv.getSection(0);
+ DeserializedV1Section<NoMetadata> section = adv.getSection(0);
assertThat(section).isNotNull();
assertThat(section.getIdentityKind()).isEqualTo(IdentityKind.PLAINTEXT);
@@ -197,10 +235,10 @@
}
@Test
- void deserializeAdvertisement_v1_canReadGenericDe() {
- try (DeserializeResult result = parsePublicAdv(V1_PUBLIC)) {
- DeserializedV1Advertisement adv = result.getAsV1();
- DeserializedV1Section section = adv.getSection(0);
+ public void deserializeAdvertisement_v1_canReadGenericDe() {
+ try (DeserializeResult<NoMetadata> result = parsePublicAdv(V1_PUBLIC)) {
+ DeserializedV1Advertisement<NoMetadata> adv = result.getAsV1();
+ DeserializedV1Section<NoMetadata> section = adv.getSection(0);
V1DataElement de = section.getDataElement(0);
@@ -208,7 +246,67 @@
assertThat(de).isInstanceOf(V1DataElement.Generic.class);
V1DataElement.Generic generic = (V1DataElement.Generic) de;
assertThat(generic.getType()).isEqualTo(0x05 /* V1 TxPower */);
- assertThat(generic.getData()).asList().containsExactly((byte) 6);
+ assertThat(generic.getData()).isEqualTo(new byte[] {(byte) 6});
+ }
+ }
+
+ @Test
+ public void deserializeAdvertisement_v1_canIterateSections() throws Exception {
+ final int NUM_SECTIONS = 5;
+ byte[] advBytes;
+
+ try (V1AdvertisementBuilder builder = V1AdvertisementBuilder.newPublic()) {
+ for (int i = 0; i < NUM_SECTIONS; i++) {
+ builder
+ .addPublicSection()
+ .addDataElement(new V1DataElement.Generic(123, new byte[] {(byte) i}))
+ .finishSection();
+ }
+ advBytes = builder.build();
+ }
+
+ try (DeserializeResult<NoMetadata> result = parsePublicAdv(advBytes)) {
+ DeserializedV1Advertisement<NoMetadata> adv = result.getAsV1();
+
+ byte i = 0;
+ for (DeserializedV1Section<NoMetadata> section : adv.getSections()) {
+ V1DataElement.Generic de = (V1DataElement.Generic) section.getDataElement(0);
+ assertThat(de.getType()).isEqualTo(123);
+ // Validate section order
+ assertThat(de.getData()).isEqualTo(new byte[] {i++});
+ }
+ // Validate section count
+ assertThat(i).isEqualTo(NUM_SECTIONS);
+ }
+ }
+
+ @Test
+ public void deserializeAdvertisement_v1_canIterateDataElements() throws Exception {
+ final int numDes = 5;
+ byte[] advBytes;
+
+ try (V1AdvertisementBuilder builder = V1AdvertisementBuilder.newPublic()) {
+ try (V1SectionBuilder section = builder.addPublicSection()) {
+ for (int i = 0; i < numDes; i++) {
+ section.addDataElement(new V1DataElement.Generic(123, new byte[] {(byte) i}));
+ }
+ }
+ advBytes = builder.build();
+ }
+
+ try (DeserializeResult<NoMetadata> result = parsePublicAdv(advBytes)) {
+ DeserializedV1Advertisement<NoMetadata> adv = result.getAsV1();
+ DeserializedV1Section<NoMetadata> section = adv.getSection(0);
+
+ byte i = 0;
+ for (V1DataElement de : section.getDataElements()) {
+ V1DataElement.Generic generic = (V1DataElement.Generic) de;
+ assertThat(generic.getType()).isEqualTo(123);
+ // Validate de order
+ assertThat(generic.getData()).isEqualTo(new byte[] {i++});
+ }
+ // Validate de count
+ assertThat(i).isEqualTo(numDes);
}
}
}
diff --git a/nearby/presence/np_java_ffi/test/com/google/android/nearby/presence/rust/OwnedHandleTests.java b/nearby/presence/np_java_ffi/test/com/google/android/nearby/presence/rust/OwnedHandleTests.java
new file mode 100644
index 0000000..978a75b
--- /dev/null
+++ b/nearby/presence/np_java_ffi/test/com/google/android/nearby/presence/rust/OwnedHandleTests.java
@@ -0,0 +1,115 @@
+/*
+ * 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.nearby.presence.rust;
+
+import static com.google.common.truth.Truth.assertThat;
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.ArgumentMatchers.same;
+import static org.mockito.Mockito.spy;
+import static org.mockito.Mockito.verify;
+
+import java.lang.ref.PhantomReference;
+import java.lang.ref.ReferenceQueue;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.rules.Verifier;
+import org.junit.runner.RunWith;
+import org.junit.runners.JUnit4;
+import org.mockito.Mock;
+import org.mockito.junit.MockitoJUnit;
+import org.mockito.junit.MockitoRule;
+import org.mockito.quality.Strictness;
+
+@RunWith(JUnit4.class)
+public class OwnedHandleTests {
+
+ @Rule public MockitoRule mockito = MockitoJUnit.rule().strictness(Strictness.STRICT_STUBS);
+
+ @Rule
+ public Verifier checkCleanerAfterTest =
+ new Verifier() {
+ @Override
+ protected void verify() {
+ assertThat(cleaner.getRegisteredObjectCount()).isEqualTo(0);
+ }
+ };
+
+ CooperativeCleaner cleaner = new CooperativeCleaner();
+ @Mock OwnedHandle.Destructor destructor;
+
+ class MyHandle extends OwnedHandle {
+ public MyHandle(long handleId) {
+ super(handleId, cleaner, destructor);
+ }
+
+ public MyHandle(CooperativeCleaner cleaner, long handleId) {
+ super(handleId, cleaner, destructor);
+ }
+ }
+
+ @Test
+ public void constructor_registersWithCleaner() {
+ CooperativeCleaner spyCleaner = spy(cleaner);
+ try (MyHandle handle = new MyHandle(spyCleaner, 123)) {
+ verify(spyCleaner).register(same(handle), any());
+ }
+ }
+
+ @Test
+ public void close_callsDestructor() {
+ try (MyHandle handle = new MyHandle(123)) {}
+ verify(destructor).deallocate(eq(123L));
+ }
+
+ @Test
+ public void leaked_willEndUpInCleanerQueue() throws Exception {
+ {
+ // leaked
+ waitForGc(new MyHandle(0xbad));
+ }
+
+ cleaner.processQueuedObjects();
+
+ verify(destructor).deallocate(eq(0xbadL));
+ }
+
+ @Test
+ public void close_callsDestructorOfLeakedObject() throws Exception {
+ {
+ // leaked
+ waitForGc(new MyHandle(0xbad));
+ }
+
+ MyHandle handle = new MyHandle(123);
+ handle.close();
+
+ verify(destructor).deallocate(eq(0xbadL));
+ verify(destructor).deallocate(eq(123L));
+ }
+
+ private void waitForGc(Object object) throws InterruptedException {
+ ReferenceQueue<Object> queue = new ReferenceQueue<>();
+ // Need to keep this around to be notified when object is GC'd
+ PhantomReference<Object> ref = new PhantomReference<>(object, queue);
+
+ object = null;
+ System.gc();
+
+ assertThat(queue.remove()).isSameInstanceAs(ref);
+ }
+}
diff --git a/nearby/presence/np_java_ffi/test/com/google/android/nearby/presence/rust/SerializeTests.java b/nearby/presence/np_java_ffi/test/com/google/android/nearby/presence/rust/SerializeTests.java
index b8e18fd..7c4d88e 100644
--- a/nearby/presence/np_java_ffi/test/com/google/android/nearby/presence/rust/SerializeTests.java
+++ b/nearby/presence/np_java_ffi/test/com/google/android/nearby/presence/rust/SerializeTests.java
@@ -16,18 +16,27 @@
package com.google.android.nearby.presence.rust;
-import static com.google.android.nearby.presence.rust.TestData.*;
-import static com.google.android.nearby.presence.rust.V0DataElement.TxPower;
-import static com.google.android.nearby.presence.rust.V0DataElement.V0ActionType;
-import static com.google.android.nearby.presence.rust.V0DataElement.V0Actions;
-import static com.google.android.nearby.presence.rust.credential.CredentialBook.NoMetadata;
+import static com.google.android.nearby.presence.rust.TestData.V0_BROADCAST_CRED;
+import static com.google.android.nearby.presence.rust.TestData.V0_CRED;
+import static com.google.android.nearby.presence.rust.TestData.V1_BROADCAST_CRED;
+import static com.google.android.nearby.presence.rust.TestData.V1_CRED;
+import static com.google.android.nearby.presence.rust.TestData.V1_PRIVATE;
+import static com.google.android.nearby.presence.rust.TestData.V1_PUBLIC;
import static com.google.common.truth.Truth.assertThat;
-import static org.junit.jupiter.api.Assertions.assertThrows;
+import static org.junit.Assert.assertThrows;
+import com.google.android.nearby.presence.rust.V0DataElement.TxPower;
+import com.google.android.nearby.presence.rust.V0DataElement.V0ActionType;
+import com.google.android.nearby.presence.rust.V0DataElement.V0Actions;
+import com.google.android.nearby.presence.rust.V1DataElement.Generic;
import com.google.android.nearby.presence.rust.credential.CredentialBook;
-import org.junit.jupiter.api.Disabled;
-import org.junit.jupiter.api.Test;
+import com.google.android.nearby.presence.rust.credential.CredentialBook.NoMetadata;
+import org.junit.Ignore;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.JUnit4;
+@RunWith(JUnit4.class)
public class SerializeTests {
public static final TxPower TX_POWER = new TxPower(7);
@@ -38,10 +47,144 @@
public static final V0Actions PRIVATE_ACTIONS =
new V0Actions(IdentityKind.DECRYPTED, V0ActionType.CALL_TRANSFER, V0ActionType.NEARBY_SHARE);
+ public static final Generic GENERIC_DE = new Generic(1234, new byte[] {0x01, 0x02, 0x03, 0x04});
+
+ @SuppressWarnings("MutablePublicArray")
public static final byte[] SALT = new byte[] {0x12, 0x34};
@Test
- void serializeAdvertisement_v0_canSerialize() throws Exception {
+ public void constructActionsDe_mergedValueIsNonZero() {
+ assertThat(PUBLIC_ACTIONS.getActionBits()).isNotEqualTo(0);
+ assertThat(PRIVATE_ACTIONS.getActionBits()).isNotEqualTo(0);
+ }
+
+ @Test
+ public void serializeAdvertisement_v1_canCreatePubSection() throws Exception {
+ try (V1AdvertisementBuilder builder = V1AdvertisementBuilder.newPublic()) {
+ try (V1SectionBuilder sectionBuilder = builder.addPublicSection()) {
+ sectionBuilder.addDataElement(GENERIC_DE);
+ }
+ byte[] adv = builder.build();
+
+ assertThat(adv).isNotNull();
+ assertThat(adv).isNotEmpty();
+ }
+ }
+
+ @Test
+ public void serializeAdvertisement_v1_canCreateMicEncryptedSection() throws Exception {
+ try (V1AdvertisementBuilder builder = V1AdvertisementBuilder.newEncrypted()) {
+ try (V1SectionBuilder sectionBuilder =
+ builder.addEncryptedSection(V1_BROADCAST_CRED, VerificationMode.MIC)) {
+ sectionBuilder.addDataElement(GENERIC_DE);
+ }
+ byte[] adv = builder.build();
+
+ assertThat(adv).isNotNull();
+ assertThat(adv).isNotEmpty();
+ }
+ }
+
+ @Test
+ public void serializeAdvertisement_v1_canCreateEmptyPublicAdvertisement() throws Exception {
+ try (V1AdvertisementBuilder builder = V1AdvertisementBuilder.newPublic()) {
+
+ byte[] adv = builder.build();
+
+ assertThat(adv).isNotNull();
+ }
+ }
+
+ @Test
+ public void serializeAdvertisement_v1_canCreateEmptyPublicSection() throws Exception {
+ try (V1AdvertisementBuilder builder = V1AdvertisementBuilder.newPublic()) {
+ builder.addPublicSection().close();
+
+ byte[] adv = builder.build();
+
+ assertThat(adv).isNotNull();
+ }
+ }
+
+ @Test
+ public void serializeAdvertisement_v1_canCreateSignatureEncryptedSection() throws Exception {
+ try (V1AdvertisementBuilder builder = V1AdvertisementBuilder.newEncrypted()) {
+ try (V1SectionBuilder sectionBuilder =
+ builder.addEncryptedSection(V1_BROADCAST_CRED, VerificationMode.SIGNATURE)) {
+ sectionBuilder.addDataElement(GENERIC_DE);
+ }
+ byte[] adv = builder.build();
+
+ assertThat(adv).isNotNull();
+ assertThat(adv).isNotEmpty();
+ }
+ }
+
+ @Test
+ public void serializeAdvertisement_v1_canCreateRoundtripEncrypted() throws Exception {
+ try (CredentialBook<NoMetadata> book =
+ CredentialBook.<NoMetadata>builder()
+ .addDiscoveryCredential(V1_CRED, NoMetadata.INSTANCE)
+ .build();
+ DeserializeResult<NoMetadata> original = NpAdv.deserializeAdvertisement(V1_PRIVATE, book);
+ V1AdvertisementBuilder builder = V1AdvertisementBuilder.newEncrypted()) {
+ for (DeserializedV1Section<NoMetadata> section : original.getAsV1().getSections()) {
+ try (V1SectionBuilder sectionBuilder =
+ builder.addEncryptedSection(V1_BROADCAST_CRED, VerificationMode.SIGNATURE)) {
+ for (V1DataElement de : section.getDataElements()) {
+ sectionBuilder.addDataElement(de);
+ }
+ }
+ }
+ byte[] adv = builder.build();
+
+ // Can't check contents due to mismatched salts
+ assertThat(adv.length).isEqualTo(V1_PRIVATE.length);
+ }
+ }
+
+ @Test
+ public void serializeAdvertisement_v1_canCreateRoundtripPublic() throws Exception {
+ try (CredentialBook<NoMetadata> book = CredentialBook.empty();
+ DeserializeResult<NoMetadata> original = NpAdv.deserializeAdvertisement(V1_PUBLIC, book);
+ V1AdvertisementBuilder builder = V1AdvertisementBuilder.newPublic()) {
+ for (DeserializedV1Section<NoMetadata> section : original.getAsV1().getSections()) {
+ try (V1SectionBuilder sectionBuilder = builder.addPublicSection()) {
+ for (V1DataElement de : section.getDataElements()) {
+ sectionBuilder.addDataElement(de);
+ }
+ }
+ }
+ byte[] adv = builder.build();
+
+ assertThat(adv).isEqualTo(V1_PUBLIC);
+ }
+ }
+
+ @Test
+ public void serializeAdvertisement_v1_cantCreatePubSectionInEncryptedAdv() throws Exception {
+ try (V1AdvertisementBuilder builder = V1AdvertisementBuilder.newEncrypted()) {
+ assertThrows(
+ SerializationException.InvalidSectionKindException.class,
+ () -> {
+ builder.addPublicSection().close();
+ });
+ }
+ }
+
+ @Test
+ public void serializeAdvertisement_v1_cantCreateEncryptedSectionInPublicAdv() throws Exception {
+ try (V1AdvertisementBuilder builder = V1AdvertisementBuilder.newPublic()) {
+ assertThrows(
+ SerializationException.InvalidSectionKindException.class,
+ () -> {
+ builder.addEncryptedSection(V1_BROADCAST_CRED, VerificationMode.SIGNATURE).close();
+ });
+ }
+ }
+
+ @Test
+ public void serializeAdvertisement_v0_canSerialize() throws Exception {
try (V0AdvertisementBuilder builder = V0AdvertisementBuilder.newPublic()) {
builder.addDataElement(TX_POWER);
builder.addDataElement(PUBLIC_ACTIONS);
@@ -51,7 +194,7 @@
}
@Test
- void serializeAdvertisement_v0_canSerializePrivate() throws Exception {
+ public void serializeAdvertisement_v0_canSerializePrivate() throws Exception {
try (V0AdvertisementBuilder builder =
V0AdvertisementBuilder.newEncrypted(V0_BROADCAST_CRED, SALT)) {
builder.addDataElement(TX_POWER);
@@ -62,15 +205,15 @@
}
@Test
- void serializeAdvertisement_v0_canRoundtrip() throws Exception {
+ public void serializeAdvertisement_v0_canRoundtrip() throws Exception {
try (V0AdvertisementBuilder builder = V0AdvertisementBuilder.newPublic();
- CredentialBook book = CredentialBook.empty()) {
+ CredentialBook<NoMetadata> book = CredentialBook.empty()) {
builder.addDataElement(TX_POWER);
builder.addDataElement(PUBLIC_ACTIONS);
byte[] advBytes = builder.build();
- DeserializeResult result = NpAdv.deserializeAdvertisement(advBytes, book);
- DeserializedV0Advertisement adv = result.getAsV0();
+ DeserializeResult<NoMetadata> result = NpAdv.deserializeAdvertisement(advBytes, book);
+ DeserializedV0Advertisement<NoMetadata> adv = result.getAsV0();
assertThat(adv).isNotNull();
assertThat(adv.getDataElementCount()).isEqualTo(2);
@@ -87,17 +230,19 @@
}
@Test
- void serializeAdvertisement_v0_canRoundtripPrivate() throws Exception {
+ public void serializeAdvertisement_v0_canRoundtripPrivate() throws Exception {
try (V0AdvertisementBuilder builder =
V0AdvertisementBuilder.newEncrypted(V0_BROADCAST_CRED, SALT);
- CredentialBook book =
- CredentialBook.builder().addDiscoveryCredential(V0_CRED, NoMetadata.INSTANCE).build()) {
+ CredentialBook<NoMetadata> book =
+ CredentialBook.<NoMetadata>builder()
+ .addDiscoveryCredential(V0_CRED, NoMetadata.INSTANCE)
+ .build()) {
builder.addDataElement(TX_POWER);
builder.addDataElement(PRIVATE_ACTIONS);
byte[] advBytes = builder.build();
- DeserializeResult result = NpAdv.deserializeAdvertisement(advBytes, book);
- DeserializedV0Advertisement adv = result.getAsV0();
+ DeserializeResult<NoMetadata> result = NpAdv.deserializeAdvertisement(advBytes, book);
+ DeserializedV0Advertisement<NoMetadata> adv = result.getAsV0();
assertThat(adv).isNotNull();
assertThat(adv.getDataElementCount()).isEqualTo(2);
@@ -114,14 +259,14 @@
}
@Test
- void serializeAdvertisement_v0_emptyIsError() throws Exception {
+ public void serializeAdvertisement_v0_emptyIsError() throws Exception {
try (V0AdvertisementBuilder builder = V0AdvertisementBuilder.newPublic()) {
assertThrows(SerializationException.UnencryptedSizeException.class, () -> builder.build());
}
}
@Test
- void serializeAdvertisement_v0_fullIsError() throws Exception {
+ public void serializeAdvertisement_v0_fullIsError() throws Exception {
try (V0AdvertisementBuilder builder = V0AdvertisementBuilder.newPublic()) {
assertThrows(
SerializationException.InsufficientSpaceException.class,
@@ -135,7 +280,7 @@
}
@Test
- void serializeAdvertisement_v0_publicAdvPrivateActionsIsError() throws Exception {
+ public void serializeAdvertisement_v0_publicAdvPrivateActionsIsError() throws Exception {
try (V0AdvertisementBuilder builder = V0AdvertisementBuilder.newPublic()) {
assertThrows(
SerializationException.InvalidDataElementException.class,
@@ -144,7 +289,7 @@
}
@Test
- void serializeAdvertisement_v0_privateAdvPublicActionsIsError() throws Exception {
+ public void serializeAdvertisement_v0_privateAdvPublicActionsIsError() throws Exception {
try (V0AdvertisementBuilder builder =
V0AdvertisementBuilder.newEncrypted(V0_BROADCAST_CRED, SALT)) {
assertThrows(
@@ -154,7 +299,7 @@
}
@Test
- void serializeAdvertisement_v0_invalidTxPowerIsError() throws Exception {
+ public void serializeAdvertisement_v0_invalidTxPowerIsError() throws Exception {
try (V0AdvertisementBuilder builder = V0AdvertisementBuilder.newPublic()) {
assertThrows(
SerializationException.InvalidDataElementException.class,
@@ -163,18 +308,18 @@
}
@Test
- void serializeAdvertisement_v0_handleIsConsumedByBuild() throws Exception {
+ public void serializeAdvertisement_v0_handleIsConsumedByBuild() throws Exception {
try (V0AdvertisementBuilder builder = V0AdvertisementBuilder.newPublic()) {
builder.addDataElement(TX_POWER);
- byte[] adv = builder.build();
+ byte[] unused = builder.build();
assertThrows(Handle.InvalidHandleException.class, () -> builder.addDataElement(TX_POWER));
assertThrows(Handle.InvalidHandleException.class, () -> builder.build());
}
}
@Test
- @Disabled("b/311225033: Duplicate data element spec change not implemented")
- void serializeAdvertisement_v0_deNotAddedTwice() throws Exception {
+ @Ignore("b/311225033: Duplicate data element spec change not implemented")
+ public void serializeAdvertisement_v0_deNotAddedTwice() throws Exception {
try (V0AdvertisementBuilder builder = V0AdvertisementBuilder.newPublic()) {
builder.addDataElement(TX_POWER);
assertThrows(Exception.class, () -> builder.addDataElement(TX_POWER));
diff --git a/nearby/presence/np_java_ffi/test/com/google/android/nearby/presence/rust/TestData.java b/nearby/presence/np_java_ffi/test/com/google/android/nearby/presence/rust/TestData.java
index 122b2a9..7408de6 100644
--- a/nearby/presence/np_java_ffi/test/com/google/android/nearby/presence/rust/TestData.java
+++ b/nearby/presence/np_java_ffi/test/com/google/android/nearby/presence/rust/TestData.java
@@ -18,10 +18,14 @@
import com.google.android.nearby.presence.rust.credential.V0BroadcastCredential;
import com.google.android.nearby.presence.rust.credential.V0DiscoveryCredential;
+import com.google.android.nearby.presence.rust.credential.V1BroadcastCredential;
import com.google.android.nearby.presence.rust.credential.V1DiscoveryCredential;
+@SuppressWarnings("MutablePublicArray")
public class TestData {
+ private TestData() {}
+
public static final byte[] V0_PUBLIC = {
0x00, // adv header
0x15, 20, // tx power
@@ -141,67 +145,6 @@
(byte) 0x68
};
- public static final byte[] V1_ENCRYPTED_ALICE_METADATA = {
- (byte) 0x0c,
- (byte) 0x27,
- (byte) 0x43,
- (byte) 0x58,
- (byte) 0xb8,
- (byte) 0xb9,
- (byte) 0x90,
- (byte) 0x72,
- (byte) 0xb1,
- (byte) 0xe8,
- (byte) 0xba,
- (byte) 0x7d,
- (byte) 0x8e,
- (byte) 0xdb,
- (byte) 0xac,
- (byte) 0x7e,
- (byte) 0x15,
- (byte) 0xd5,
- (byte) 0x05,
- (byte) 0x30,
- (byte) 0x4d,
- (byte) 0xb7,
- (byte) 0xe0,
- (byte) 0xbd,
- (byte) 0x8f,
- (byte) 0xe9,
- (byte) 0xab,
- (byte) 0x48,
- (byte) 0x66,
- (byte) 0xac,
- (byte) 0x2c,
- (byte) 0x8a,
- (byte) 0x24,
- (byte) 0x0c,
- (byte) 0x02,
- (byte) 0xb4,
- (byte) 0xd0,
- (byte) 0xb7,
- (byte) 0x10,
- (byte) 0xd6,
- (byte) 0x30,
- (byte) 0x12,
- (byte) 0xd6,
- (byte) 0x98,
- (byte) 0xdc,
- (byte) 0x55,
- (byte) 0xae,
- (byte) 0xb0,
- (byte) 0x9c,
- (byte) 0x9a,
- (byte) 0x7d,
- (byte) 0x2e,
- (byte) 0xcb,
- (byte) 0xba,
- (byte) 0xdd,
- (byte) 0x7d,
- (byte) 0x4e,
- (byte) 0xd1
- };
-
public static final byte[] V0_KEY_SEED = {
0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11,
0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11,
@@ -247,12 +190,6 @@
(byte) 0xFD
};
- public static final V0DiscoveryCredential V0_CRED =
- new V0DiscoveryCredential(V0_KEY_SEED, V0_IDENTITY_TOKEN_HMAC);
-
- public static final V0BroadcastCredential V0_BROADCAST_CRED =
- new V0BroadcastCredential(V0_KEY_SEED, V0_IDENTITY_TOKEN);
-
public static final byte[] V0_PRIVATE = {
0x04, // adv header
0x22,
@@ -275,305 +212,559 @@
(byte) 0xEC // ciphertext for metadata key & txpower DE
};
- public static final byte[] V1_IDENTITY_TOKEN = {
- (byte) 0x58,
- (byte) 0x31,
- (byte) 0x00,
- (byte) 0x48,
- (byte) 0x11,
- (byte) 0xe4,
- (byte) 0xea,
- (byte) 0x43,
- (byte) 0xe9,
- (byte) 0x01,
- (byte) 0x76,
- (byte) 0x25,
- (byte) 0xd8,
- (byte) 0xaf,
- (byte) 0xd6,
- (byte) 0x92
- };
+ public static final V0DiscoveryCredential V0_CRED =
+ new V0DiscoveryCredential(V0_KEY_SEED, V0_IDENTITY_TOKEN_HMAC);
+
+ public static final V0BroadcastCredential V0_BROADCAST_CRED =
+ new V0BroadcastCredential(V0_KEY_SEED, V0_IDENTITY_TOKEN);
public static final byte[] V1_KEY_SEED = {
- (byte) 0xc8,
- (byte) 0xdd,
- (byte) 0x01,
- (byte) 0x4d,
- (byte) 0x25,
- (byte) 0x01,
- (byte) 0xc0,
- (byte) 0xbf,
- (byte) 0x5b,
- (byte) 0x2a,
- (byte) 0x05,
- (byte) 0x48,
- (byte) 0x49,
- (byte) 0x8c,
- (byte) 0xe6,
- (byte) 0xbf,
- (byte) 0x48,
- (byte) 0x5b,
- (byte) 0x89,
- (byte) 0xb8,
- (byte) 0x47,
- (byte) 0x13,
- (byte) 0xcc,
- (byte) 0xdd,
- (byte) 0xa0,
- (byte) 0x18,
- (byte) 0xac,
- (byte) 0xd9,
- (byte) 0xef,
- (byte) 0x58,
- (byte) 0x9f,
- (byte) 0x76
- };
-
- public static final byte[] V1_MIC_SHORT_HMAC = {
- (byte) 0x09,
- (byte) 0x48,
- (byte) 0x4e,
- (byte) 0x8f,
+ (byte) 0x3B,
+ (byte) 0x4C,
+ (byte) 0x0E,
(byte) 0x39,
- (byte) 0xdc,
- (byte) 0x16,
- (byte) 0x27,
+ (byte) 0x95,
+ (byte) 0x92,
+ (byte) 0x47,
+ (byte) 0xC8,
(byte) 0x85,
- (byte) 0x0a,
- (byte) 0xea,
- (byte) 0xfc,
- (byte) 0x84,
- (byte) 0xf6,
- (byte) 0x43,
- (byte) 0x51,
- (byte) 0x62,
- (byte) 0x16,
- (byte) 0xf1,
- (byte) 0x8d,
- (byte) 0xda,
- (byte) 0xd3,
- (byte) 0xbc,
- (byte) 0xba,
- (byte) 0x43,
- (byte) 0xf1,
- (byte) 0x62,
- (byte) 0x4e,
- (byte) 0xa7,
- (byte) 0x09,
- (byte) 0xda,
- (byte) 0xde
- };
-
- public static final byte[] V1_MIC_LONG_HMAC = {
- (byte) 0xb9,
- (byte) 0x6a,
- (byte) 0xd2,
- (byte) 0x3e,
- (byte) 0x8e,
- (byte) 0x08,
- (byte) 0xe0,
- (byte) 0xf4,
- (byte) 0xe9,
- (byte) 0xba,
- (byte) 0xe9,
- (byte) 0xbb,
- (byte) 0x3d,
- (byte) 0xe3,
- (byte) 0x2f,
- (byte) 0xd1,
- (byte) 0x14,
- (byte) 0x3a,
- (byte) 0x51,
- (byte) 0x19,
- (byte) 0x54,
- (byte) 0xf8,
- (byte) 0x66,
- (byte) 0x9f,
- (byte) 0xf6,
- (byte) 0xdb,
- (byte) 0xf6,
- (byte) 0x03,
- (byte) 0xf7,
- (byte) 0x41,
- (byte) 0x20,
- (byte) 0xd7
- };
-
- public static final byte[] V1_SIG_HMAC = {
- (byte) 0xc4,
- (byte) 0x19,
- (byte) 0x6e,
- (byte) 0x84,
- (byte) 0x95,
- (byte) 0x3a,
- (byte) 0x8a,
- (byte) 0x97,
- (byte) 0xb9,
- (byte) 0xed,
- (byte) 0xf0,
- (byte) 0xba,
- (byte) 0xd2,
- (byte) 0x5d,
- (byte) 0xa4,
+ (byte) 0xCC,
+ (byte) 0x87,
+ (byte) 0x3B,
+ (byte) 0x2E,
+ (byte) 0x70,
+ (byte) 0x75,
+ (byte) 0x87,
+ (byte) 0xB5,
+ (byte) 0x85,
+ (byte) 0x9C,
+ (byte) 0x35,
+ (byte) 0x27,
+ (byte) 0xD8,
+ (byte) 0x22,
+ (byte) 0xFD,
(byte) 0x32,
- (byte) 0xb1,
- (byte) 0xf2,
- (byte) 0x1a,
- (byte) 0xf7,
- (byte) 0x7d,
+ (byte) 0x0C,
+ (byte) 0x57,
+ (byte) 0xB3,
+ (byte) 0x40,
+ (byte) 0xE4,
+ (byte) 0x3E,
+ (byte) 0xAE
+ };
+
+ public static final byte[] V1_IDENTITY_TOKEN = {
(byte) 0x95,
- (byte) 0x8f,
- (byte) 0xeb,
- (byte) 0x5f,
- (byte) 0xbe,
- (byte) 0xfd,
- (byte) 0x62,
- (byte) 0xa7,
- (byte) 0xc0,
- (byte) 0x16,
- (byte) 0x66
+ (byte) 0x71,
+ (byte) 0x75,
+ (byte) 0x38,
+ (byte) 0x49,
+ (byte) 0xEF,
+ (byte) 0x1F,
+ (byte) 0xA7,
+ (byte) 0x10,
+ (byte) 0x58,
+ (byte) 0xDD,
+ (byte) 0x2C,
+ (byte) 0xD5,
+ (byte) 0x7E,
+ (byte) 0xE6,
+ (byte) 0x14
+ };
+
+ public static final byte[] V1_PRIVATE_KEY = {
+ (byte) 0x21,
+ (byte) 0xD4,
+ (byte) 0x08,
+ (byte) 0x74,
+ (byte) 0x0F,
+ (byte) 0xD5,
+ (byte) 0x14,
+ (byte) 0x29,
+ (byte) 0xBF,
+ (byte) 0x52,
+ (byte) 0x3B,
+ (byte) 0x10,
+ (byte) 0x1B,
+ (byte) 0x23,
+ (byte) 0x23,
+ (byte) 0x80,
+ (byte) 0xCF,
+ (byte) 0xA6,
+ (byte) 0x86,
+ (byte) 0x80,
+ (byte) 0x8A,
+ (byte) 0x34,
+ (byte) 0xAC,
+ (byte) 0x06,
+ (byte) 0xC5,
+ (byte) 0x06,
+ (byte) 0x43,
+ (byte) 0x28,
+ (byte) 0x50,
+ (byte) 0xD5,
+ (byte) 0x9C,
+ (byte) 0x89
};
public static final byte[] V1_PUB_KEY = {
- (byte) 0x3c,
- (byte) 0x59,
- (byte) 0xd7,
- (byte) 0x30,
- (byte) 0x58,
- (byte) 0x8c,
- (byte) 0x45,
- (byte) 0x26,
- (byte) 0x7e,
- (byte) 0x52,
- (byte) 0x29,
+ (byte) 0xC0,
+ (byte) 0x87,
+ (byte) 0x25,
+ (byte) 0xE9,
+ (byte) 0x0F,
+ (byte) 0x5A,
+ (byte) 0xD7,
+ (byte) 0xA4,
+ (byte) 0xD8,
+ (byte) 0x7E,
+ (byte) 0x9E,
+ (byte) 0x76,
+ (byte) 0xDF,
+ (byte) 0x10,
+ (byte) 0x44,
+ (byte) 0xE9,
+ (byte) 0x78,
+ (byte) 0xF2,
+ (byte) 0x80,
+ (byte) 0x0F,
+ (byte) 0x2B,
+ (byte) 0x43,
+ (byte) 0x03,
+ (byte) 0xCA,
+ (byte) 0x64,
+ (byte) 0x0A,
+ (byte) 0x7A,
+ (byte) 0xA1,
+ (byte) 0xE4,
+ (byte) 0x43,
+ (byte) 0x63,
+ (byte) 0xAF
+ };
+
+ public static final byte[] V1_MIC_SHORT_HMAC = {
+ (byte) 0x90,
+ (byte) 0x8D,
+ (byte) 0x6D,
+ (byte) 0x40,
+ (byte) 0x76,
+ (byte) 0x0E,
+ (byte) 0x13,
+ (byte) 0xC8,
+ (byte) 0x3C,
+ (byte) 0x76,
+ (byte) 0xD2,
+ (byte) 0xE4,
+ (byte) 0xED,
+ (byte) 0x8F,
+ (byte) 0xBD,
+ (byte) 0x83,
+ (byte) 0xED,
+ (byte) 0xEC,
+ (byte) 0xFD,
+ (byte) 0xCE,
+ (byte) 0x0A,
+ (byte) 0x90,
+ (byte) 0x85,
+ (byte) 0x47,
+ (byte) 0x5D,
+ (byte) 0xAA,
+ (byte) 0x3F,
+ (byte) 0xAE,
+ (byte) 0x6C,
+ (byte) 0x90,
+ (byte) 0x43,
+ (byte) 0x15
+ };
+
+ public static final byte[] V1_MIC_LONG_HMAC = {
+ (byte) 0x57,
+ (byte) 0x99,
(byte) 0x54,
- (byte) 0xca,
- (byte) 0xc9,
- (byte) 0xcb,
- (byte) 0xca,
+ (byte) 0x4E,
+ (byte) 0xB2,
+ (byte) 0xB0,
+ (byte) 0xA1,
+ (byte) 0x99,
+ (byte) 0x2F,
+ (byte) 0x0D,
+ (byte) 0x13,
+ (byte) 0x25,
+ (byte) 0xAE,
+ (byte) 0xE0,
+ (byte) 0x8F,
+ (byte) 0xB3,
+ (byte) 0xE2,
+ (byte) 0x8F,
+ (byte) 0xD9,
+ (byte) 0x56,
+ (byte) 0x8B,
+ (byte) 0x70,
+ (byte) 0xDE,
+ (byte) 0x28,
+ (byte) 0x82,
+ (byte) 0x60,
+ (byte) 0xE7,
+ (byte) 0x71,
+ (byte) 0xA4,
+ (byte) 0x57,
+ (byte) 0xCE,
+ (byte) 0x9E
+ };
+
+ public static final byte[] V1_SIG_HMAC = {
+ (byte) 0x37,
+ (byte) 0x53,
+ (byte) 0x78,
+ (byte) 0x59,
+ (byte) 0x3F,
+ (byte) 0x8A,
+ (byte) 0x06,
+ (byte) 0x5B,
+ (byte) 0xE5,
+ (byte) 0x03,
+ (byte) 0x05,
+ (byte) 0xE7,
+ (byte) 0xC1,
+ (byte) 0xD0,
+ (byte) 0x29,
+ (byte) 0x3D,
+ (byte) 0x2C,
+ (byte) 0x3D,
+ (byte) 0xDA,
+ (byte) 0x43,
+ (byte) 0x8D,
+ (byte) 0x0A,
+ (byte) 0x29,
+ (byte) 0x23,
+ (byte) 0x58,
+ (byte) 0xAC,
+ (byte) 0x98,
+ (byte) 0x10,
+ (byte) 0x1A,
+ (byte) 0x0A,
+ (byte) 0xA6,
+ (byte) 0xB2
+ };
+
+ public static final byte[] V1_ALICE_METADATA = {
+ (byte) 0x7B,
+ (byte) 0x22,
+ (byte) 0x75,
+ (byte) 0x75,
+ (byte) 0x69,
+ (byte) 0x64,
+ (byte) 0x22,
+ (byte) 0x3A,
+ (byte) 0x22,
+ (byte) 0x33,
+ (byte) 0x37,
+ (byte) 0x38,
+ (byte) 0x38,
+ (byte) 0x34,
+ (byte) 0x35,
+ (byte) 0x65,
+ (byte) 0x31,
+ (byte) 0x2D,
+ (byte) 0x32,
+ (byte) 0x36,
+ (byte) 0x31,
+ (byte) 0x36,
+ (byte) 0x2D,
+ (byte) 0x34,
+ (byte) 0x32,
+ (byte) 0x30,
+ (byte) 0x64,
+ (byte) 0x2D,
+ (byte) 0x38,
+ (byte) 0x36,
+ (byte) 0x66,
+ (byte) 0x35,
+ (byte) 0x2D,
+ (byte) 0x36,
+ (byte) 0x37,
+ (byte) 0x34,
+ (byte) 0x31,
+ (byte) 0x37,
+ (byte) 0x37,
+ (byte) 0x61,
+ (byte) 0x37,
+ (byte) 0x35,
+ (byte) 0x30,
+ (byte) 0x34,
+ (byte) 0x64,
+ (byte) 0x22,
+ (byte) 0x2C,
+ (byte) 0x22,
+ (byte) 0x64,
+ (byte) 0x69,
+ (byte) 0x73,
+ (byte) 0x70,
+ (byte) 0x6C,
+ (byte) 0x61,
+ (byte) 0x79,
+ (byte) 0x5F,
+ (byte) 0x6E,
+ (byte) 0x61,
+ (byte) 0x6D,
+ (byte) 0x65,
+ (byte) 0x22,
+ (byte) 0x3A,
+ (byte) 0x22,
+ (byte) 0x41,
+ (byte) 0x6C,
+ (byte) 0x69,
+ (byte) 0x63,
+ (byte) 0x65,
+ (byte) 0x22,
+ (byte) 0x2C,
+ (byte) 0x22,
+ (byte) 0x6C,
+ (byte) 0x6F,
+ (byte) 0x63,
+ (byte) 0x61,
+ (byte) 0x74,
+ (byte) 0x69,
+ (byte) 0x6F,
+ (byte) 0x6E,
+ (byte) 0x22,
+ (byte) 0x3A,
+ (byte) 0x22,
+ (byte) 0x57,
+ (byte) 0x6F,
+ (byte) 0x6E,
+ (byte) 0x64,
+ (byte) 0x65,
(byte) 0x72,
- (byte) 0x94,
- (byte) 0x24,
- (byte) 0xd8,
- (byte) 0xf5,
- (byte) 0xa6,
- (byte) 0x1e,
- (byte) 0xcf,
+ (byte) 0x6C,
+ (byte) 0x61,
+ (byte) 0x6E,
+ (byte) 0x64,
+ (byte) 0x22,
+ (byte) 0x7D
+ };
+
+ public static final byte[] V1_ENCRYPTED_ALICE_METADATA = {
+ (byte) 0xFD,
+ (byte) 0xEC,
+ (byte) 0x0B,
+ (byte) 0xE8,
+ (byte) 0x79,
+ (byte) 0x1C,
+ (byte) 0x10,
+ (byte) 0x6D,
+ (byte) 0x33,
+ (byte) 0x11,
+ (byte) 0xC7,
+ (byte) 0xF3,
+ (byte) 0x9B,
+ (byte) 0x90,
+ (byte) 0x8A,
+ (byte) 0x5A,
+ (byte) 0x74,
+ (byte) 0x7F,
+ (byte) 0x9C,
+ (byte) 0x3C,
+ (byte) 0xC9,
+ (byte) 0xBC,
+ (byte) 0x90,
+ (byte) 0xC5,
+ (byte) 0x88,
+ (byte) 0xE0,
+ (byte) 0xED,
+ (byte) 0x28,
+ (byte) 0xDC,
+ (byte) 0x25,
+ (byte) 0xE5,
+ (byte) 0xD8,
+ (byte) 0x9C,
+ (byte) 0x28,
+ (byte) 0x0F,
+ (byte) 0x23,
+ (byte) 0x9D,
+ (byte) 0x21,
+ (byte) 0xC7,
+ (byte) 0xFF,
+ (byte) 0xE1,
+ (byte) 0x60,
+ (byte) 0x62,
+ (byte) 0x5B,
+ (byte) 0xBC,
+ (byte) 0x22,
+ (byte) 0xB4,
+ (byte) 0x3A,
+ (byte) 0x02,
+ (byte) 0x9D,
+ (byte) 0x69,
+ (byte) 0x53,
+ (byte) 0x2A,
+ (byte) 0x62,
+ (byte) 0xB0,
+ (byte) 0x82,
+ (byte) 0xC7,
+ (byte) 0x79,
+ (byte) 0x9E,
+ (byte) 0x30,
+ (byte) 0x76,
+ (byte) 0xB0,
+ (byte) 0x8A,
+ (byte) 0xC3,
+ (byte) 0xCD,
+ (byte) 0x11,
+ (byte) 0xEA,
+ (byte) 0x45,
+ (byte) 0x4A,
+ (byte) 0x3C,
+ (byte) 0x25,
+ (byte) 0x45,
+ (byte) 0xD9,
+ (byte) 0x44,
+ (byte) 0x5B,
+ (byte) 0xE3,
+ (byte) 0xA0,
+ (byte) 0x3E,
(byte) 0x04,
- (byte) 0x3e,
- (byte) 0x8f,
- (byte) 0x91,
+ (byte) 0x79,
+ (byte) 0x6B,
+ (byte) 0xC3,
+ (byte) 0x62,
+ (byte) 0xA4,
+ (byte) 0xA1,
+ (byte) 0xF4,
+ (byte) 0x76,
+ (byte) 0x58,
+ (byte) 0x42,
+ (byte) 0xDC,
+ (byte) 0x37,
+ (byte) 0x93,
(byte) 0x81,
- (byte) 0x6d,
- (byte) 0x19,
- (byte) 0x74
+ (byte) 0x60,
+ (byte) 0x8E,
+ (byte) 0x00,
+ (byte) 0x6D,
+ (byte) 0xE0,
+ (byte) 0x22,
+ (byte) 0x82,
+ (byte) 0x98,
+ (byte) 0x40,
+ (byte) 0x59,
+ (byte) 0x41,
+ (byte) 0xF9,
+ (byte) 0x88,
+ (byte) 0xB8,
+ (byte) 0xFB,
+ (byte) 0x9E,
+ (byte) 0xED
+ };
+
+ public static final byte[] V1_PRIVATE = {
+ (byte) 0x20,
+ (byte) 0x03,
+ (byte) 0x6C,
+ (byte) 0x94,
+ (byte) 0x77,
+ (byte) 0x9A,
+ (byte) 0xF9,
+ (byte) 0x89,
+ (byte) 0x83,
+ (byte) 0x9F,
+ (byte) 0x41,
+ (byte) 0x98,
+ (byte) 0x33,
+ (byte) 0x68,
+ (byte) 0x24,
+ (byte) 0x9A,
+ (byte) 0xCF,
+ (byte) 0xAC,
+ (byte) 0x8E,
+ (byte) 0xC3,
+ (byte) 0x46,
+ (byte) 0x02,
+ (byte) 0x30,
+ (byte) 0x89,
+ (byte) 0x68,
+ (byte) 0x99,
+ (byte) 0xA2,
+ (byte) 0xF5,
+ (byte) 0x32,
+ (byte) 0x36,
+ (byte) 0x06,
+ (byte) 0x18,
+ (byte) 0x0D,
+ (byte) 0xF6,
+ (byte) 0x42,
+ (byte) 0x52,
+ (byte) 0xA1,
+ (byte) 0x59,
+ (byte) 0x46,
+ (byte) 0xC8,
+ (byte) 0xD1,
+ (byte) 0x27,
+ (byte) 0x74,
+ (byte) 0xFF,
+ (byte) 0xBF,
+ (byte) 0x53,
+ (byte) 0xFE,
+ (byte) 0x51,
+ (byte) 0xCB,
+ (byte) 0x39,
+ (byte) 0x6D,
+ (byte) 0x28,
+ (byte) 0x3C,
+ (byte) 0x7E,
+ (byte) 0xD9,
+ (byte) 0x6F,
+ (byte) 0xB0,
+ (byte) 0x70,
+ (byte) 0x82,
+ (byte) 0x26,
+ (byte) 0x51,
+ (byte) 0x11,
+ (byte) 0xF2,
+ (byte) 0x90,
+ (byte) 0xC0,
+ (byte) 0xBE,
+ (byte) 0x34,
+ (byte) 0x96,
+ (byte) 0x57,
+ (byte) 0x1F,
+ (byte) 0x4F,
+ (byte) 0xC0,
+ (byte) 0x87,
+ (byte) 0xF0,
+ (byte) 0xA8,
+ (byte) 0x0D,
+ (byte) 0xD8,
+ (byte) 0xE3,
+ (byte) 0xC7,
+ (byte) 0x8C,
+ (byte) 0xAA,
+ (byte) 0x7E,
+ (byte) 0x41,
+ (byte) 0x14,
+ (byte) 0x58,
+ (byte) 0xF4,
+ (byte) 0xD3,
+ (byte) 0x8E,
+ (byte) 0x8E,
+ (byte) 0xA0,
+ (byte) 0x57,
+ (byte) 0xF4,
+ (byte) 0x63,
+ (byte) 0x43,
+ (byte) 0x74,
+ (byte) 0x08,
+ (byte) 0xC7,
+ (byte) 0xB8,
+ (byte) 0x51,
+ (byte) 0x66,
+ (byte) 0xB9
};
public static final V1DiscoveryCredential V1_CRED =
new V1DiscoveryCredential(
V1_KEY_SEED, V1_MIC_SHORT_HMAC, V1_MIC_LONG_HMAC, V1_SIG_HMAC, V1_PUB_KEY);
- public static final byte[] V1_PRIVATE = {
- (byte) 0x20,
- (byte) 0x03,
- (byte) 0xfc,
- (byte) 0x32,
- (byte) 0xb7,
- (byte) 0x5d,
- (byte) 0xdd,
- (byte) 0x6a,
- (byte) 0xdb,
- (byte) 0xb0,
- (byte) 0x89,
- (byte) 0x7d,
- (byte) 0xb9,
- (byte) 0xcd,
- (byte) 0xa9,
- (byte) 0x6e,
- (byte) 0x73,
- (byte) 0x6d,
- (byte) 0x7a,
- (byte) 0xfc,
- (byte) 0xeb,
- (byte) 0x2b,
- (byte) 0x0c,
- (byte) 0x02,
- (byte) 0x3d,
- (byte) 0xc8,
- (byte) 0xfa,
- (byte) 0xc8,
- (byte) 0x78,
- (byte) 0x83,
- (byte) 0x56,
- (byte) 0xfa,
- (byte) 0x53,
- (byte) 0x11,
- (byte) 0x42,
- (byte) 0x08,
- (byte) 0x9e,
- (byte) 0xfe,
- (byte) 0x70,
- (byte) 0xd0,
- (byte) 0x68,
- (byte) 0x6c,
- (byte) 0x7c,
- (byte) 0x29,
- (byte) 0x86,
- (byte) 0xd6,
- (byte) 0x76,
- (byte) 0x2b,
- (byte) 0x03,
- (byte) 0xa4,
- (byte) 0xc7,
- (byte) 0x47,
- (byte) 0x5c,
- (byte) 0x41,
- (byte) 0x9d,
- (byte) 0x21,
- (byte) 0x15,
- (byte) 0x54,
- (byte) 0x89,
- (byte) 0x43,
- (byte) 0x32,
- (byte) 0x44,
- (byte) 0x47,
- (byte) 0x34,
- (byte) 0xd7,
- (byte) 0xbd,
- (byte) 0x4f,
- (byte) 0x38,
- (byte) 0x83,
- (byte) 0x74,
- (byte) 0xe4,
- (byte) 0xdb,
- (byte) 0xcf,
- (byte) 0xfe,
- (byte) 0xe4,
- (byte) 0x7a,
- (byte) 0xae,
- (byte) 0xa8,
- (byte) 0xe2,
- (byte) 0xf5,
- (byte) 0x69,
- (byte) 0xb8,
- (byte) 0x42,
- (byte) 0xf5,
- (byte) 0x67,
- (byte) 0x7a,
- (byte) 0x34,
- (byte) 0x6d,
- (byte) 0x86,
- (byte) 0x8b,
- (byte) 0x4c,
- (byte) 0xa9,
- (byte) 0x7f,
- (byte) 0x45,
- (byte) 0x1c,
- (byte) 0x37,
- (byte) 0xf1,
- (byte) 0x6e,
- (byte) 0xfc,
- (byte) 0xae,
- (byte) 0xc6
- };
+ public static final V1BroadcastCredential V1_BROADCAST_CRED =
+ new V1BroadcastCredential(V1_KEY_SEED, V1_IDENTITY_TOKEN, V1_PRIVATE_KEY);
}
diff --git a/nearby/presence/np_java_ffi/test/com/google/android/nearby/presence/rust/TestVectors.java b/nearby/presence/np_java_ffi/test/com/google/android/nearby/presence/rust/TestVectors.java
new file mode 100644
index 0000000..c496023
--- /dev/null
+++ b/nearby/presence/np_java_ffi/test/com/google/android/nearby/presence/rust/TestVectors.java
@@ -0,0 +1,182 @@
+/*
+ * 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.nearby.presence.rust;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import com.google.android.nearby.presence.rust.credential.V1BroadcastCredential;
+import com.google.gson.FieldNamingPolicy;
+import com.google.gson.Gson;
+import com.google.gson.GsonBuilder;
+import com.google.gson.JsonDeserializationContext;
+import com.google.gson.JsonDeserializer;
+import com.google.gson.JsonElement;
+import com.google.gson.JsonParseException;
+import com.google.gson.reflect.TypeToken;
+import java.io.BufferedReader;
+import java.lang.reflect.Type;
+import java.nio.file.Files;
+import java.nio.file.Paths;
+import java.security.SecureRandom;
+import java.util.Arrays;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.JUnit4;
+
+@RunWith(JUnit4.class)
+public class TestVectors {
+
+ static final class DataElement {
+ byte[] contents;
+ int deType;
+ }
+
+ static final class TestVector {
+ byte[] keySeed;
+ byte[] identityToken;
+ byte[] advHeaderByte;
+ byte[] sectionSalt;
+ DataElement[] dataElements;
+ byte[] aesKey;
+ byte[] sectionMicHmacKey;
+ byte[] nonce;
+ byte[] encodedSection;
+ }
+
+ static final class HexArrayDeserializer implements JsonDeserializer<byte[]> {
+
+ private static final byte byteFromHexChar(char c) {
+ if (c >= '0' && c <= '9') {
+ return (byte) (c - '0');
+ } else if (c >= 'a' && c <= 'f') {
+ return (byte) (c - 'a' + 0x0a);
+ } else if (c >= 'A' && c <= 'F') {
+ return (byte) (c - 'A' + 0x0a);
+ } else {
+ throw new IllegalArgumentException("Invalid hex char: " + c);
+ }
+ }
+
+ @Override
+ public byte[] deserialize(JsonElement json, Type typeOfT, JsonDeserializationContext context)
+ throws JsonParseException {
+ String hex = json.getAsString();
+ int byteLen = hex.length() / 2;
+ byte[] out = new byte[byteLen];
+
+ for (int i = 0; i < byteLen; i++) {
+ int offset = i * 2;
+ out[i] =
+ (byte)
+ ((byteFromHexChar(hex.charAt(offset)) << 4)
+ | byteFromHexChar(hex.charAt(offset + 1)));
+ }
+
+ return out;
+ }
+ }
+
+ static Gson createJsonParser() {
+ return new GsonBuilder()
+ .setFieldNamingPolicy(FieldNamingPolicy.LOWER_CASE_WITH_UNDERSCORES)
+ .registerTypeAdapter(byte[].class, new HexArrayDeserializer())
+ .create();
+ }
+
+ @Test
+ public void micExtendedSaltEncryptedTestVectors() throws Exception {
+ Gson gson = createJsonParser();
+ TestVector[] testVectors = null;
+ try (BufferedReader br =
+ Files.newBufferedReader(
+ Paths.get(
+ "..",
+ "np_adv",
+ "resources",
+ "test",
+ "mic-extended-salt-encrypted-test-vectors.json"))) {
+ testVectors = gson.fromJson(br, new TypeToken<TestVector[]>() {});
+ }
+ assertThat(testVectors).isNotNull();
+
+ for (TestVector tv : testVectors) {
+ byte[] privateKey = new byte[32];
+ SecureRandom.getInstanceStrong().nextBytes(privateKey);
+ V1BroadcastCredential credential =
+ new V1BroadcastCredential(tv.keySeed, tv.identityToken, privateKey);
+ try (V1AdvertisementBuilder builder = V1AdvertisementBuilder.newEncrypted();
+ V1SectionBuilder section =
+ nativeAddSaltedSection(builder.builder, credential, tv.sectionSalt)) {
+ for (DataElement de : tv.dataElements) {
+ section.addDataElement(new V1DataElement.Generic(de.deType, de.contents));
+ }
+ section.finishSection();
+
+ byte[] adv = builder.build();
+ byte[] sectionBytes = Arrays.copyOfRange(adv, 1, adv.length);
+
+ assertThat(sectionBytes).isEqualTo(tv.encodedSection);
+ }
+ }
+ }
+
+ @Test
+ public void createJsonParser_canParseTestVector() {
+ Gson gson = createJsonParser();
+ String testJson =
+ "{ \"adv_header_byte\": \"20\", \"aes_key\": \"F3DB017C70E08EC5178C92F3AEA0C362\","
+ + " \"data_elements\": [ { \"contents\": \"CF75D23EDA8F6E4A23\", \"de_type\": 383 }, {"
+ + " \"contents\": \"731B76151735869205CC41\", \"de_type\": 73 }, { \"contents\":"
+ + " \"7C2A8DE86B2CBB997703\", \"de_type\": 228 }, { \"contents\":"
+ + " \"99F5163DCA0BB9BE89755A6C5AB321\", \"de_type\": 446 } ], \"encoded_section\":"
+ + " \"6D91100056F596D16E1F87B107EE86102FFC6D5E9002F00C41CDDF533667362CD14AC54A9388FA3D30AA7CA6603071B8B0FA19BF582479F773F1C7D0EADF98E98B9447139F244D571B780475ACD9CB248F33B2C085925213360732D44081C27CA6EB40BD8A626BC776D88C5FDB09\","
+ + " \"key_seed\": \"F0ED9126768CE7DC685FF74932AC5A876442C4E42359A43F720A575142A45043\","
+ + " \"identity_token\": \"45795EE4C6533A830886E2C5885EB9E5\", \"nonce\":"
+ + " \"40E95D525FAEA1C1FEE39A8E\", \"section_mic_hmac_key\":"
+ + " \"A22386E85112EF883218A5B75669B7102E017E9AA149F408A079F60B1D14B4F4\","
+ + " \"section_salt\": \"56F596D16E1F87B107EE86102FFC6D5E\" }";
+
+ TestVector testVector = gson.fromJson(testJson, TestVector.class);
+
+ assertThat(testVector.aesKey)
+ .isEqualTo(
+ new byte[] {
+ (byte) 0xF3,
+ (byte) 0xDB,
+ (byte) 0x01,
+ (byte) 0x7C,
+ (byte) 0x70,
+ (byte) 0xE0,
+ (byte) 0x8E,
+ (byte) 0xC5,
+ (byte) 0x17,
+ (byte) 0x8C,
+ (byte) 0x92,
+ (byte) 0xF3,
+ (byte) 0xAE,
+ (byte) 0xA0,
+ (byte) 0xC3,
+ (byte) 0x62
+ });
+ }
+
+ // Expose the test-only salted section API
+ private static native V1SectionBuilder nativeAddSaltedSection(
+ V1AdvertisementBuilder.V1BuilderHandle builder,
+ V1BroadcastCredential credential,
+ byte[] salt);
+}
diff --git a/nearby/presence/np_java_ffi/test/com/google/android/nearby/presence/rust/credential/CredentialBookTests.java b/nearby/presence/np_java_ffi/test/com/google/android/nearby/presence/rust/credential/CredentialBookTests.java
deleted file mode 100644
index 12302d3..0000000
--- a/nearby/presence/np_java_ffi/test/com/google/android/nearby/presence/rust/credential/CredentialBookTests.java
+++ /dev/null
@@ -1,40 +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.android.nearby.presence.rust.credential;
-
-import static com.google.common.truth.Truth.assertThat;
-import static org.mockito.Mockito.*;
-
-import java.lang.ref.Cleaner;
-import org.junit.jupiter.api.Test;
-import org.junit.jupiter.api.extension.ExtendWith;
-import org.mockito.Mock;
-import org.mockito.junit.jupiter.MockitoExtension;
-
-@ExtendWith(MockitoExtension.class)
-public class CredentialBookTests {
-
- @Mock Cleaner cleaner;
-
- @Test
- void CredentialBook_wasRegisteredWithCleaner() {
- try (CredentialBook book = new CredentialBook.Builder(cleaner).build()) {
- assertThat(book).isNotNull();
- verify(cleaner).register(same(book), any());
- }
- }
-}
diff --git a/nearby/presence/rand_ext/Cargo.toml b/nearby/presence/rand_ext/Cargo.toml
index 79a8ea8..da5744e 100644
--- a/nearby/presence/rand_ext/Cargo.toml
+++ b/nearby/presence/rand_ext/Cargo.toml
@@ -3,6 +3,7 @@
version.workspace = true
edition.workspace = true
publish.workspace = true
+license.workspace = true
[lints]
workspace = true
diff --git a/nearby/presence/sink/Cargo.toml b/nearby/presence/sink/Cargo.toml
index a479205..60727c0 100644
--- a/nearby/presence/sink/Cargo.toml
+++ b/nearby/presence/sink/Cargo.toml
@@ -3,6 +3,7 @@
version.workspace = true
edition.workspace = true
publish.workspace = true
+license.workspace = true
[lints]
workspace = true
diff --git a/nearby/presence/test_helper/Cargo.toml b/nearby/presence/test_helper/Cargo.toml
index 340a843..6fd2e3a 100644
--- a/nearby/presence/test_helper/Cargo.toml
+++ b/nearby/presence/test_helper/Cargo.toml
@@ -3,6 +3,7 @@
version.workspace = true
edition.workspace = true
publish.workspace = true
+license.workspace = true
[lints]
workspace = true
@@ -11,3 +12,7 @@
hex.workspace = true
serde_json.workspace = true
itertools.workspace = true
+
+[features]
+default = ["std"]
+std = ["itertools/use_std"]
diff --git a/nearby/presence/test_helper/src/lib.rs b/nearby/presence/test_helper/src/lib.rs
index 76dccf4..41446fd 100644
--- a/nearby/presence/test_helper/src/lib.rs
+++ b/nearby/presence/test_helper/src/lib.rs
@@ -73,6 +73,7 @@
///
/// assert_eq!("0x12, 0x34", hex_bytes(&[0x12, 0x34]));
/// ```
+#[cfg(feature = "std")]
pub fn hex_bytes(data: impl AsRef<[u8]>) -> String {
hex::encode_upper(data).chars().tuples().map(|(a, b)| format!("0x{}{}", a, b)).join(", ")
}
diff --git a/nearby/presence/test_vector_hkdf/Cargo.toml b/nearby/presence/test_vector_hkdf/Cargo.toml
index cf5d1a2..76d1e6c 100644
--- a/nearby/presence/test_vector_hkdf/Cargo.toml
+++ b/nearby/presence/test_vector_hkdf/Cargo.toml
@@ -3,6 +3,7 @@
version.workspace = true
edition.workspace = true
publish.workspace = true
+license.workspace = true
[lints]
workspace = true
diff --git a/nearby/presence/xts_aes/Cargo.toml b/nearby/presence/xts_aes/Cargo.toml
index 86745c4..b003951 100644
--- a/nearby/presence/xts_aes/Cargo.toml
+++ b/nearby/presence/xts_aes/Cargo.toml
@@ -3,6 +3,7 @@
version.workspace = true
edition.workspace = true
publish.workspace = true
+license.workspace = true
[lints]
workspace = true
diff --git a/nearby/presence/xts_aes/fuzz/Cargo.toml b/nearby/presence/xts_aes/fuzz/Cargo.toml
index 5d99173..a231176 100644
--- a/nearby/presence/xts_aes/fuzz/Cargo.toml
+++ b/nearby/presence/xts_aes/fuzz/Cargo.toml
@@ -4,6 +4,7 @@
authors = ["Automatically generated"]
publish = false
edition = "2018"
+license = "Apache-2.0"
[package.metadata]
cargo-fuzz = true
@@ -19,6 +20,9 @@
[target.'cfg(fuzzing)'.dependencies]
libfuzzer-sys.workspace = true
+[lints.rust]
+unexpected_cfgs = { level = "warn", check-cfg = ['cfg(fuzzing)'] }
+
[[bin]]
name = "xts_roundtrip"
path = "src/bin/xts_roundtrip.rs"
diff --git a/nearby/src/fuzzers.rs b/nearby/src/fuzzers.rs
deleted file mode 100644
index e809518..0000000
--- a/nearby/src/fuzzers.rs
+++ /dev/null
@@ -1,88 +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 cmd_runner::{run_cmd_shell, run_cmd_shell_with_color, YellowStderr};
-use std::{fs, path};
-
-pub(crate) fn run_rust_fuzzers(root: &path::Path) -> anyhow::Result<()> {
- log::info!("Running rust fuzzers");
- run_cmd_shell_with_color::<YellowStderr>(
- &root.join("presence/xts_aes"),
- "cargo +nightly fuzz run xts_roundtrip -- -runs=10000 -max_total_time=60",
- )?;
- run_cmd_shell_with_color::<YellowStderr>(
- &root.join("presence/ldt"),
- "cargo +nightly fuzz run ldt_roundtrip -- -runs=10000 -max_total_time=60",
- )?;
- run_cmd_shell_with_color::<YellowStderr>(
- &root.join("presence/ldt_np_adv"),
- "cargo +nightly fuzz run ldt_np_decrypt -- -runs=10000 -max_total_time=60",
- )?;
- run_cmd_shell_with_color::<YellowStderr>(
- &root.join("presence/ldt_np_adv"),
- "cargo +nightly fuzz run ldt_np_roundtrip -- -runs=10000 -max_total_time=60",
- )?;
- run_cmd_shell_with_color::<YellowStderr>(
- &root.join("connections/ukey2/ukey2_connections"),
- "cargo +nightly fuzz run fuzz_connection -- -runs=10000 -max_total_time=60",
- )?;
- run_cmd_shell_with_color::<YellowStderr>(
- &root.join("connections/ukey2/ukey2_connections"),
- "cargo +nightly fuzz run fuzz_from_saved_session -- -runs=10000 -max_total_time=60",
- )?;
- run_cmd_shell_with_color::<YellowStderr>(
- &root.join("connections/ukey2/ukey2_connections"),
- "cargo +nightly fuzz run fuzz_handshake -- -runs=10000 -max_total_time=60",
- )?;
- run_cmd_shell_with_color::<YellowStderr>(
- &root.join("crypto/crypto_provider_test"),
- "cargo +nightly fuzz run fuzz_p256 -- -runs=10000 -max_total_time=60",
- )?;
- run_cmd_shell_with_color::<YellowStderr>(
- &root.join("crypto/crypto_provider_test"),
- concat!(
- "cargo +nightly fuzz run fuzz_p256 --features=boringssl --no-default-features ",
- "-- -runs=10000 -max_total_time=60"
- ),
- )?;
-
- Ok(())
-}
-
-// Runs the fuzztest fuzzers as short lived unit tests, compatible with gtest
-pub(crate) fn build_fuzztest_unit_tests(root: &path::Path) -> anyhow::Result<()> {
- log::info!("Checking fuzztest targets in unit test mode");
- run_cmd_shell(root, "cargo build -p np_c_ffi --release")?;
- run_cmd_shell(root, "cargo build -p ldt_np_adv_ffi --release")?;
- let build_dir = root.join("cmake-build");
- fs::create_dir_all(&build_dir)?;
- run_cmd_shell_with_color::<YellowStderr>(&build_dir, "cmake -G Ninja -DENABLE_FUZZ=true ..")?;
-
- for target in ["deserialization_fuzzer", "ldt_fuzzer"] {
- run_cmd_shell_with_color::<YellowStderr>(
- &build_dir,
- format!("cmake --build . --target {}", target),
- )?;
- }
-
- run_cmd_shell_with_color::<YellowStderr>(
- &build_dir.join("presence/np_cpp_ffi/fuzz/"),
- "ctest",
- )?;
- run_cmd_shell_with_color::<YellowStderr>(
- &build_dir.join("presence/ldt_np_adv_ffi/c/fuzz/"),
- "ctest",
- )?;
- Ok(())
-}
diff --git a/remoteauth/Cargo.lock b/remoteauth/Cargo.lock
index 7e17954..ab31216 100644
--- a/remoteauth/Cargo.lock
+++ b/remoteauth/Cargo.lock
@@ -58,9 +58,9 @@
[[package]]
name = "anstyle-query"
-version = "1.0.3"
+version = "1.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "a64c907d4e79225ac72e2a354c9ce84d50ebb4586dee56c82b3ee73004f537f5"
+checksum = "ad186efb764318d35165f1758e7dcef3b10628e26d41a44bc5550652e6804391"
dependencies = [
"windows-sys",
]
@@ -77,9 +77,9 @@
[[package]]
name = "anyhow"
-version = "1.0.83"
+version = "1.0.86"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "25bdb32cbbdce2b519a9cd7df3a678443100e265d5e25ca763b7572a5104f5f3"
+checksum = "b3d1d046238990b9cf5bcde22a3fb3584ee5cf65fb2765f454ed428c7a0063da"
[[package]]
name = "autocfg"
@@ -98,7 +98,7 @@
]
[[package]]
-name = "build-scripts"
+name = "build_scripts"
version = "0.1.0"
dependencies = [
"anyhow",
@@ -116,9 +116,9 @@
[[package]]
name = "cc"
-version = "1.0.97"
+version = "1.0.99"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "099a5357d84c4c61eb35fc8eafa9a79a902c2f76911e5747ced4e032edd8d9b4"
+checksum = "96c51067fd44124faa7f870b4b1c969379ad32b2ba805aa959430ceaa384f695"
[[package]]
name = "cfg-if"
@@ -142,9 +142,9 @@
[[package]]
name = "clap"
-version = "4.5.4"
+version = "4.5.13"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "90bc066a67923782aa8515dbaea16946c5bcc5addbd668bb80af688e53e548a0"
+checksum = "0fbb260a053428790f3de475e304ff84cdbc4face759ea7a3e64c1edd938a7fc"
dependencies = [
"clap_builder",
"clap_derive",
@@ -152,9 +152,9 @@
[[package]]
name = "clap_builder"
-version = "4.5.2"
+version = "4.5.13"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "ae129e2e766ae0ec03484e609954119f123cc1fe650337e155d03b022f24f7b4"
+checksum = "64b17d7ea74e9f833c7dbf2cbe4fb12ff26783eda4782a8975b72f895c9b4d99"
dependencies = [
"anstream",
"anstyle",
@@ -164,9 +164,9 @@
[[package]]
name = "clap_derive"
-version = "4.5.4"
+version = "4.5.13"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "528131438037fd55894f62d6e9f068b8f45ac57ffa77517819645d10aed04f64"
+checksum = "501d359d5f3dcaf6ecdeee48833ae73ec6e42723a1e52419c79abf9507eec0a0"
dependencies = [
"heck",
"proc-macro2",
@@ -176,9 +176,9 @@
[[package]]
name = "clap_lex"
-version = "0.7.0"
+version = "0.7.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "98cc8fbded0c607b7ba9dd60cd98df59af97e84d24e49c8557331cfc26d301ce"
+checksum = "4b82cf0babdbd58558212896d1a4272303a57bdb245c2bf1147185fb45640e70"
[[package]]
name = "cmd_runner"
@@ -191,6 +191,8 @@
"globset",
"log",
"owo-colors",
+ "serde",
+ "serde_json",
"shell-escape",
"xshell",
]
@@ -222,9 +224,9 @@
[[package]]
name = "crossbeam-channel"
-version = "0.5.12"
+version = "0.5.13"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "ab3db02a9c5b5121e1e42fbdb1aeb65f5e02624cc58c43f2884c6ccac0b82f95"
+checksum = "33480d6946193aa8033910124896ca395333cae7e2d1113d1fef6c3272217df2"
dependencies = [
"crossbeam-utils",
]
@@ -259,9 +261,9 @@
[[package]]
name = "crossbeam-utils"
-version = "0.8.19"
+version = "0.8.20"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "248e3bacc7dc6baa3b21e405ee045c3047101a49145e7e9eca583ab4c2ca5345"
+checksum = "22ec99545bb0ed0ea7bb9b8e1e9122ea386ff8a48c0922e43f36d45ab09e0e80"
[[package]]
name = "ctap_protocol"
@@ -390,15 +392,15 @@
[[package]]
name = "libc"
-version = "0.2.154"
+version = "0.2.155"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "ae743338b92ff9146ce83992f766a31066a91a8c84a45e0e9f21e7cf6de6d346"
+checksum = "97b3888a4aecf77e811145cadf6eef5901f4782c53886191b2f693f24761847c"
[[package]]
name = "license"
-version = "3.3.1"
+version = "3.4.0+3.24.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "3bba2f02ee1d13cd4bea565658939cd851d70e391f34f7c27b45b2077df3a2e4"
+checksum = "a7da1e0d845faf299a9fe5f201a918a0dc0d5fc22c7b9580a6a23fed3a912b37"
dependencies = [
"reword",
"serde",
@@ -413,9 +415,9 @@
[[package]]
name = "memchr"
-version = "2.7.2"
+version = "2.7.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "6c8640c5d730cb13ebd907d8d04b52f55ac9a2eec55b440c8892f40d56c76c1d"
+checksum = "78ca9ab1a0babb1e7d5695e3530886289c18cf2f87ec19a575a0abdce112e3a3"
[[package]]
name = "num-traits"
@@ -447,9 +449,9 @@
[[package]]
name = "proc-macro2"
-version = "1.0.82"
+version = "1.0.86"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "8ad3d49ab951a01fbaafe34f2ec74122942fe18a3f9814c3268f1bb72042131b"
+checksum = "5e719e8df665df0d1c8fbfd238015744736151d4445ec0836b8e628aae103b77"
dependencies = [
"unicode-ident",
]
@@ -465,9 +467,9 @@
[[package]]
name = "regex"
-version = "1.10.4"
+version = "1.10.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "c117dbdfde9c8308975b6a18d71f3f385c89461f7b3fb054288ecf2a2058ba4c"
+checksum = "b91213439dad192326a0d7c6ee3955910425f441d7038e0d6933b0aec5c4517f"
dependencies = [
"aho-corasick",
"memchr",
@@ -477,9 +479,9 @@
[[package]]
name = "regex-automata"
-version = "0.4.6"
+version = "0.4.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "86b83b8b9847f9bf95ef68afb0b8e6cdb80f498442f5179a29fad448fcc1eaea"
+checksum = "38caf58cc5ef2fed281f89292ef23f6365465ed9a41b7a7754eb4e26496c92df"
dependencies = [
"aho-corasick",
"memchr",
@@ -488,9 +490,9 @@
[[package]]
name = "regex-syntax"
-version = "0.8.3"
+version = "0.8.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "adad44e29e4c806119491a7f06f03de4d1af22c3a680dd47f1e6e179439d1f56"
+checksum = "7a66a03ae7c801facd77a29370b4faec201768915ac14a721ba36f20bc9c209b"
[[package]]
name = "remote_auth_protocol"
@@ -525,18 +527,18 @@
[[package]]
name = "serde"
-version = "1.0.200"
+version = "1.0.203"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "ddc6f9cc94d67c0e21aaf7eda3a010fd3af78ebf6e096aa6e2e13c79749cce4f"
+checksum = "7253ab4de971e72fb7be983802300c30b5a7f0c2e56fab8abfc6a214307c0094"
dependencies = [
"serde_derive",
]
[[package]]
name = "serde_derive"
-version = "1.0.200"
+version = "1.0.203"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "856f046b9400cee3c8c94ed572ecdb752444c24528c035cd35882aad6f492bcb"
+checksum = "500cbc0ebeb6f46627f50f3f5811ccf6bf00643be300b4c3eabc0ef55dc5b5ba"
dependencies = [
"proc-macro2",
"quote",
@@ -545,9 +547,9 @@
[[package]]
name = "serde_json"
-version = "1.0.116"
+version = "1.0.118"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "3e17db7126d17feb94eb3fad46bf1a96b034e8aacbc2e775fe81505f8b0b2813"
+checksum = "d947f6b3163d8857ea16c4fa0dd4840d52f3041039a85decd46867eb1abef2e4"
dependencies = [
"itoa",
"ryu",
@@ -568,9 +570,9 @@
[[package]]
name = "syn"
-version = "2.0.61"
+version = "2.0.67"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "c993ed8ccba56ae856363b1845da7266a7cb78e1d146c8a32d54b45a8b831fc9"
+checksum = "ff8655ed1d86f3af4ee3fd3263786bc14245ad17c4c7e85ba7187fb3ae028c90"
dependencies = [
"proc-macro2",
"quote",
@@ -588,18 +590,18 @@
[[package]]
name = "thiserror"
-version = "1.0.60"
+version = "1.0.61"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "579e9083ca58dd9dcf91a9923bb9054071b9ebbd800b342194c9feb0ee89fc18"
+checksum = "c546c80d6be4bc6a00c0f01730c08df82eaa7a7a61f11d656526506112cc1709"
dependencies = [
"thiserror-impl",
]
[[package]]
name = "thiserror-impl"
-version = "1.0.60"
+version = "1.0.61"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "e2470041c06ec3ac1ab38d0356a6119054dedaea53e12fbefc0de730a1c08524"
+checksum = "46c3384250002a6d5af4d114f2845d37b57521033f30d5c3f46c4d70e1197533"
dependencies = [
"proc-macro2",
"quote",
@@ -620,9 +622,9 @@
[[package]]
name = "utf8parse"
-version = "0.2.1"
+version = "0.2.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "711b9620af191e0cdc7468a8d14e709c3dcdb115b36f838e601583af800a370a"
+checksum = "06abde3611657adf66d383f00b093d7faecc7fa57071cce2578660c9f1010821"
[[package]]
name = "walkdir"
diff --git a/remoteauth/Cargo.toml b/remoteauth/Cargo.toml
index d86adc0..68580eb 100644
--- a/remoteauth/Cargo.toml
+++ b/remoteauth/Cargo.toml
@@ -1,27 +1,18 @@
[workspace]
members = [
+ "build_scripts",
"ctap_protocol",
"platform",
"remote_auth_protocol",
]
+default-members = ["build_scripts"]
+resolver = "2"
[workspace.package]
version = "0.1.0"
edition = "2021"
publish = false
+license = "Apache-2.0"
[workspace.dependencies]
anyhow = "1.0.72"
-
-[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 = "../common/cmd_runner" }
-env_logger = "0.10.0"
-log = "0.4.17"
\ No newline at end of file
diff --git a/remoteauth/build_scripts/Cargo.toml b/remoteauth/build_scripts/Cargo.toml
new file mode 100644
index 0000000..604a617
--- /dev/null
+++ b/remoteauth/build_scripts/Cargo.toml
@@ -0,0 +1,13 @@
+[package]
+name = "build_scripts"
+version.workspace = true
+edition.workspace = true
+publish.workspace = true
+license.workspace = true
+
+[dependencies]
+anyhow.workspace = true
+clap = { version = "4.5.13", features = ["derive"] }
+cmd_runner = { path = "../../common/cmd_runner" }
+env_logger = "0.10.0"
+log = "0.4.17"
diff --git a/remoteauth/src/ctap_protocol.rs b/remoteauth/build_scripts/src/ctap_protocol.rs
similarity index 100%
rename from remoteauth/src/ctap_protocol.rs
rename to remoteauth/build_scripts/src/ctap_protocol.rs
diff --git a/remoteauth/src/main.rs b/remoteauth/build_scripts/src/main.rs
similarity index 83%
rename from remoteauth/src/main.rs
rename to remoteauth/build_scripts/src/main.rs
index e0851eb..2caf8c0 100644
--- a/remoteauth/src/main.rs
+++ b/remoteauth/build_scripts/src/main.rs
@@ -17,7 +17,10 @@
use clap::Parser as _;
use cmd_runner::run_cmd_shell;
use env_logger::Env;
-use std::{env, path};
+use std::{
+ env,
+ path::{self, PathBuf},
+};
mod ctap_protocol;
mod platform;
@@ -27,17 +30,20 @@
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();
+ let build_scripts_dir = PathBuf::from(
+ env::var("CARGO_MANIFEST_DIR").expect("Must be run via Cargo to establish root directory"),
+ );
+ let root_dir = build_scripts_dir
+ .parent()
+ .expect("build_scripts directory should have a parent");
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::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)?
+ remote_auth_protocol::check_remote_auth_protocol(root_dir)?
}
}
diff --git a/remoteauth/src/platform.rs b/remoteauth/build_scripts/src/platform.rs
similarity index 100%
rename from remoteauth/src/platform.rs
rename to remoteauth/build_scripts/src/platform.rs
diff --git a/remoteauth/src/remote_auth_protocol.rs b/remoteauth/build_scripts/src/remote_auth_protocol.rs
similarity index 100%
rename from remoteauth/src/remote_auth_protocol.rs
rename to remoteauth/build_scripts/src/remote_auth_protocol.rs
diff --git a/remoteauth/ctap_protocol/Cargo.toml b/remoteauth/ctap_protocol/Cargo.toml
index 8a06840..d5ed9c2 100644
--- a/remoteauth/ctap_protocol/Cargo.toml
+++ b/remoteauth/ctap_protocol/Cargo.toml
@@ -3,6 +3,7 @@
version.workspace = true
edition.workspace = true
publish.workspace = true
+license.workspace = true
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
diff --git a/remoteauth/deny.toml b/remoteauth/deny.toml
index 2bf5920..7f0eda7 100644
--- a/remoteauth/deny.toml
+++ b/remoteauth/deny.toml
@@ -9,24 +9,6 @@
# 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
@@ -35,16 +17,8 @@
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 = [
@@ -70,8 +44,6 @@
# 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
@@ -87,26 +59,6 @@
"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.
@@ -154,7 +106,7 @@
# 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
+ignore = false
# 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
diff --git a/remoteauth/platform/Cargo.toml b/remoteauth/platform/Cargo.toml
index 0136c27..23b2074 100644
--- a/remoteauth/platform/Cargo.toml
+++ b/remoteauth/platform/Cargo.toml
@@ -3,6 +3,7 @@
version.workspace = true
edition.workspace = true
publish.workspace = true
+license.workspace = true
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
diff --git a/remoteauth/remote_auth_protocol/Cargo.toml b/remoteauth/remote_auth_protocol/Cargo.toml
index e9fd6d2..e0fcad5 100644
--- a/remoteauth/remote_auth_protocol/Cargo.toml
+++ b/remoteauth/remote_auth_protocol/Cargo.toml
@@ -3,6 +3,7 @@
version.workspace = true
edition.workspace = true
publish.workspace = true
+license.workspace = true
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html